gremlin 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f7287de278b662763ddc2ca3cca5d38c41bdd190
4
+ data.tar.gz: 823ee29d0d1dac97672d32d3649c502699271f0a
5
+ SHA512:
6
+ metadata.gz: d40b078ddda9c56352cb260a493002bc9e13fa695cb781b5065fedd08c99a0c78205c8161c02953ce0004eaaf1f72473d3971744bc2d33faf5108623dee2b741
7
+ data.tar.gz: 6ba936ae95928ad71d409f3213921ced78bca12e71f189b38c8da808fe158bbd9e33930a7b67d2c1a3857afbb9f845a9d83b95b4343498502e5088ab7238a6dd
@@ -0,0 +1,2 @@
1
+ *.sw*
2
+ spec/examples.txt
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,142 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ gremlin (0.0.1)
5
+ quantile
6
+ rails
7
+ redis (~> 3.2)
8
+ redis-native_hash
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ actioncable (5.0.1)
14
+ actionpack (= 5.0.1)
15
+ nio4r (~> 1.2)
16
+ websocket-driver (~> 0.6.1)
17
+ actionmailer (5.0.1)
18
+ actionpack (= 5.0.1)
19
+ actionview (= 5.0.1)
20
+ activejob (= 5.0.1)
21
+ mail (~> 2.5, >= 2.5.4)
22
+ rails-dom-testing (~> 2.0)
23
+ actionpack (5.0.1)
24
+ actionview (= 5.0.1)
25
+ activesupport (= 5.0.1)
26
+ rack (~> 2.0)
27
+ rack-test (~> 0.6.3)
28
+ rails-dom-testing (~> 2.0)
29
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
30
+ actionview (5.0.1)
31
+ activesupport (= 5.0.1)
32
+ builder (~> 3.1)
33
+ erubis (~> 2.7.0)
34
+ rails-dom-testing (~> 2.0)
35
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
36
+ activejob (5.0.1)
37
+ activesupport (= 5.0.1)
38
+ globalid (>= 0.3.6)
39
+ activemodel (5.0.1)
40
+ activesupport (= 5.0.1)
41
+ activerecord (5.0.1)
42
+ activemodel (= 5.0.1)
43
+ activesupport (= 5.0.1)
44
+ arel (~> 7.0)
45
+ activesupport (5.0.1)
46
+ concurrent-ruby (~> 1.0, >= 1.0.2)
47
+ i18n (~> 0.7)
48
+ minitest (~> 5.1)
49
+ tzinfo (~> 1.1)
50
+ arel (7.1.4)
51
+ builder (3.2.3)
52
+ concurrent-ruby (1.0.4)
53
+ diff-lcs (1.2.5)
54
+ erubis (2.7.0)
55
+ globalid (0.3.7)
56
+ activesupport (>= 4.1.0)
57
+ i18n (0.7.0)
58
+ loofah (2.0.3)
59
+ nokogiri (>= 1.5.9)
60
+ mail (2.6.4)
61
+ mime-types (>= 1.16, < 4)
62
+ method_source (0.8.2)
63
+ mime-types (3.1)
64
+ mime-types-data (~> 3.2015)
65
+ mime-types-data (3.2016.0521)
66
+ mini_portile2 (2.1.0)
67
+ minitest (5.10.1)
68
+ nio4r (1.2.1)
69
+ nokogiri (1.7.0.1)
70
+ mini_portile2 (~> 2.1.0)
71
+ quantile (0.2.0)
72
+ rack (2.0.1)
73
+ rack-test (0.6.3)
74
+ rack (>= 1.0)
75
+ rails (5.0.1)
76
+ actioncable (= 5.0.1)
77
+ actionmailer (= 5.0.1)
78
+ actionpack (= 5.0.1)
79
+ actionview (= 5.0.1)
80
+ activejob (= 5.0.1)
81
+ activemodel (= 5.0.1)
82
+ activerecord (= 5.0.1)
83
+ activesupport (= 5.0.1)
84
+ bundler (>= 1.3.0, < 2.0)
85
+ railties (= 5.0.1)
86
+ sprockets-rails (>= 2.0.0)
87
+ rails-dom-testing (2.0.2)
88
+ activesupport (>= 4.2.0, < 6.0)
89
+ nokogiri (~> 1.6)
90
+ rails-html-sanitizer (1.0.3)
91
+ loofah (~> 2.0)
92
+ railties (5.0.1)
93
+ actionpack (= 5.0.1)
94
+ activesupport (= 5.0.1)
95
+ method_source
96
+ rake (>= 0.8.7)
97
+ thor (>= 0.18.1, < 2.0)
98
+ rake (10.5.0)
99
+ rdoc (5.0.0)
100
+ redis (3.3.2)
101
+ redis-native_hash (0.2.1)
102
+ redis (>= 2.0.0)
103
+ rspec (3.5.0)
104
+ rspec-core (~> 3.5.0)
105
+ rspec-expectations (~> 3.5.0)
106
+ rspec-mocks (~> 3.5.0)
107
+ rspec-core (3.5.4)
108
+ rspec-support (~> 3.5.0)
109
+ rspec-expectations (3.5.0)
110
+ diff-lcs (>= 1.2.0, < 2.0)
111
+ rspec-support (~> 3.5.0)
112
+ rspec-mocks (3.5.0)
113
+ diff-lcs (>= 1.2.0, < 2.0)
114
+ rspec-support (~> 3.5.0)
115
+ rspec-support (3.5.0)
116
+ sprockets (3.7.1)
117
+ concurrent-ruby (~> 1.0)
118
+ rack (> 1, < 3)
119
+ sprockets-rails (3.2.0)
120
+ actionpack (>= 4.0)
121
+ activesupport (>= 4.0)
122
+ sprockets (>= 3.0.0)
123
+ thor (0.19.4)
124
+ thread_safe (0.3.5)
125
+ tzinfo (1.2.2)
126
+ thread_safe (~> 0.1)
127
+ websocket-driver (0.6.4)
128
+ websocket-extensions (>= 0.1.0)
129
+ websocket-extensions (0.1.2)
130
+
131
+ PLATFORMS
132
+ ruby
133
+
134
+ DEPENDENCIES
135
+ bundler (~> 1.11)
136
+ gremlin!
137
+ rake (~> 10.0)
138
+ rdoc
139
+ rspec (~> 3.3, >= 3.3.0)
140
+
141
+ BUNDLED WITH
142
+ 1.11.2
@@ -0,0 +1,35 @@
1
+ module Gremlin
2
+ class MetricsController < ::ActionController::Base
3
+ def index
4
+ render plain: marshal.join("\n")
5
+ end
6
+
7
+ def marshal
8
+ registry = Gremlin.registry
9
+
10
+ metrics = registry.metrics.map do |metric|
11
+ metric.repr
12
+ end
13
+
14
+ plaintext(metrics)
15
+ end
16
+
17
+ def plaintext(metric_representations)
18
+ lines = []
19
+ metric_representations.each do |repr|
20
+ lines << repr[:type]
21
+ lines << repr[:help]
22
+
23
+ # Prometheus expects metric labels to be of the format <label>="<label_value>"
24
+ repr[:values].each do |labels, value|
25
+ l = []
26
+ labels.each do |k,v|
27
+ l << "#{k}=\"#{v}\""
28
+ end
29
+ lines << "#{repr[:name].to_s}{#{l.join(',')}} #{value}"
30
+ end
31
+ end
32
+ lines
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,5 @@
1
+ Gremlin::Engine.routes.draw do
2
+ scope module: 'gremlin' do
3
+ resources :metrics, only: :index
4
+ end
5
+ end
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'gremlin/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "gremlin"
8
+ spec.version = Gremlin::VERSION
9
+ spec.authors = ["Kevin G. Phillips"]
10
+ spec.email = ["platform@omadahealth.com"]
11
+
12
+ spec.summary = %q{Gem to sanely emit metrics from Omadahealth rails applications}
13
+ spec.description = %q{A modular metrics harness for Omadahealth rails applications}
14
+ spec.homepage = "https://github.com/omadahealth/omada-metrics"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
+ # delete this section to allow pushing this gem to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
+ end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_development_dependency "bundler", "~> 1.11"
31
+ spec.add_development_dependency "rake", "~> 10.0"
32
+ spec.add_development_dependency 'rspec', '~> 3.3', '>= 3.3.0'
33
+ spec.add_development_dependency 'rdoc'
34
+
35
+ spec.add_runtime_dependency 'redis-native_hash'
36
+ spec.add_runtime_dependency 'quantile'
37
+ spec.add_runtime_dependency 'rails'
38
+ spec.add_runtime_dependency 'redis', '~> 3.2'
39
+ end
@@ -0,0 +1,27 @@
1
+ # Supporting librarires
2
+ require "active_support/cache"
3
+ require "active_support/notifications" if defined?(::Rails)
4
+ require "quantile"
5
+ require "json"
6
+ require "redis_hash"
7
+
8
+ # Gem internals
9
+ require "gremlin/version"
10
+ require "gremlin/registry"
11
+ require "gremlin/instruments"
12
+ require "gremlin/instruments/counter"
13
+ require "gremlin/instruments/gauge"
14
+ require "gremlin/instruments/summary"
15
+ require "gremlin/quantile"
16
+
17
+ # Rails support
18
+ require "gremlin/engine" if defined?(::Rails::Engine)
19
+ require "gremlin/railtie" if defined?(::Rails)
20
+
21
+ module Gremlin
22
+ class << self
23
+ def registry
24
+ @registry ||= Gremlin::Registry.new
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,5 @@
1
+ module Gremlin
2
+ class Engine < ::Rails::Engine
3
+ engine_name = "gremlin"
4
+ end
5
+ end
@@ -0,0 +1,82 @@
1
+ module Gremlin
2
+ module Instruments
3
+ class Base
4
+ attr_accessor :name, :base_labels, :docstring
5
+
6
+ def initialize(name, docstring="placeholder help string", base_labels={})
7
+ @name = name
8
+ @docstring = docstring
9
+ @base_labels = base_labels
10
+ @mutex = Mutex.new
11
+
12
+ @values = Redis::NativeHash.find(retention_key) || Redis::NativeHash.new
13
+ @values.key = retention_key
14
+ @values.save
15
+ end
16
+
17
+ def node
18
+ `hostname`.strip
19
+ end
20
+
21
+ def retention_key
22
+ nil
23
+ end
24
+
25
+ def type
26
+ nil
27
+ end
28
+
29
+ def type_string
30
+ "# TYPE #{@name} #{type}"
31
+ end
32
+
33
+ def default
34
+ nil
35
+ end
36
+
37
+ def help
38
+ @docstring
39
+ end
40
+
41
+ def help_string
42
+ "# HELP #{@name} #{help}"
43
+ end
44
+
45
+ def repr
46
+ {
47
+ name: @name,
48
+ type: type_string,
49
+ help: help_string,
50
+ values: values.each_with_object({}) { |(l,v), m| m[l.merge(@base_labels)] = v }
51
+ }
52
+ end
53
+
54
+ def values
55
+ @mutex.synchronize do
56
+ @values.each_with_object({}) do |(labels, value), memo|
57
+ case type
58
+ when :counter
59
+ memo[JSON.parse(labels)] = value.to_i
60
+ when :gauge
61
+ memo[JSON.parse(labels)] = value.to_f
62
+ else
63
+ memo[JSON.parse(labels)] = value
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ def get(labels={})
70
+ v = @values[labels.to_json]
71
+ case type
72
+ when :counter
73
+ v.to_i
74
+ when :gauge
75
+ v.to_f
76
+ else
77
+ v
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,28 @@
1
+ require 'gremlin/instruments'
2
+
3
+ module Gremlin
4
+ module Instruments
5
+ class Counter < Base
6
+ def increment(labels={}, by=1)
7
+ @mutex.synchronize do
8
+ prev_val = get(labels.to_json) || default
9
+ @values[labels.to_json] = prev_val + by
10
+ @values.save
11
+ @values[labels.to_json]
12
+ end
13
+ end
14
+
15
+ def default
16
+ 0
17
+ end
18
+
19
+ def type
20
+ :counter
21
+ end
22
+
23
+ def retention_key
24
+ "gremlin_prometheus_#{node}_metrics_counter_#{name}"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,23 @@
1
+ require 'gremlin/instruments'
2
+
3
+ module Gremlin
4
+ module Instruments
5
+ class Gauge < Base
6
+ def set(labels={}, value)
7
+ @mutex.synchronize do
8
+ @values[labels.to_json] = value
9
+ @values.save
10
+ @values[labels.to_json]
11
+ end
12
+ end
13
+
14
+ def type
15
+ :gauge
16
+ end
17
+
18
+ def retention_key
19
+ "gremlin_prometheus_#{node}_metrics_gauge_#{name}"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,96 @@
1
+ require 'gremlin/instruments'
2
+
3
+ module Gremlin
4
+ module Instruments
5
+ class Summary < Base
6
+ class Value < Hash
7
+ def initialize(estimator)
8
+ @sum = estimator.sum
9
+ @total = estimator.observations
10
+
11
+ estimator.invariants.each do |invariant|
12
+ self[invariant.quantile] = estimator.query(invariant.quantile)
13
+ end
14
+ end
15
+ end
16
+
17
+ def observe(labels={}, value)
18
+ @mutex.synchronize do
19
+ e = @values[labels.to_json]
20
+
21
+ if e.nil?
22
+ estimator = default
23
+ else
24
+ deserialized = JSON.parse(e)
25
+ estimator = unpack(deserialized)
26
+ end
27
+ sum = estimator.observe(value)
28
+
29
+ @values[labels.to_json] = estimator.serialize
30
+ @values.save
31
+
32
+ sum
33
+ end
34
+ end
35
+
36
+ def values
37
+ @mutex.synchronize do
38
+ @values.each_with_object({}) do |(labels, value), memo|
39
+ deserialized = JSON.parse(value)
40
+ e = unpack(deserialized)
41
+ memo[JSON.parse(labels)] = Value.new(e)
42
+ end
43
+ end
44
+ end
45
+
46
+ def get(labels={})
47
+ @mutex.synchronize do
48
+ val = @values[labels.to_json]
49
+ return val if val.nil?
50
+
51
+ deserialized = JSON.parse(val)
52
+ Value.new(unpack(deserialized))
53
+ end
54
+ end
55
+
56
+ def unpack(metric_hash)
57
+ Gremlin::Quantile::Estimator.new(metric_hash["buffer"],
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 }
66
+
67
+ values = vals.each_with_object({}) do |(labels, value), memo|
68
+ if value.is_a? Hash
69
+ value.each do |b, i|
70
+ memo[labels.merge({quantile: b})] = i
71
+ end
72
+ end
73
+ end
74
+
75
+ {
76
+ name: @name,
77
+ type: type_string,
78
+ help: help_string,
79
+ values: values
80
+ }
81
+ end
82
+
83
+ def default
84
+ Gremlin::Quantile::Estimator.new
85
+ end
86
+
87
+ def type
88
+ :summary
89
+ end
90
+
91
+ def retention_key
92
+ "gremlin_prometheus_#{node}_summary_#{name}"
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,7 @@
1
+ require 'gremlin/quantile/estimator'
2
+ require 'gremlin/quantile/quantile'
3
+
4
+ module Gremlin
5
+ module Quantile
6
+ end
7
+ end
@@ -0,0 +1,27 @@
1
+ module Gremlin
2
+ module Quantile
3
+ class Estimator < ::Quantile::Estimator
4
+ def initialize(buffer=[], head=nil, observations=0, sum=0, *invariants)
5
+ if invariants.empty?
6
+ invariants = [Gremlin::Quantile::Quantile.new(0.5, 0.05), Gremlin::Quantile::Quantile.new(0.9, 0.01), Gremlin::Quantile::Quantile.new(0.99, 0.001)]
7
+ end
8
+
9
+ @invariants = invariants
10
+ @buffer = buffer
11
+ @head = head
12
+ @observations = observations
13
+ @sum = sum
14
+ end
15
+
16
+ def serialize
17
+ JSON.dump({
18
+ invariants: @invariants.map { |i| i.to_h },
19
+ buffer: @buffer,
20
+ head: @head,
21
+ sum: @sum,
22
+ observations: @observations
23
+ })
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,16 @@
1
+ module Gremlin
2
+ module Quantile
3
+ class Quantile < ::Quantile::Quantile
4
+ def to_h
5
+ {
6
+ quantile: @quantile,
7
+ inaccuracy: @inaccuracy
8
+ }
9
+ end
10
+
11
+ def serialize
12
+ to_h.to_json
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,130 @@
1
+ module Gremlin
2
+ class Railtie < ::Rails::Railtie
3
+ config.before_initialize do
4
+ # XXX is there anything to configure? redis?
5
+ end
6
+
7
+ initializer "gremlin.base_instrumentation" do |app|
8
+ environment = ::Rails.env.to_s
9
+ application_name = ::Rails.application.class.parent_name.downcase
10
+ node = `hostname`.strip
11
+
12
+ #
13
+ # ActionController
14
+ #
15
+
16
+ ActiveSupport::Notifications.subscribe "start_processing.action_controller" do |name, start, finish, id, payload|
17
+ # Payload contains:
18
+ # :controller --- The controller name
19
+ # :action --- The action
20
+ # :params --- Hash of request parameters without any filtered parameter
21
+ # :headers --- Request headers
22
+ # :format --- html/js/json/xml etc
23
+ # :method --- HTTP request verb
24
+ # :path --- Request path
25
+
26
+ # Counter to track event occurances
27
+ begin
28
+ occurance_counter = Gremlin::Instruments::Counter.new("action_controller_start_processing_events".to_sym,
29
+ "ActionController start processing occurance counter.",
30
+ application: application_name, environment: environment, node: node)
31
+ Gremlin.registry.register occurance_counter
32
+ rescue Gremlin::Registry::AlreadyRegisteredError
33
+ occurance_counter = Gremlin.registry.get "action_controller_start_processing_events".to_sym
34
+ end
35
+
36
+ occurance_counter.increment({ controller: payload[:controller].to_s, format: payload[:format].to_s, action: payload[:action] })
37
+ end
38
+
39
+ ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, start, finish, id, payload|
40
+ # Payload contains:
41
+ # :controller --- The controller name
42
+ # :action --- The action
43
+ # :params --- Hash of request parameters *without any filtered parameter*
44
+ # :headers --- Request headers
45
+ # :format --- html/js/json/xml etc
46
+ # :method --- HTTP request verb
47
+ # :path --- Request path
48
+ # :status --- HTTP status code
49
+ # :view_runtime --- Amount spent in view in ms
50
+ # :db_runtime --- Amount spent executing database queries in ms
51
+
52
+ # Counter to track event occurances
53
+ begin
54
+ occurance_counter = Gremlin::Instruments::Counter.new("action_controller_process_action_events".to_sym,
55
+ "ActionController process action occurance counter.",
56
+ application: application_name, environment: environment, node: node)
57
+ Gremlin.registry.register occurance_counter
58
+ rescue Gremlin::Registry::AlreadyRegisteredError
59
+ occurance_counter = Gremlin.registry.get "action_controller_process_action_events".to_sym
60
+ end
61
+
62
+ occurance_counter.increment({ controller: payload[:controller].to_s, format: payload[:format].to_s, action: payload[:action], status: payload[:status] })
63
+
64
+ begin
65
+ db_summary = Gremlin::Instruments::Summary.new("action_controller_process_action_db_runtime_ms".to_sym,
66
+ "ActionController process action DB runtime in milliseconds.",
67
+ application: application_name, environment: environment, node: node)
68
+ Gremlin.registry.register db_summary
69
+ rescue Gremlin::Registry::AlreadyRegisteredError
70
+ db_summary = Gremlin.registry.get "action_controller_process_action_db_runtime_ms".to_sym
71
+ end
72
+
73
+ db_summary.observe({ controller: payload[:controller], method: payload[:method], status: payload[:status], action: payload[:action] }, payload[:db_runtime])
74
+
75
+ begin
76
+ view_summary = Gremlin::Instruments::Summary.new("action_controller_process_action_view_runtime_ms".to_sym,
77
+ "ActionController process action view runtime in milliseconds.",
78
+ application: application_name, environment: environment, node: node)
79
+ Gremlin.registry.register view_summary
80
+ rescue Gremlin::Registry::AlreadyRegisteredError
81
+ view_summary = Gremlin.registry.get "action_controller_process_action_view_runtime_ms".to_sym
82
+ end
83
+
84
+ view_summary.observe({ controller: payload[:controller], method: payload[:method], status: payload[:status], action: payload[:action] }, payload[:view_runtime])
85
+ end
86
+
87
+ ActiveSupport::Notifications.subscribe "redirect_to.action_controller" do |name, start, finish, id, payload|
88
+ # Payload contains:
89
+ # :status --- HTTP response code
90
+ # :location --- URL to redirect to
91
+
92
+ # Counter to track event occurances
93
+ begin
94
+ occurance_counter = Gremlin::Instruments::Counter.new("action_controller_redirect_to_events".to_sym,
95
+ "ActionController redirect to occurance counter.",
96
+ application: application_name, environment: environment, node: node)
97
+ Gremlin.registry.register occurance_counter
98
+ rescue Gremlin::Registry::AlreadyRegisteredError
99
+ occurance_counter = Gremlin.registry.get "action_controller_redirect_to_events".to_sym
100
+ end
101
+
102
+ occurance_counter.increment({ status: payload[:status] })
103
+ end
104
+
105
+ ##
106
+ ## ActiveRecord
107
+ ##
108
+
109
+ ActiveSupport::Notifications.subscribe "sql.active_record" do |name, start, finish, id, payload|
110
+ # Payload contains:
111
+ # :sql --- SQL statement
112
+ # :name --- Name of the operation
113
+ # :connection_id --- self.object_id
114
+ # :binds --- Bind parameters
115
+
116
+ # Counter to track event occurances
117
+ begin
118
+ occurance_counter = Gremlin::Instruments::Counter.new("active_record_sql_events".to_sym,
119
+ "ActiveRecord SQL occurance counter.",
120
+ application: application_name, environment: environment, node: node)
121
+ Gremlin.registry.register occurance_counter
122
+ rescue Gremlin::Registry::AlreadyRegisteredError
123
+ occurance_counter = Gremlin.registry.get "active_record_sql_events".to_sym
124
+ end
125
+
126
+ occurance_counter.increment
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,74 @@
1
+ module Gremlin
2
+ class Registry
3
+ AlreadyRegisteredError = Class.new StandardError
4
+ NotRegisteredError = Class.new StandardError
5
+ NotAMetricError = Class.new StandardError
6
+
7
+ def initialize
8
+ @metrics = Redis::NativeHash.find(retention_key) || Redis::NativeHash.new()
9
+ @metrics.key = retention_key
10
+ @metrics.save
11
+
12
+ @mutex = Mutex.new
13
+ end
14
+
15
+ def node
16
+ `hostname`.strip
17
+ end
18
+
19
+ def retention_key
20
+ "gremlin_prometheus_#{node}_metrics_registry"
21
+ end
22
+
23
+ def metrics
24
+ @metrics.values.map do |m|
25
+ deserialize(m)
26
+ end
27
+ end
28
+
29
+ def get(name)
30
+ m = @metrics[name.to_sym]
31
+ deserialize(m) unless m.nil?
32
+ end
33
+
34
+ def exist?(name)
35
+ @metrics.key? name
36
+ end
37
+
38
+ def register(instrument)
39
+ name = instrument.name
40
+ raise AlreadyRegisteredError.new("#{name} has already been registered") if exist? name
41
+ @mutex.synchronize do
42
+ @metrics[name.to_sym] = serialize(instrument)
43
+ @metrics.save
44
+ end
45
+
46
+ instrument
47
+ end
48
+
49
+ def deregister(instrument)
50
+ name = instrument.name
51
+ raise NotRegisteredError.new("#{name} has not been registered.") unless exist? name
52
+ @mutex.synchronize do
53
+ @metrics.delete(instrument.name.to_sym)
54
+ @metrics.save
55
+ metrics
56
+ end
57
+ end
58
+
59
+ def deserialize(metric)
60
+ m = JSON.parse(metric)
61
+ Gremlin::Instruments.const_get(m["type"].capitalize).new(m["name"].to_sym, m["docstring"], m["base_labels"])
62
+ end
63
+
64
+ def serialize(metric)
65
+ raise NotAMetricError.new("Specified object is not an instrument and cannot be serialized") unless metric.is_a? Gremlin::Instruments::Base
66
+ {
67
+ type: metric.type,
68
+ name: metric.name.to_s,
69
+ docstring: metric.docstring,
70
+ base_labels: metric.base_labels
71
+ }.to_json
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,3 @@
1
+ module Gremlin
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,182 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gremlin
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Kevin G. Phillips
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-01-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.3'
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: 3.3.0
51
+ type: :development
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - "~>"
56
+ - !ruby/object:Gem::Version
57
+ version: '3.3'
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 3.3.0
61
+ - !ruby/object:Gem::Dependency
62
+ name: rdoc
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: redis-native_hash
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: quantile
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :runtime
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: rails
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :runtime
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ - !ruby/object:Gem::Dependency
118
+ name: redis
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '3.2'
124
+ type: :runtime
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '3.2'
131
+ description: A modular metrics harness for Omadahealth rails applications
132
+ email:
133
+ - platform@omadahealth.com
134
+ executables: []
135
+ extensions: []
136
+ extra_rdoc_files: []
137
+ files:
138
+ - ".gitignore"
139
+ - Gemfile
140
+ - Gemfile.lock
141
+ - app/controllers/gremlin/metrics_controller.rb
142
+ - config/routes.rb
143
+ - gremlin.gemspec
144
+ - lib/gremlin.rb
145
+ - lib/gremlin/engine.rb
146
+ - lib/gremlin/instruments.rb
147
+ - lib/gremlin/instruments/counter.rb
148
+ - lib/gremlin/instruments/gauge.rb
149
+ - lib/gremlin/instruments/summary.rb
150
+ - lib/gremlin/quantile.rb
151
+ - lib/gremlin/quantile/estimator.rb
152
+ - lib/gremlin/quantile/quantile.rb
153
+ - lib/gremlin/railtie.rb
154
+ - lib/gremlin/registry.rb
155
+ - lib/gremlin/version.rb
156
+ homepage: https://github.com/omadahealth/omada-metrics
157
+ licenses:
158
+ - MIT
159
+ metadata:
160
+ allowed_push_host: https://rubygems.org
161
+ post_install_message:
162
+ rdoc_options: []
163
+ require_paths:
164
+ - lib
165
+ required_ruby_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
170
+ required_rubygems_version: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ version: '0'
175
+ requirements: []
176
+ rubyforge_project:
177
+ rubygems_version: 2.5.1
178
+ signing_key:
179
+ specification_version: 4
180
+ summary: Gem to sanely emit metrics from Omadahealth rails applications
181
+ test_files: []
182
+ has_rdoc: