gremlin 0.0.3 → 0.0.4
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.
- checksums.yaml +4 -4
- data/.gitignore +8 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile.lock +14 -6
- data/README.md +4 -3
- data/app/controllers/gremlin/metrics_controller.rb +1 -1
- data/benchmark.rb +23 -19
- data/bin/rails +13 -0
- data/gremlin.gemspec +3 -2
- data/lib/gremlin.rb +2 -1
- data/lib/gremlin/instruments.rb +42 -28
- data/lib/gremlin/instruments/counter.rb +3 -6
- data/lib/gremlin/instruments/gauge.rb +4 -7
- data/lib/gremlin/instruments/summary.rb +50 -45
- data/lib/gremlin/quantile.rb +1 -1
- data/lib/gremlin/quantile/estimator.rb +32 -13
- data/lib/gremlin/quantile/percentile.rb +26 -0
- data/lib/gremlin/railtie.rb +92 -70
- data/lib/gremlin/registry.rb +12 -22
- data/lib/gremlin/version.rb +1 -1
- metadata +24 -5
- data/lib/gremlin/quantile/quantile.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 76f9c1702a54c108247ab988504f59efb41a0736
|
4
|
+
data.tar.gz: 739424d848af9ff260de7ba34d78addb6a77f89d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cacbcc4da37f36e33167541c6958005e31f8dee7b7bf8f7c21d464deb7fe566313092c7b0fbdf93a2809967b65b523f333235cea42155db5252d0cc6dbe696b6
|
7
|
+
data.tar.gz: 0562dee3d4491516df49a0f4bd6f17d59d6a4ab59207c259663cdeae21901edae05da79431d1957591cb923481fe3eaca5ee8b5724d2e38f133723baa9b98d34
|
data/.gitignore
CHANGED
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
gremlin
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.3.0
|
data/Gemfile.lock
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
gremlin (0.0.
|
4
|
+
gremlin (0.0.4)
|
5
5
|
quantile
|
6
6
|
rails
|
7
7
|
redis (~> 3.2)
|
8
|
-
redis-native_hash
|
9
8
|
|
10
9
|
GEM
|
11
10
|
remote: https://rubygems.org/
|
@@ -96,10 +95,8 @@ GEM
|
|
96
95
|
rake (>= 0.8.7)
|
97
96
|
thor (>= 0.18.1, < 2.0)
|
98
97
|
rake (10.5.0)
|
99
|
-
rdoc (5.
|
98
|
+
rdoc (5.1.0)
|
100
99
|
redis (3.3.3)
|
101
|
-
redis-native_hash (0.2.1)
|
102
|
-
redis (>= 2.0.0)
|
103
100
|
rspec (3.5.0)
|
104
101
|
rspec-core (~> 3.5.0)
|
105
102
|
rspec-expectations (~> 3.5.0)
|
@@ -112,6 +109,14 @@ GEM
|
|
112
109
|
rspec-mocks (3.5.0)
|
113
110
|
diff-lcs (>= 1.2.0, < 2.0)
|
114
111
|
rspec-support (~> 3.5.0)
|
112
|
+
rspec-rails (3.5.2)
|
113
|
+
actionpack (>= 3.0)
|
114
|
+
activesupport (>= 3.0)
|
115
|
+
railties (>= 3.0)
|
116
|
+
rspec-core (~> 3.5.0)
|
117
|
+
rspec-expectations (~> 3.5.0)
|
118
|
+
rspec-mocks (~> 3.5.0)
|
119
|
+
rspec-support (~> 3.5.0)
|
115
120
|
rspec-support (3.5.0)
|
116
121
|
sprockets (3.7.1)
|
117
122
|
concurrent-ruby (~> 1.0)
|
@@ -120,6 +125,7 @@ GEM
|
|
120
125
|
actionpack (>= 4.0)
|
121
126
|
activesupport (>= 4.0)
|
122
127
|
sprockets (>= 3.0.0)
|
128
|
+
sqlite3 (1.3.13)
|
123
129
|
thor (0.19.4)
|
124
130
|
thread_safe (0.3.6)
|
125
131
|
tzinfo (1.2.2)
|
@@ -137,6 +143,8 @@ DEPENDENCIES
|
|
137
143
|
rake (~> 10.0)
|
138
144
|
rdoc
|
139
145
|
rspec (~> 3.3, >= 3.3.0)
|
146
|
+
rspec-rails
|
147
|
+
sqlite3
|
140
148
|
|
141
149
|
BUNDLED WITH
|
142
|
-
1.
|
150
|
+
1.14.5
|
data/README.md
CHANGED
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/gremlin`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
4
|
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
6
|
-
|
7
5
|
## Installation
|
8
6
|
|
9
7
|
Add this line to your application's Gemfile:
|
@@ -32,10 +30,13 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
32
30
|
|
33
31
|
## Contributing
|
34
32
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
33
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/omadahealth/gremlin.
|
36
34
|
|
37
35
|
|
38
36
|
## License
|
39
37
|
|
40
38
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
41
39
|
|
40
|
+
## Resources
|
41
|
+
|
42
|
+
https://speakerdeck.com/nextmat/digging-deep-with-activesupportnotifications
|
data/benchmark.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'benchmark'
|
2
2
|
require 'gremlin'
|
3
|
+
require 'securerandom'
|
3
4
|
|
4
5
|
|
5
6
|
include Benchmark
|
@@ -8,15 +9,16 @@ print "Starting benchmarks..."
|
|
8
9
|
|
9
10
|
orig_std_out = STDOUT.clone
|
10
11
|
STDOUT.reopen(File::open('/dev/null', 'w'))
|
12
|
+
iterations = ARGV[-1].to_i
|
11
13
|
|
12
|
-
counter_init_benchmarks = (1..
|
14
|
+
counter_init_benchmarks = (1..iterations).map do |n|
|
13
15
|
Benchmark.benchmark(CAPTION, 35, FORMAT) do |x|
|
14
16
|
counter_name = "test_#{SecureRandom.hex(5)}_counter".to_sym
|
15
17
|
x.report("Counter initialize") { Gremlin::Instruments::Counter.new counter_name, "placeholder", {} }
|
16
18
|
end
|
17
19
|
end
|
18
20
|
|
19
|
-
counter_init_and_fetch_benchmarks = (1..
|
21
|
+
counter_init_and_fetch_benchmarks = (1..iterations).map do |n|
|
20
22
|
Benchmark.benchmark(CAPTION, 35, FORMAT) do |x|
|
21
23
|
counter_name = "test_#{SecureRandom.hex(5)}_counter".to_sym
|
22
24
|
x.report("Counter initialize then fetch") do
|
@@ -26,7 +28,7 @@ counter_init_and_fetch_benchmarks = (1..100).map do |n|
|
|
26
28
|
end
|
27
29
|
end
|
28
30
|
|
29
|
-
counter_init_and_incr_benchmarks = (1..
|
31
|
+
counter_init_and_incr_benchmarks = (1..iterations).map do |n|
|
30
32
|
Benchmark.benchmark(CAPTION, 35, FORMAT) do |x|
|
31
33
|
counter_name = "test_#{SecureRandom.hex(5)}_counter".to_sym
|
32
34
|
x.report("Counter increment") do
|
@@ -36,7 +38,7 @@ counter_init_and_incr_benchmarks = (1..100).map do |n|
|
|
36
38
|
end
|
37
39
|
end
|
38
40
|
|
39
|
-
gauge_init_benchmarks = (1..
|
41
|
+
gauge_init_benchmarks = (1..iterations).map do |n|
|
40
42
|
Benchmark.benchmark(CAPTION, 35, FORMAT) do |x|
|
41
43
|
gauge_name = "test_#{SecureRandom.hex(5)}_gauge".to_sym
|
42
44
|
gauge_set_val = rand(1..25000000)
|
@@ -44,7 +46,7 @@ gauge_init_benchmarks = (1..100).map do |n|
|
|
44
46
|
end
|
45
47
|
end
|
46
48
|
|
47
|
-
gauge_init_and_fetch_benchmarks = (1..
|
49
|
+
gauge_init_and_fetch_benchmarks = (1..iterations).map do |n|
|
48
50
|
Benchmark.benchmark(CAPTION, 35, FORMAT) do |x|
|
49
51
|
gauge_name = "test_#{SecureRandom.hex(5)}_gauge".to_sym
|
50
52
|
gauge_set_val = rand(1..25000000)
|
@@ -55,7 +57,7 @@ gauge_init_and_fetch_benchmarks = (1..100).map do |n|
|
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
58
|
-
gauge_init_and_set_benchmarks = (1..
|
60
|
+
gauge_init_and_set_benchmarks = (1..iterations).map do |n|
|
59
61
|
Benchmark.benchmark(CAPTION, 35, FORMAT) do |x|
|
60
62
|
gauge_name = "test_#{SecureRandom.hex(5)}_gauge".to_sym
|
61
63
|
gauge_set_val = rand(1..25000000)
|
@@ -66,7 +68,7 @@ gauge_init_and_set_benchmarks = (1..100).map do |n|
|
|
66
68
|
end
|
67
69
|
end
|
68
70
|
|
69
|
-
summary_init_benchmarks = (1..
|
71
|
+
summary_init_benchmarks = (1..iterations).map do |n|
|
70
72
|
Benchmark.benchmark(CAPTION, 35, FORMAT) do |x|
|
71
73
|
summary_name = "test_#{SecureRandom.hex(5)}_summary".to_sym
|
72
74
|
summary_observe_val = rand(1..25000000)
|
@@ -74,7 +76,7 @@ summary_init_benchmarks = (1..100).map do |n|
|
|
74
76
|
end
|
75
77
|
end
|
76
78
|
|
77
|
-
summary_init_and_fetch_benchmarks = (1..
|
79
|
+
summary_init_and_fetch_benchmarks = (1..iterations).map do |n|
|
78
80
|
Benchmark.benchmark(CAPTION, 35, FORMAT) do |x|
|
79
81
|
summary_name = "test_#{SecureRandom.hex(5)}_summary".to_sym
|
80
82
|
summary_observe_val = rand(1..25000000)
|
@@ -85,13 +87,15 @@ summary_init_and_fetch_benchmarks = (1..100).map do |n|
|
|
85
87
|
end
|
86
88
|
end
|
87
89
|
|
88
|
-
summary_init_and_observe_benchmarks = (1..
|
90
|
+
summary_init_and_observe_benchmarks = (1..iterations).map do |n|
|
89
91
|
Benchmark.benchmark(CAPTION, 35, FORMAT) do |x|
|
90
92
|
summary_name = "test_#{SecureRandom.hex(5)}_summary".to_sym
|
91
93
|
summary_observe_val = rand(1..25000000)
|
92
94
|
x.report("Summary observe") do
|
93
95
|
summary = Gremlin::Instruments::Summary.new summary_name.to_sym, "placeholder", {}
|
94
|
-
|
96
|
+
(1..iterations).each do
|
97
|
+
summary.observe({ :bar => "foo" }, summary_observe_val)
|
98
|
+
end
|
95
99
|
end
|
96
100
|
end
|
97
101
|
end
|
@@ -106,11 +110,11 @@ counter_init_and_fetch_agg = counter_init_and_fetch_benchmarks.flatten.inject(0)
|
|
106
110
|
puts "--------------------------------------------------------"
|
107
111
|
puts ""
|
108
112
|
puts "Counter Initialize Total: #{counter_init_agg*1000} ms"
|
109
|
-
puts "Counter Initialize Avg: #{counter_init_agg/
|
113
|
+
puts "Counter Initialize Avg: #{counter_init_agg/iterations*1000} ms over #{iterations} iterations"
|
110
114
|
puts "Counter Initialize and Fetch Total: #{counter_init_and_fetch_agg*1000} ms"
|
111
|
-
puts "Counter Initialize and Fetch Avg: #{counter_init_and_fetch_agg/
|
115
|
+
puts "Counter Initialize and Fetch Avg: #{counter_init_and_fetch_agg/iterations*1000} ms over #{iterations} iterations"
|
112
116
|
puts "Counter Initialize and Fetch Total: #{counter_init_and_incr_agg*1000} ms"
|
113
|
-
puts "Counter Initialize and Fetch Avg: #{counter_init_and_incr_agg/
|
117
|
+
puts "Counter Initialize and Fetch Avg: #{counter_init_and_incr_agg/iterations*1000} ms over #{iterations} iterations"
|
114
118
|
puts ""
|
115
119
|
|
116
120
|
gauge_init_agg = gauge_init_benchmarks.flatten.inject(0) { |sum, x| sum + x.real }
|
@@ -120,11 +124,11 @@ gauge_init_and_set_agg = gauge_init_and_set_benchmarks.flatten.inject(0) { |su
|
|
120
124
|
puts "--------------------------------------------------------"
|
121
125
|
puts ""
|
122
126
|
puts "Gauge Initialize Total: #{gauge_init_agg*1000} ms"
|
123
|
-
puts "Gauge Initialize Avg: #{gauge_init_agg/
|
127
|
+
puts "Gauge Initialize Avg: #{gauge_init_agg/iterations*1000} ms over #{iterations} iterations"
|
124
128
|
puts "Gauge Initialize and Fetch Total: #{gauge_init_and_fetch_agg*1000} ms"
|
125
|
-
puts "Gauge Initialize and Fetch Avg: #{gauge_init_and_fetch_agg/
|
129
|
+
puts "Gauge Initialize and Fetch Avg: #{gauge_init_and_fetch_agg/iterations*1000} ms over #{iterations} iterations"
|
126
130
|
puts "Gauge Initialize and Set Total: #{gauge_init_and_set_agg*1000} ms"
|
127
|
-
puts "Gauge Initialize and Set Avg: #{gauge_init_and_set_agg/
|
131
|
+
puts "Gauge Initialize and Set Avg: #{gauge_init_and_set_agg/iterations*1000} ms over #{iterations} iterations"
|
128
132
|
puts ""
|
129
133
|
puts "--------------------------------------------------------"
|
130
134
|
|
@@ -134,10 +138,10 @@ summary_init_and_observe_agg = summary_init_and_observe_benchmarks.flatten.inj
|
|
134
138
|
|
135
139
|
puts ""
|
136
140
|
puts "Summary Initialize Total: #{summary_init_agg*1000} ms"
|
137
|
-
puts "Summary Initialize Avg: #{summary_init_agg/
|
141
|
+
puts "Summary Initialize Avg: #{summary_init_agg/iterations*1000} ms over #{iterations} iterations"
|
138
142
|
puts "Summary Initialize and Fetch Total: #{summary_init_and_fetch_agg*1000} ms"
|
139
|
-
puts "Summary Initialize and Fetch Avg: #{summary_init_and_fetch_agg/
|
143
|
+
puts "Summary Initialize and Fetch Avg: #{summary_init_and_fetch_agg/iterations*1000} ms over #{iterations} iterations"
|
140
144
|
puts "Summary Initialize and Observe Total: #{summary_init_and_observe_agg*1000} ms"
|
141
|
-
puts "Summary Initialize and Observe Avg: #{summary_init_and_observe_agg/
|
145
|
+
puts "Summary Initialize and Observe Avg: #{summary_init_and_observe_agg/(iterations*iterations)*1000} ms over #{iterations} observations with #{iterations} iterations"
|
142
146
|
puts ""
|
143
147
|
puts "--------------------------------------------------------"
|
data/bin/rails
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# This command will automatically be run when you run "rails" with Rails gems
|
3
|
+
# installed from the root of your application.
|
4
|
+
|
5
|
+
ENGINE_ROOT = File.expand_path('../..', __FILE__)
|
6
|
+
ENGINE_PATH = File.expand_path('../../lib/gremlin/engine', __FILE__)
|
7
|
+
|
8
|
+
# Set up gems listed in the Gemfile.
|
9
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
|
10
|
+
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
11
|
+
|
12
|
+
require 'rails/all'
|
13
|
+
require 'rails/engine/commands'
|
data/gremlin.gemspec
CHANGED
@@ -6,7 +6,7 @@ require 'gremlin/version'
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "gremlin"
|
8
8
|
spec.version = Gremlin::VERSION
|
9
|
-
spec.authors = ["Kevin G. Phillips"]
|
9
|
+
spec.authors = ["Kevin G. Phillips", "Chris Constantine"]
|
10
10
|
spec.email = ["platform@omadahealth.com"]
|
11
11
|
|
12
12
|
spec.summary = %q{Gem to sanely emit metrics from Omadahealth rails applications}
|
@@ -31,8 +31,9 @@ Gem::Specification.new do |spec|
|
|
31
31
|
spec.add_development_dependency "rake", "~> 10.0"
|
32
32
|
spec.add_development_dependency 'rspec', '~> 3.3', '>= 3.3.0'
|
33
33
|
spec.add_development_dependency 'rdoc'
|
34
|
+
spec.add_development_dependency "rspec-rails"
|
35
|
+
spec.add_development_dependency "sqlite3"
|
34
36
|
|
35
|
-
spec.add_runtime_dependency 'redis-native_hash'
|
36
37
|
spec.add_runtime_dependency 'quantile'
|
37
38
|
spec.add_runtime_dependency 'rails'
|
38
39
|
spec.add_runtime_dependency 'redis', '~> 3.2'
|
data/lib/gremlin.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
# Supporting librarires
|
2
2
|
require "active_support/cache"
|
3
3
|
require "active_support/notifications" if defined?(::Rails)
|
4
|
+
require "active_support/subscriber" if defined?(::Rails)
|
4
5
|
require "erb"
|
5
6
|
require "json"
|
6
7
|
require "quantile"
|
7
|
-
require "
|
8
|
+
require "redis"
|
8
9
|
require "yaml"
|
9
10
|
|
10
11
|
# Gem internals
|
data/lib/gremlin/instruments.rb
CHANGED
@@ -7,14 +7,39 @@ module Gremlin
|
|
7
7
|
@name = name
|
8
8
|
@docstring = docstring
|
9
9
|
@base_labels = base_labels
|
10
|
-
@mutex = Mutex.new
|
11
10
|
|
12
11
|
@r = Redis.new(**Gremlin.configuration.redis)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
@
|
17
|
-
|
12
|
+
end
|
13
|
+
|
14
|
+
def delete
|
15
|
+
@r.del retention_key
|
16
|
+
end
|
17
|
+
|
18
|
+
def retention_get
|
19
|
+
@r.hgetall retention_key
|
20
|
+
end
|
21
|
+
|
22
|
+
def parse(value)
|
23
|
+
{
|
24
|
+
name: @name,
|
25
|
+
type: type_string,
|
26
|
+
help: help_string,
|
27
|
+
values: value.each_with_object({}) { |(l,v), m| m[JSON.parse(l).merge(@base_labels)] = cast(v) }
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def repr
|
32
|
+
parse(retention_get)
|
33
|
+
end
|
34
|
+
|
35
|
+
def repr_and_delete
|
36
|
+
val = nil
|
37
|
+
@r.pipelined do
|
38
|
+
val = retention_get
|
39
|
+
@r.del retention_key
|
40
|
+
end
|
41
|
+
v = parse(val.value)
|
42
|
+
return v
|
18
43
|
end
|
19
44
|
|
20
45
|
def node
|
@@ -33,10 +58,6 @@ module Gremlin
|
|
33
58
|
"# TYPE #{@name} #{type}"
|
34
59
|
end
|
35
60
|
|
36
|
-
def default
|
37
|
-
nil
|
38
|
-
end
|
39
|
-
|
40
61
|
def help
|
41
62
|
@docstring
|
42
63
|
end
|
@@ -45,27 +66,20 @@ module Gremlin
|
|
45
66
|
"# HELP #{@name} #{help}"
|
46
67
|
end
|
47
68
|
|
48
|
-
def
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
69
|
+
def cast(v)
|
70
|
+
case type
|
71
|
+
when :counter
|
72
|
+
v.to_i
|
73
|
+
when :gauge
|
74
|
+
v.to_f
|
75
|
+
else
|
76
|
+
v
|
77
|
+
end
|
55
78
|
end
|
56
79
|
|
57
80
|
def values
|
58
|
-
|
59
|
-
|
60
|
-
case type
|
61
|
-
when :counter
|
62
|
-
memo[JSON.parse(labels)] = value.to_i
|
63
|
-
when :gauge
|
64
|
-
memo[JSON.parse(labels)] = value.to_f
|
65
|
-
else
|
66
|
-
memo[JSON.parse(labels)] = value
|
67
|
-
end
|
68
|
-
end
|
81
|
+
retention_get.each_with_object({}) do |(labels, value), memo|
|
82
|
+
memo[JSON.parse(labels)] = cast(value)
|
69
83
|
end
|
70
84
|
end
|
71
85
|
end
|
@@ -4,9 +4,7 @@ module Gremlin
|
|
4
4
|
module Instruments
|
5
5
|
class Counter < Base
|
6
6
|
def increment(labels={}, by=1)
|
7
|
-
@
|
8
|
-
@r.hincrby retention_key, labels.to_json, by
|
9
|
-
end
|
7
|
+
@r.hincrby retention_key, labels.to_json, by
|
10
8
|
end
|
11
9
|
|
12
10
|
def default
|
@@ -22,9 +20,8 @@ module Gremlin
|
|
22
20
|
end
|
23
21
|
|
24
22
|
def get(labels={})
|
25
|
-
|
26
|
-
v
|
27
|
-
v.to_i
|
23
|
+
v = retention_get[labels.to_json]
|
24
|
+
cast(v)
|
28
25
|
end
|
29
26
|
end
|
30
27
|
end
|
@@ -4,10 +4,8 @@ module Gremlin
|
|
4
4
|
module Instruments
|
5
5
|
class Gauge < Base
|
6
6
|
def set(labels={}, value)
|
7
|
-
@
|
8
|
-
|
9
|
-
get(labels)
|
10
|
-
end
|
7
|
+
@r.hset retention_key, labels.to_json, value
|
8
|
+
get(labels)
|
11
9
|
end
|
12
10
|
|
13
11
|
def type
|
@@ -19,9 +17,8 @@ module Gremlin
|
|
19
17
|
end
|
20
18
|
|
21
19
|
def get(labels={})
|
22
|
-
|
23
|
-
v
|
24
|
-
v.to_f
|
20
|
+
v = retention_get[labels.to_json]
|
21
|
+
cast(v)
|
25
22
|
end
|
26
23
|
end
|
27
24
|
end
|
@@ -6,68 +6,68 @@ module Gremlin
|
|
6
6
|
class Value < Hash
|
7
7
|
def initialize(estimator)
|
8
8
|
@sum = estimator.sum
|
9
|
-
@total = estimator.observations
|
10
9
|
|
11
|
-
estimator.
|
12
|
-
self[
|
10
|
+
estimator.values.each do |percent, value|
|
11
|
+
self[percent] = value
|
13
12
|
end
|
14
13
|
end
|
15
14
|
end
|
16
15
|
|
17
16
|
def observe(labels={}, value)
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
17
|
+
# grabbing estimator from redis
|
18
|
+
# deserializing and converting back to objects
|
19
|
+
# adding a new observation into the values array
|
20
|
+
# set the updated estimator into redis
|
21
|
+
# return sum
|
22
|
+
# if retention_get == {}
|
23
|
+
# e = nil
|
24
|
+
# else
|
25
|
+
# e = parse(retention_get)[labels.to_json]
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# if e.nil?
|
29
|
+
# estimator = default
|
30
|
+
# else
|
31
|
+
# deserialized = JSON.parse(e)
|
32
|
+
# estimator = unpack(deserialized)
|
33
|
+
# end
|
34
|
+
# sum = estimator.observe(value)
|
35
|
+
#
|
36
|
+
# @r.hset retention_key, labels.to_json, p(estimator.serialize)
|
37
|
+
#
|
38
|
+
# sum
|
39
|
+
|
40
|
+
estimator = Gremlin::Quantile::Estimator.deserialize(retention_get[labels.to_json])
|
41
|
+
sum = estimator.observe(value)
|
42
|
+
@r.hset retention_key, labels.to_json, estimator.serialize
|
43
|
+
sum
|
34
44
|
end
|
35
45
|
|
36
46
|
def values
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
memo[JSON.parse(labels)] = Value.new(e)
|
42
|
-
end
|
47
|
+
retention_get.each_with_object({}) do |(labels, value), memo|
|
48
|
+
deserialized = JSON.parse(value)
|
49
|
+
e = unpack(deserialized)
|
50
|
+
memo[JSON.parse(labels)] = Value.new(e)
|
43
51
|
end
|
44
52
|
end
|
45
53
|
|
46
54
|
def get(labels={})
|
47
|
-
|
48
|
-
|
49
|
-
return val if val.nil?
|
55
|
+
val = retention_get[labels.to_json]
|
56
|
+
return val if val.nil?
|
50
57
|
|
51
|
-
|
52
|
-
|
53
|
-
end
|
58
|
+
deserialized = JSON.parse(val)
|
59
|
+
Value.new(unpack(deserialized))
|
54
60
|
end
|
55
61
|
|
56
|
-
def
|
57
|
-
|
58
|
-
metric_hash["head"],
|
59
|
-
metric_hash["observations"].to_i,
|
60
|
-
metric_hash["sum"].to_f,
|
61
|
-
*metric_hash["invariants"].map { |i| Gremlin::Quantile::Quantile.new i["quantile"], i["inaccuracy"] })
|
62
|
-
end
|
63
|
-
|
64
|
-
def repr
|
65
|
-
vals = values.each_with_object({}) { |(l,v), m| m[l.merge(@base_labels)] = v }
|
62
|
+
def parse(value)
|
63
|
+
vals = value.each_with_object({}) { |(l,v), m| m[JSON.parse(l).merge(@base_labels)] = cast(v) }
|
66
64
|
|
67
|
-
values = vals.each_with_object({}) do |(
|
68
|
-
|
69
|
-
|
70
|
-
|
65
|
+
values = vals.each_with_object({}) do |(l, v), memo|
|
66
|
+
v = JSON.parse(v)
|
67
|
+
if v.is_a? Hash
|
68
|
+
v = Value.new(unpack(v))
|
69
|
+
v.each do |b, i|
|
70
|
+
memo[l.merge({percent: b})] = i
|
71
71
|
end
|
72
72
|
end
|
73
73
|
end
|
@@ -80,6 +80,11 @@ module Gremlin
|
|
80
80
|
}
|
81
81
|
end
|
82
82
|
|
83
|
+
def unpack(metric_hash)
|
84
|
+
Gremlin::Quantile::Estimator.new(values: metric_hash["values"],
|
85
|
+
invariants: metric_hash["invariants"].map { |i| Gremlin::Quantile::Percentile.new(i["percent"]) })
|
86
|
+
end
|
87
|
+
|
83
88
|
def default
|
84
89
|
Gremlin::Quantile::Estimator.new
|
85
90
|
end
|
data/lib/gremlin/quantile.rb
CHANGED
@@ -1,27 +1,46 @@
|
|
1
1
|
module Gremlin
|
2
2
|
module Quantile
|
3
|
-
class Estimator
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
class Estimator
|
4
|
+
|
5
|
+
attr_accessor :invariants
|
6
|
+
|
7
|
+
def initialize(values: nil, invariants: nil)
|
8
|
+
if invariants.nil?
|
9
|
+
invariants = [Gremlin::Quantile::Percentile.new(0.5), Gremlin::Quantile::Percentile.new(0.9), Gremlin::Quantile::Percentile.new(0.99)]
|
7
10
|
end
|
8
11
|
|
9
12
|
@invariants = invariants
|
10
|
-
@
|
11
|
-
|
12
|
-
|
13
|
-
|
13
|
+
@values = values || []
|
14
|
+
end
|
15
|
+
|
16
|
+
def observe(value)
|
17
|
+
@values << value
|
18
|
+
sum
|
19
|
+
end
|
20
|
+
|
21
|
+
def sum
|
22
|
+
@values.reduce { |sum, x| sum += x }
|
23
|
+
end
|
24
|
+
|
25
|
+
def values
|
26
|
+
@values.sort!
|
27
|
+
invariants.inject(Hash.new) do |doc, invariant|
|
28
|
+
doc[invariant.percent] = invariant.calculate(@values)
|
29
|
+
doc
|
30
|
+
end
|
14
31
|
end
|
15
32
|
|
16
33
|
def serialize
|
17
34
|
JSON.dump({
|
18
|
-
|
19
|
-
|
20
|
-
head: @head,
|
21
|
-
sum: @sum,
|
22
|
-
observations: @observations
|
35
|
+
invariants: @invariants.map { |i| i.to_h },
|
36
|
+
values: @values
|
23
37
|
})
|
24
38
|
end
|
39
|
+
|
40
|
+
def self.deserialize(message)
|
41
|
+
doc = JSON.parse(message || "{}")
|
42
|
+
new(values: doc["values"], invariants: doc["invariants"])
|
43
|
+
end
|
25
44
|
end
|
26
45
|
end
|
27
46
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Gremlin
|
2
|
+
module Quantile
|
3
|
+
class Percentile
|
4
|
+
attr_accessor :percent
|
5
|
+
|
6
|
+
def initialize(percent)
|
7
|
+
@percent = percent
|
8
|
+
raise unless @percent
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_h
|
12
|
+
{
|
13
|
+
percent: @percent,
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def calculate(sorted_values)
|
18
|
+
sorted_values[(@percent * sorted_values.count).to_i]
|
19
|
+
end
|
20
|
+
|
21
|
+
def serialize
|
22
|
+
to_h.to_json
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/gremlin/railtie.rb
CHANGED
@@ -1,39 +1,4 @@
|
|
1
|
-
module Gremlin
|
2
|
-
module ActionController
|
3
|
-
class StartProcessingCounterReceiver < Gremlin::NotificationObserver::CounterReceiver
|
4
|
-
def call(name, start, finish, id, payload)
|
5
|
-
begin
|
6
|
-
instrument.increment({ controller: payload[:controller].to_s,
|
7
|
-
format: payload[:format].to_s,
|
8
|
-
action: payload[:action] })
|
9
|
-
rescue; end
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
class ProcessActionCounterReceiver < Gremlin::NotificationObserver::CounterReceiver
|
14
|
-
def call(name, start, finish, id, payload)
|
15
|
-
begin
|
16
|
-
instrument.increment({ controller: payload[:controller].to_s,
|
17
|
-
format: payload[:format].to_s,
|
18
|
-
action: payload[:action],
|
19
|
-
status: payload[:status] })
|
20
|
-
rescue; end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
class ProcessActionSummaryReceiver < Gremlin::NotificationObserver::SummaryReceiver
|
25
|
-
def call(name, start, finish, id, payload)
|
26
|
-
begin
|
27
|
-
instrument.observe({ controller: payload[:controller],
|
28
|
-
method: payload[:method],
|
29
|
-
status: payload[:status],
|
30
|
-
action: payload[:action] },
|
31
|
-
payload[@field_to_observe.to_sym])
|
32
|
-
rescue; end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
1
|
+
module Gremlin
|
37
2
|
class Railtie < ::Rails::Railtie
|
38
3
|
config.before_initialize do
|
39
4
|
Gremlin.configure do |c|
|
@@ -50,44 +15,101 @@ module Gremlin
|
|
50
15
|
end
|
51
16
|
end
|
52
17
|
end
|
18
|
+
end
|
19
|
+
end
|
53
20
|
|
54
|
-
|
21
|
+
module ActionController
|
22
|
+
class StatsSubscriber < ActiveSupport::Subscriber
|
23
|
+
attach_to :action_controller
|
24
|
+
|
25
|
+
def basic_labels
|
55
26
|
environment = ::Rails.env.to_s
|
56
27
|
application_name = ::Rails.application.class.parent_name.downcase
|
57
28
|
node = `hostname`.strip
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
29
|
+
|
30
|
+
{ node: node, app: application_name, env: environment }
|
31
|
+
end
|
32
|
+
|
33
|
+
def notification_count
|
34
|
+
@notification_count ||= Gremlin.registry.register Gremlin::Instruments::Counter.new("notification_count",
|
35
|
+
"Count of notifications handled by Gremlin.",
|
36
|
+
basic_labels)
|
37
|
+
end
|
38
|
+
|
39
|
+
def action_controller_start_processing_count
|
40
|
+
@start_processing_count ||= Gremlin.registry.register Gremlin::Instruments::Counter.new("action_controller_start_processing_count",
|
41
|
+
"Count of start_processing.action_controller notifications handled by Gremlin.",
|
42
|
+
basic_labels)
|
43
|
+
end
|
44
|
+
|
45
|
+
def action_controller_process_action_count
|
46
|
+
@process_action_count ||= Gremlin.registry.register Gremlin::Instruments::Counter.new("action_controller_process_action_count",
|
47
|
+
"Count of process_action.action_controller notifications handled by Gremlin.",
|
48
|
+
basic_labels)
|
49
|
+
end
|
50
|
+
|
51
|
+
def action_controller_process_action_view_runtime_ms
|
52
|
+
@process_action_view_runtime ||= Gremlin.registry.register Gremlin::Instruments::Summary.new("action_controller_process_action_view_runtime_ms",
|
53
|
+
"ActionController process action view runtime in milliseconds.",
|
54
|
+
basic_labels)
|
55
|
+
end
|
56
|
+
|
57
|
+
def action_controller_process_action_db_runtime_ms
|
58
|
+
@process_action_db_runtime ||= Gremlin.registry.register Gremlin::Instruments::Summary.new("action_controller_process_action_db_runtime_ms",
|
59
|
+
"ActionController process action db runtime in milliseconds.",
|
60
|
+
basic_labels)
|
61
|
+
end
|
62
|
+
|
63
|
+
def action_controller_process_action_total_runtime_ms
|
64
|
+
@process_action_total_runtime ||= Gremlin.registry.register Gremlin::Instruments::Summary.new("action_controller_process_action_total_runtime_ms",
|
65
|
+
"ActionController process action total runtime in milliseconds.",
|
66
|
+
basic_labels)
|
67
|
+
end
|
68
|
+
|
69
|
+
def process_action(event)
|
70
|
+
return if Gremlin.disabled?
|
71
|
+
notification_count.increment
|
72
|
+
|
73
|
+
action_controller_process_action_count.increment({ controller: event.payload[:controller].to_s,
|
74
|
+
format: event.payload[:format].to_s,
|
75
|
+
action: event.payload[:action],
|
76
|
+
status: event.payload[:status] })
|
77
|
+
|
78
|
+
begin
|
79
|
+
action_controller_process_action_view_runtime_ms.observe({ controller: event.payload[:controller],
|
80
|
+
method: event.payload[:method],
|
81
|
+
status: event.payload[:status],
|
82
|
+
action: event.payload[:action] },
|
83
|
+
event.payload[:view_runtime]
|
84
|
+
)
|
85
|
+
rescue; end
|
86
|
+
|
87
|
+
begin
|
88
|
+
action_controller_process_action_db_runtime_ms.observe({ controller: event.payload[:controller],
|
89
|
+
method: event.payload[:method],
|
90
|
+
status: event.payload[:status],
|
91
|
+
action: event.payload[:action] },
|
92
|
+
event.payload[:db_runtime]
|
93
|
+
)
|
94
|
+
rescue; end
|
95
|
+
|
96
|
+
begin
|
97
|
+
action_controller_process_action_total_runtime_ms.observe({ controller: event.payload[:controller],
|
98
|
+
method: event.payload[:method],
|
99
|
+
status: event.payload[:status],
|
100
|
+
action: event.payload[:action] },
|
101
|
+
event.duration
|
102
|
+
)
|
103
|
+
rescue; end
|
104
|
+
end
|
105
|
+
|
106
|
+
def start_processing(event)
|
107
|
+
return if Gremlin.disabled?
|
108
|
+
notification_count.increment
|
109
|
+
|
110
|
+
action_controller_start_processing_count.increment({ controller: event.payload[:controller].to_s,
|
111
|
+
format: event.payload[:format].to_s,
|
112
|
+
action: event.payload[:action] })
|
91
113
|
end
|
92
114
|
end
|
93
115
|
end
|
data/lib/gremlin/registry.rb
CHANGED
@@ -5,13 +5,7 @@ module Gremlin
|
|
5
5
|
NotAMetricError = Class.new StandardError
|
6
6
|
|
7
7
|
def initialize
|
8
|
-
|
9
|
-
@metrics = Redis::NativeHash.find(retention_key) || Redis::NativeHash.new()
|
10
|
-
#@metrics.redis = Redis::NativeHash.redis
|
11
|
-
@metrics.key = retention_key
|
12
|
-
@metrics.save
|
13
|
-
|
14
|
-
@mutex = Mutex.new
|
8
|
+
@r = Redis.new(**Gremlin.configuration.redis)
|
15
9
|
end
|
16
10
|
|
17
11
|
def node
|
@@ -22,44 +16,40 @@ module Gremlin
|
|
22
16
|
"gremlin_prometheus_#{node}_metrics_registry"
|
23
17
|
end
|
24
18
|
|
19
|
+
def retention_get
|
20
|
+
@r.hgetall retention_key
|
21
|
+
end
|
22
|
+
|
25
23
|
def metrics
|
26
|
-
|
24
|
+
retention_get.values.map do |m|
|
27
25
|
deserialize(m)
|
28
26
|
end
|
29
27
|
end
|
30
28
|
|
31
29
|
def dump_metrics
|
32
|
-
|
30
|
+
retention_get
|
33
31
|
end
|
34
32
|
|
35
33
|
def get(name)
|
36
|
-
m =
|
34
|
+
m = retention_get[name.to_s]
|
37
35
|
deserialize(m) unless m.nil?
|
38
36
|
end
|
39
37
|
|
40
38
|
def exist?(name)
|
41
|
-
@
|
39
|
+
@r.hexists retention_key, name.to_s
|
42
40
|
end
|
43
41
|
|
44
42
|
def register(instrument)
|
45
43
|
name = instrument.name
|
46
|
-
|
47
|
-
@mutex.synchronize do
|
48
|
-
@metrics[name.to_sym] = serialize(instrument)
|
49
|
-
@metrics.save
|
50
|
-
end
|
44
|
+
set = @r.hset retention_key, name, serialize(instrument)
|
51
45
|
|
52
46
|
instrument
|
53
47
|
end
|
54
48
|
|
55
49
|
def deregister(instrument)
|
56
50
|
name = instrument.name
|
57
|
-
raise NotRegisteredError.new(
|
58
|
-
|
59
|
-
@metrics.delete(instrument.name.to_sym)
|
60
|
-
@metrics.save
|
61
|
-
metrics
|
62
|
-
end
|
51
|
+
raise NotRegisteredError.new() if @r.hdel(retention_key, name) == 0
|
52
|
+
metrics
|
63
53
|
end
|
64
54
|
|
65
55
|
def deserialize(metric)
|
data/lib/gremlin/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gremlin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin G. Phillips
|
8
|
+
- Chris Constantine
|
8
9
|
autorequire:
|
9
10
|
bindir: exe
|
10
11
|
cert_chain: []
|
11
|
-
date: 2017-
|
12
|
+
date: 2017-03-03 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: bundler
|
@@ -73,13 +74,27 @@ dependencies:
|
|
73
74
|
- !ruby/object:Gem::Version
|
74
75
|
version: '0'
|
75
76
|
- !ruby/object:Gem::Dependency
|
76
|
-
name:
|
77
|
+
name: rspec-rails
|
77
78
|
requirement: !ruby/object:Gem::Requirement
|
78
79
|
requirements:
|
79
80
|
- - ">="
|
80
81
|
- !ruby/object:Gem::Version
|
81
82
|
version: '0'
|
82
|
-
type: :
|
83
|
+
type: :development
|
84
|
+
prerelease: false
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
- !ruby/object:Gem::Dependency
|
91
|
+
name: sqlite3
|
92
|
+
requirement: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
type: :development
|
83
98
|
prerelease: false
|
84
99
|
version_requirements: !ruby/object:Gem::Requirement
|
85
100
|
requirements:
|
@@ -136,6 +151,9 @@ extensions: []
|
|
136
151
|
extra_rdoc_files: []
|
137
152
|
files:
|
138
153
|
- ".gitignore"
|
154
|
+
- ".rspec"
|
155
|
+
- ".ruby-gemset"
|
156
|
+
- ".ruby-version"
|
139
157
|
- Gemfile
|
140
158
|
- Gemfile.lock
|
141
159
|
- LICENSE.txt
|
@@ -144,6 +162,7 @@ files:
|
|
144
162
|
- app/controllers/gremlin/metrics_controller.rb
|
145
163
|
- benchmark.rb
|
146
164
|
- bin/console
|
165
|
+
- bin/rails
|
147
166
|
- bin/setup
|
148
167
|
- config/routes.rb
|
149
168
|
- gremlin.gemspec
|
@@ -156,7 +175,7 @@ files:
|
|
156
175
|
- lib/gremlin/notification_observer.rb
|
157
176
|
- lib/gremlin/quantile.rb
|
158
177
|
- lib/gremlin/quantile/estimator.rb
|
159
|
-
- lib/gremlin/quantile/
|
178
|
+
- lib/gremlin/quantile/percentile.rb
|
160
179
|
- lib/gremlin/railtie.rb
|
161
180
|
- lib/gremlin/registry.rb
|
162
181
|
- lib/gremlin/version.rb
|