rails-instrumentation 0.1.0

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,44 @@
1
+ module Rails
2
+ module Instrumentation
3
+ module ActionViewSubscriber
4
+ include Subscriber
5
+
6
+ EVENT_NAMESPACE = 'action_view'.freeze
7
+
8
+ EVENTS = %w[
9
+ render_template
10
+ render_partial
11
+ render_collection
12
+ ].freeze
13
+
14
+ class << self
15
+ def render_template(event)
16
+ tags = {
17
+ 'template.identifier' => event.payload[:identifier],
18
+ 'template.layout' => event.payload[:layout]
19
+ }
20
+
21
+ Utils.trace_notification(event: event, tags: tags)
22
+ end
23
+
24
+ def render_partial(event)
25
+ tags = {
26
+ 'partial.identifier' => event.payload[:identifier]
27
+ }
28
+
29
+ Utils.trace_notification(event: event, tags: tags)
30
+ end
31
+
32
+ def render_collection(event)
33
+ tags = {
34
+ 'template.identifier' => event.payload[:identifier],
35
+ 'template.count' => event.payload[:count]
36
+ }
37
+ tags['template.cache_hits'] = event.payload[:cache_hits] if event.payload.key? :cache_hits
38
+
39
+ Utils.trace_notification(event: event, tags: tags)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,54 @@
1
+ module Rails
2
+ module Instrumentation
3
+ module ActiveJobSubscriber
4
+ include Subscriber
5
+
6
+ EVENT_NAMESPACE = 'active_job'.freeze
7
+
8
+ EVENTS = %w[
9
+ enqueue_at
10
+ enqueue
11
+ perform_start
12
+ perform
13
+ ].freeze
14
+
15
+ class << self
16
+ def enqueue_at(event)
17
+ tags = {
18
+ 'adapter' => event.payload[:adapter],
19
+ 'job' => event.payload[:job]
20
+ }
21
+
22
+ Utils.trace_notification(event: event, tags: tags)
23
+ end
24
+
25
+ def enqueue(event)
26
+ tags = {
27
+ 'adapter' => event.payload[:adapter],
28
+ 'job' => event.payload[:job]
29
+ }
30
+
31
+ Utils.trace_notification(event: event, tags: tags)
32
+ end
33
+
34
+ def perform_start(event)
35
+ tags = {
36
+ 'adapter' => event.payload[:adapter],
37
+ 'job' => event.payload[:job]
38
+ }
39
+
40
+ Utils.trace_notification(event: event, tags: tags)
41
+ end
42
+
43
+ def perform(event)
44
+ tags = {
45
+ 'adapter' => event.payload[:adapter],
46
+ 'job' => event.payload[:job]
47
+ }
48
+
49
+ Utils.trace_notification(event: event, tags: tags)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,39 @@
1
+ # require 'active_support'
2
+
3
+ module Rails
4
+ module Instrumentation
5
+ module ActiveRecordSubscriber
6
+ include Subscriber
7
+
8
+ EVENT_NAMESPACE = 'active_record'.freeze
9
+
10
+ EVENTS = %w[
11
+ sql
12
+ instantiation
13
+ ].freeze
14
+
15
+ class << self
16
+ def sql(event)
17
+ tags = {
18
+ 'db.statement' => event.payload[:sql],
19
+ 'name' => event.payload[:name],
20
+ 'connection_id' => event.payload[:connection_id],
21
+ 'binds' => event.payload[:binds],
22
+ 'cached' => event.payload[:cached]
23
+ }
24
+
25
+ Utils.trace_notification(event: event, tags: tags)
26
+ end
27
+
28
+ def instantiation(event)
29
+ tags = {
30
+ 'record.count' => event.payload[:record_count],
31
+ 'record.class' => event.payload[:class_name]
32
+ }
33
+
34
+ Utils.trace_notification(event: event, tags: tags)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,87 @@
1
+ module Rails
2
+ module Instrumentation
3
+ module ActiveStorageSubscriber
4
+ include Subscriber
5
+
6
+ EVENT_NAMESPACE = 'active_storage'.freeze
7
+
8
+ EVENTS = %w[
9
+ service_upload
10
+ service_streaming_download
11
+ service_download
12
+ service_delete
13
+ service_delete_prefixed
14
+ service_exist
15
+ service_url
16
+ ].freeze
17
+
18
+ class << self
19
+ def service_upload(event)
20
+ tags = {
21
+ 'key' => event.payload[:key],
22
+ 'service' => event.payload[:service],
23
+ 'checksum' => event.payload[:checksum]
24
+ }
25
+
26
+ Utils.trace_notification(event: event, tags: tags)
27
+ end
28
+
29
+ def service_streaming_download(event)
30
+ tags = {
31
+ 'key' => event.payload[:key],
32
+ 'service' => event.payload[:service]
33
+ }
34
+
35
+ Utils.trace_notification(event: event, tags: tags)
36
+ end
37
+
38
+ def service_download(event)
39
+ tags = {
40
+ 'key' => event.payload[:key],
41
+ 'service' => event.payload[:service]
42
+ }
43
+
44
+ Utils.trace_notification(event: event, tags: tags)
45
+ end
46
+
47
+ def service_delete(event)
48
+ tags = {
49
+ 'key' => event.payload[:key],
50
+ 'service' => event.payload[:service]
51
+ }
52
+
53
+ Utils.trace_notification(event: event, tags: tags)
54
+ end
55
+
56
+ def service_delete_prefixed(event)
57
+ tags = {
58
+ 'key.prefix' => event.payload[:prefix],
59
+ 'service' => event.payload[:service]
60
+ }
61
+
62
+ Utils.trace_notification(event: event, tags: tags)
63
+ end
64
+
65
+ def service_exist(event)
66
+ tags = {
67
+ 'key' => event.payload[:key],
68
+ 'service' => event.payload[:service],
69
+ 'exist' => event.payload[:exist]
70
+ }
71
+
72
+ Utils.trace_notification(event: event, tags: tags)
73
+ end
74
+
75
+ def service_url(event)
76
+ tags = {
77
+ 'key' => event.payload[:key],
78
+ 'service' => event.payload[:service],
79
+ 'url' => event.payload[:url] # generated url, not accessed url
80
+ }
81
+
82
+ Utils.trace_notification(event: event, tags: tags)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,70 @@
1
+ module Rails
2
+ module Instrumentation
3
+ module ActiveSupportSubscriber
4
+ include Subscriber
5
+
6
+ EVENT_NAMESPACE = 'active_support'.freeze
7
+
8
+ EVENTS = %w[
9
+ cache_read
10
+ cache_generate
11
+ cache_fetch_hit
12
+ cache_write
13
+ cache_delete
14
+ cache_exist?
15
+ ].freeze
16
+
17
+ class << self
18
+ def cache_read(event)
19
+ tags = {
20
+ 'key' => event.payload[:key],
21
+ 'hit' => event.payload[:hit],
22
+ 'super_operation' => event.payload[:super_operation]
23
+ }
24
+
25
+ Utils.trace_notification(event: event, tags: tags)
26
+ end
27
+
28
+ def cache_generate(event)
29
+ tags = {
30
+ 'key' => event.payload[:key]
31
+ }
32
+
33
+ Utils.trace_notification(event: event, tags: tags)
34
+ end
35
+
36
+ def cache_fetch_hit(event)
37
+ tags = {
38
+ 'key' => event.payload[:key]
39
+ }
40
+
41
+ Utils.trace_notification(event: event, tags: tags)
42
+ end
43
+
44
+ def cache_write(event)
45
+ tags = {
46
+ 'key' => event.payload[:key]
47
+ }
48
+
49
+ Utils.trace_notification(event: event, tags: tags)
50
+ end
51
+
52
+ def cache_delete(event)
53
+ tags = {
54
+ 'key' => event.payload[:key]
55
+ }
56
+
57
+ Utils.trace_notification(event: event, tags: tags)
58
+ end
59
+
60
+ def cache_exist?(event)
61
+ tags = {
62
+ 'key' => event.payload[:key]
63
+ }
64
+
65
+ Utils.trace_notification(event: event, tags: tags)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,47 @@
1
+ require 'active_support/notifications'
2
+
3
+ module Rails
4
+ module Instrumentation
5
+ module Utils
6
+ class << self
7
+ # calls a handler function with name 'event' on the handler module.
8
+ # For example, if the handler module is ActionViewSubscriber and the
9
+ # event hook is 'render_template.action_controller', full_name is
10
+ # 'render_template.action_controller' and event_name is 'render_template'
11
+ def register_subscriber(full_name: '', event_name: '', handler_module: nil)
12
+ ::ActiveSupport::Notifications.subscribe(full_name) do |*args|
13
+ event = ::ActiveSupport::Notifications::Event.new(*args)
14
+ handler_module.send(event_name, event)
15
+ end
16
+ end
17
+
18
+ # takes and event and some set of tags from a handler, and creates a
19
+ # span with the event's name and the start and finish times.
20
+ def trace_notification(event:, tags: [])
21
+ tags = tags.merge(::Rails::Instrumentation::TAGS)
22
+
23
+ span = ::Rails::Instrumentation.tracer.start_span(event.name,
24
+ tags: tags,
25
+ start_time: event.time)
26
+
27
+ # tag transaction_id
28
+ span.set_tag('transaction.id', event.transaction_id)
29
+ tag_error(span, event.payload) if event.payload.key? :exception
30
+
31
+ span.finish(end_time: event.end)
32
+ end
33
+
34
+ # according to the ActiveSupport::Notifications documentation, exceptions
35
+ # will be indicated with the presence of the :exception and :exception_object
36
+ # keys. These will be tagged and logged according to the OpenTracing
37
+ # specification.
38
+ def tag_error(span, payload)
39
+ span.set_tag('error', true)
40
+ span.log_kv(key: 'error.kind', value: payload[:exception].first)
41
+ span.log_kv(key: 'message', value: payload[:exception].last)
42
+ span.log_kv(key: 'error.object', value: payload[:exception_object])
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,5 @@
1
+ module Rails
2
+ module Instrumentation
3
+ VERSION = '0.1.0'.freeze
4
+ end
5
+ end
@@ -0,0 +1,32 @@
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'rails/instrumentation/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'rails-instrumentation'
7
+ spec.version = Rails::Instrumentation::VERSION
8
+ spec.authors = ['Ashwin Chandrasekar']
9
+ spec.email = ['achandrasekar@signalfx.com']
10
+
11
+ spec.summary = 'OpenTracing instrumentation for Rails.'
12
+ spec.homepage = 'https://github.com/signalfx/ruby-rails-instrumentation'
13
+
14
+ # Specify which files should be added to the gem when it is released.
15
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
16
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
17
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ end
19
+ spec.bindir = 'exe'
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_dependency 'opentracing', '~> 0.3'
24
+ spec.add_development_dependency 'appraisal', '~> 2.2'
25
+ spec.add_development_dependency 'bundler', '~> 1.17'
26
+ spec.add_development_dependency 'opentracing_test_tracer', '~> 0.1'
27
+ spec.add_development_dependency 'rails', '~> 5.2.2'
28
+ spec.add_development_dependency 'rake', '~> 10.0'
29
+ spec.add_development_dependency 'rspec', '~> 3.0'
30
+ spec.add_development_dependency 'rubocop', '~> 0.63.0'
31
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.31'
32
+ end
metadata ADDED
@@ -0,0 +1,191 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails-instrumentation
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ashwin Chandrasekar
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-02-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: opentracing
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: appraisal
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.2'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.17'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.17'
55
+ - !ruby/object:Gem::Dependency
56
+ name: opentracing_test_tracer
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rails
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 5.2.2
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 5.2.2
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '10.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '10.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.63.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.63.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop-rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.31'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.31'
139
+ description:
140
+ email:
141
+ - achandrasekar@signalfx.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - ".gitignore"
147
+ - ".rubocop.yml"
148
+ - Appraisals
149
+ - Gemfile
150
+ - LICENSE
151
+ - README.md
152
+ - Rakefile
153
+ - bin/console
154
+ - bin/setup
155
+ - lib/rails/instrumentation.rb
156
+ - lib/rails/instrumentation/patch.rb
157
+ - lib/rails/instrumentation/subscriber.rb
158
+ - lib/rails/instrumentation/subscribers/action_cable_subscriber.rb
159
+ - lib/rails/instrumentation/subscribers/action_controller_subscriber.rb
160
+ - lib/rails/instrumentation/subscribers/action_mailer_subscriber.rb
161
+ - lib/rails/instrumentation/subscribers/action_view_subscriber.rb
162
+ - lib/rails/instrumentation/subscribers/active_job_subscriber.rb
163
+ - lib/rails/instrumentation/subscribers/active_record_subscriber.rb
164
+ - lib/rails/instrumentation/subscribers/active_storage_subscriber.rb
165
+ - lib/rails/instrumentation/subscribers/active_support_subscriber.rb
166
+ - lib/rails/instrumentation/utils.rb
167
+ - lib/rails/instrumentation/version.rb
168
+ - rails-instrumentation.gemspec
169
+ homepage: https://github.com/signalfx/ruby-rails-instrumentation
170
+ licenses: []
171
+ metadata: {}
172
+ post_install_message:
173
+ rdoc_options: []
174
+ require_paths:
175
+ - lib
176
+ required_ruby_version: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ required_rubygems_version: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
186
+ requirements: []
187
+ rubygems_version: 3.0.2
188
+ signing_key:
189
+ specification_version: 4
190
+ summary: OpenTracing instrumentation for Rails.
191
+ test_files: []