gremlin 0.0.1

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,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: