signalfx-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.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "rails/tracer"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,4 @@
1
+ memcached:
2
+ image: memcached
3
+ ports:
4
+ - 11211:11211
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_RETRY: "1"
@@ -0,0 +1,11 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "pry"
6
+ gem "pry-stack_explorer"
7
+ gem "pry-byebug"
8
+ gem "rails", "~> 3.2.0"
9
+ gem "test-unit", "~> 3.0"
10
+
11
+ gemspec path: "../"
@@ -0,0 +1,10 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "pry"
6
+ gem "pry-stack_explorer"
7
+ gem "pry-byebug"
8
+ gem "rails", "~> 5.1.3"
9
+
10
+ gemspec path: "../"
@@ -0,0 +1,10 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "pry"
6
+ gem "pry-stack_explorer"
7
+ gem "pry-byebug"
8
+ gem "rails", "~> 4.0.0"
9
+
10
+ gemspec path: "../"
@@ -0,0 +1,10 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "pry"
6
+ gem "pry-stack_explorer"
7
+ gem "pry-byebug"
8
+ gem "rails", "~> 4.1.0"
9
+
10
+ gemspec path: "../"
@@ -0,0 +1,10 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "pry"
6
+ gem "pry-stack_explorer"
7
+ gem "pry-byebug"
8
+ gem "rails", "~> 4.2.0"
9
+
10
+ gemspec path: "../"
@@ -0,0 +1,10 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "pry"
6
+ gem "pry-stack_explorer"
7
+ gem "pry-byebug"
8
+ gem "rails", "~> 5.1.3"
9
+
10
+ gemspec path: "../"
@@ -0,0 +1,10 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "pry"
6
+ gem "pry-stack_explorer"
7
+ gem "pry-byebug"
8
+ gem "rails", "~> 5.0.0"
9
+
10
+ gemspec path: "../"
@@ -0,0 +1,10 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "pry"
6
+ gem "pry-stack_explorer"
7
+ gem "pry-byebug"
8
+ gem "rails", "~> 5.1.0"
9
+
10
+ gemspec path: "../"
@@ -0,0 +1 @@
1
+ require 'rails/tracer'
@@ -0,0 +1,100 @@
1
+
2
+ module ActionController
3
+ module Tracer
4
+ COMPONENT = "ActionController".freeze
5
+
6
+ class << self
7
+ def instrument(tracer: OpenTracing.global_tracer, active_span: nil)
8
+ @subscribers = []
9
+ @subscribers << ::ActiveSupport::Notifications.subscribe('start_processing.action_controller') do |*args|
10
+ ActionController::Tracer.start_processing(tracer: tracer, active_span: active_span, args: args)
11
+ end
12
+ @subscribers << ::ActiveSupport::Notifications.subscribe('process_action.action_controller') do |*args|
13
+ ActionController::Tracer.process_action(tracer: tracer, active_span: active_span, args: args)
14
+ end
15
+ end
16
+
17
+ def disable
18
+ @subscribers.each do |subscriber|
19
+ ::ActiveSupport::Notifications.unsubscribe(subscriber)
20
+ end
21
+ @subscribers.clear
22
+ self
23
+ end
24
+
25
+ def start_processing(tracer: OpenTracing.global_tracer, active_span: nil, args: {})
26
+ event, start, finish, id, payload = *args
27
+
28
+ # extract the rack context, if it exists
29
+ # it seems like this might be the earliest place env is available
30
+ rack_span = Rails::Tracer::SpanHelpers.rack_span(payload)
31
+ Rails::Tracer::Defer.add_parent(id, rack_span)
32
+
33
+ path = payload.fetch(:path)
34
+ name = "#{payload.fetch(:controller)}##{payload.fetch(:action)} #{event} #{path}"
35
+ tags = {
36
+ 'component' => COMPONENT,
37
+ 'span.kind' => 'client',
38
+ 'http.method' => payload.fetch(:method),
39
+ 'http.path' => path,
40
+ }
41
+
42
+ if !Rails::Tracer::Defer.enabled
43
+ span = tracer.start_span(name,
44
+ child_of: active_span.respond_to?(:call) ? active_span.call : active_span,
45
+ start_time: start,
46
+ tags: tags)
47
+
48
+ span.finish(end_time: finish)
49
+ else
50
+ spaninfo = {
51
+ 'event' => event,
52
+ 'name' => name,
53
+ 'start' => start,
54
+ 'finish' => finish,
55
+ 'tags' => tags,
56
+ }
57
+
58
+ Rails::Tracer::Defer.defer_span(id: id, spaninfo: spaninfo)
59
+ end
60
+ end
61
+
62
+ def process_action(tracer: OpenTracing.global_tracer, active_span: nil, args: {})
63
+ event, start, finish, id, payload = *args
64
+
65
+ path = payload.fetch(:path)
66
+ name = "#{payload.fetch(:controller)}##{payload.fetch(:action)} #{path}"
67
+ tags = {
68
+ 'component' => COMPONENT,
69
+ 'span.kind' => 'client',
70
+ 'http.method' => payload.fetch(:method),
71
+ 'http.status_code' => payload.fetch(:status),
72
+ 'http.path' => path,
73
+ 'view.runtime' => payload.fetch(:view_runtime),
74
+ 'db.runtime' => payload.fetch(:db_runtime),
75
+ }
76
+
77
+ if !Rails::Tracer::Defer.enabled
78
+ # write out the span
79
+ span = tracer.start_span(name,
80
+ child_of: active_span.respond_to?(:call) ? active_span.call : active_span,
81
+ start_time: start,
82
+ tags: tags)
83
+
84
+ span.finish(end_time: finish)
85
+ else
86
+ # defer the spans if full_trace is configured
87
+ spaninfo = {
88
+ 'event' => event,
89
+ 'name' => name,
90
+ 'start' => start,
91
+ 'finish' => finish,
92
+ 'tags' => tags,
93
+ }
94
+
95
+ Rails::Tracer::Defer.defer_span(id: id, spaninfo: spaninfo)
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,105 @@
1
+
2
+ module ActionView
3
+ module Tracer
4
+ COMPONENT = "ActionView".freeze
5
+
6
+ class << self
7
+ def instrument(tracer: OpenTracing.global_tracer, active_span: nil)
8
+ @subscribers = []
9
+ @subscribers << ::ActiveSupport::Notifications.subscribe('render_template.action_view') do |*args|
10
+ ActionView::Tracer.render_template(tracer: tracer, active_span: active_span, args: args)
11
+ end
12
+ @subscribers << ::ActiveSupport::Notifications.subscribe('render_partial.action_view') do |*args|
13
+ ActionView::Tracer.render_partial(tracer: tracer, active_span: active_span, args: args)
14
+ end
15
+ @subscribers << ::ActiveSupport::Notifications.subscribe('render_collection.action_view') do |*args|
16
+ ActionView::Tracer.render_collection(tracer: tracer, active_span: active_span, args: args)
17
+ end
18
+ end
19
+
20
+ def disable
21
+ @subscribers.each do |subscriber|
22
+ ::ActiveSupport::Notifications.unsubscribe(subscriber)
23
+ end
24
+ @subscribers = []
25
+ self
26
+ end
27
+
28
+ def render_template(tracer: OpenTracing.global_tracer, active_span: nil, args: {})
29
+ event, start, finish, id, payload = *args
30
+
31
+ tags = {
32
+ 'component' => COMPONENT,
33
+ 'span.kind' => 'client',
34
+ 'template_path' => payload.fetch(:identifier),
35
+ 'layout' => payload.fetch(:layout),
36
+ }
37
+
38
+ handle_notification(tracer: tracer,
39
+ active_span: active_span,
40
+ id: id,
41
+ name: event,
42
+ tags: tags,
43
+ start: start,
44
+ finish: finish)
45
+ end
46
+
47
+ def render_partial(tracer: OpenTracing.global_tracer, active_span: nil, args: {})
48
+ event, start, finish, id, payload = *args
49
+
50
+ tags = {
51
+ 'component' => COMPONENT,
52
+ 'span.kind' => 'client',
53
+ 'template_path' => payload.fetch(:identifier),
54
+ }
55
+
56
+ handle_notification(tracer: tracer,
57
+ active_span: active_span,
58
+ id: id,
59
+ name: event,
60
+ tags: tags,
61
+ start: start,
62
+ finish: finish)
63
+ end
64
+
65
+ def render_collection(tracer: OpenTracing.global_tracer, active_span: nil, args: {})
66
+ event, start, finish, id, payload = *args
67
+
68
+ tags = {
69
+ 'component' => COMPONENT,
70
+ 'span.kind' => 'client',
71
+ 'template_path' => payload.fetch(:identifier),
72
+ 'collection_size' => payload.fetch(:count),
73
+ }
74
+
75
+ handle_notification(tracer: tracer,
76
+ active_span: active_span,
77
+ id: id,
78
+ name: event,
79
+ tags: tags,
80
+ start: start,
81
+ finish: finish)
82
+ end
83
+
84
+ def handle_notification(tracer:, active_span:, id:, name:, tags:, start:, finish:)
85
+ if !Rails::Tracer::Defer.enabled
86
+ span = tracer.start_span(name,
87
+ child_of: active_span.respond_to?(:call) ? active_span.call : active_span,
88
+ start_time: start,
89
+ tags: tags)
90
+
91
+ span.finish(end_time: finish)
92
+ else
93
+ spaninfo = {
94
+ 'event' => name,
95
+ 'name' => name,
96
+ 'start' => start,
97
+ 'finish' => finish,
98
+ 'tags' => tags,
99
+ }
100
+ Rails::Tracer::Defer.defer_span(id: id, spaninfo: spaninfo)
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,89 @@
1
+ module ActiveRecord
2
+ module Tracer
3
+ DEFAULT_OPERATION_NAME = "sql.query".freeze
4
+
5
+ class << self
6
+ def instrument(tracer: OpenTracing.global_tracer, active_span: nil)
7
+ clear_subscribers
8
+ @subscriber = ::ActiveSupport::Notifications.subscribe('sql.active_record') do |*args|
9
+ ActiveRecord::Tracer.sql(tracer: tracer, active_span: active_span, args: args)
10
+ end
11
+
12
+ self
13
+ end
14
+
15
+ def disable
16
+ if @subscriber
17
+ ActiveSupport::Notifications.unsubscribe(@subscriber)
18
+ @subscriber = nil
19
+ end
20
+
21
+ self
22
+ end
23
+ alias :clear_subscribers :disable
24
+
25
+ def sql(tracer: OpenTracing.global_tracer, active_span: nil, args:)
26
+ event, start, finish, id, payload = *args
27
+
28
+ connection_config = ::ActiveRecord::Base.connection_config
29
+ name = payload.fetch(:name)
30
+ tags = {
31
+ 'component' => 'ActiveRecord',
32
+ 'span.kind' => 'client',
33
+ 'db.user' => connection_config.fetch(:username, 'unknown'),
34
+ 'db.instance' => connection_config.fetch(:database),
35
+ 'db.vendor' => connection_config.fetch(:adapter),
36
+ 'db.connection_id' => payload.fetch(:connection_id, 'unknown'),
37
+ 'db.cached' => payload.fetch(:cached, false),
38
+ 'db.statement' => payload.fetch(:sql),
39
+ 'db.type' => 'sql'
40
+ }
41
+
42
+ if !Rails::Tracer::Defer.enabled
43
+ span = tracer.start_span(name || DEFAULT_OPERATION_NAME,
44
+ child_of: active_span.respond_to?(:call) ? active_span.call : active_span,
45
+ start_time: start,
46
+ tags: tags)
47
+
48
+ if payload[:exception]
49
+ Rails::Tracer::SpanHelpers.set_error(span, payload[:exception_object] || payload[:exception])
50
+ end
51
+
52
+ span.finish(end_time: finish)
53
+ else
54
+ spaninfo = {
55
+ 'event' => event,
56
+ 'name' => name || DEFAULT_OPERATION_NAME,
57
+ 'start' => start,
58
+ 'finish' => finish,
59
+ 'tags' => tags,
60
+ }
61
+
62
+ # errors aren't being propagated yet this way...
63
+
64
+ Rails::Tracer::Defer.defer_span(id: id, spaninfo: spaninfo)
65
+ end
66
+ end
67
+
68
+ def start_span(operation_name, tracer: OpenTracing.global_tracer, active_span: nil, start_time: Time.now, **fields)
69
+ connection_config = ::ActiveRecord::Base.connection_config
70
+
71
+ span = tracer.start_span(operation_name || DEFAULT_OPERATION_NAME,
72
+ child_of: active_span.respond_to?(:call) ? active_span.call : active_span,
73
+ start_time: start_time,
74
+ tags: {
75
+ 'component' => 'ActiveRecord',
76
+ 'span.kind' => 'client',
77
+ 'db.user' => connection_config.fetch(:username, 'unknown'),
78
+ 'db.instance' => connection_config.fetch(:database),
79
+ 'db.vendor' => connection_config.fetch(:adapter),
80
+ 'db.connection_id' => fields.fetch(:connection_id, 'unknown'),
81
+ 'db.cached' => fields.fetch(:cached, false),
82
+ 'db.statement' => fields.fetch(:sql),
83
+ 'db.type' => 'sql'
84
+ })
85
+ span
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,11 @@
1
+ module ActiveSupport
2
+ module Cache
3
+ class Store
4
+ # See the PR https://github.com/rails/rails/pull/15943/files
5
+ # In order to make the instrumentation to work we need to override the original implementation
6
+ def self.instrument
7
+ true
8
+ end
9
+ end
10
+ end
11
+ end