skylight-core 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/lib/skylight/core/config.rb +454 -0
  3. data/lib/skylight/core/errors.rb +6 -0
  4. data/lib/skylight/core/fanout.rb +44 -0
  5. data/lib/skylight/core/formatters/http.rb +23 -0
  6. data/lib/skylight/core/gc.rb +107 -0
  7. data/lib/skylight/core/instrumentable.rb +144 -0
  8. data/lib/skylight/core/instrumenter.rb +249 -0
  9. data/lib/skylight/core/middleware.rb +101 -0
  10. data/lib/skylight/core/normalizers/action_controller/process_action.rb +50 -0
  11. data/lib/skylight/core/normalizers/action_controller/send_file.rb +50 -0
  12. data/lib/skylight/core/normalizers/action_view/render_collection.rb +22 -0
  13. data/lib/skylight/core/normalizers/action_view/render_partial.rb +21 -0
  14. data/lib/skylight/core/normalizers/action_view/render_template.rb +21 -0
  15. data/lib/skylight/core/normalizers/active_job/enqueue_at.rb +21 -0
  16. data/lib/skylight/core/normalizers/active_model_serializers/render.rb +26 -0
  17. data/lib/skylight/core/normalizers/active_record/instantiation.rb +17 -0
  18. data/lib/skylight/core/normalizers/active_record/sql.rb +33 -0
  19. data/lib/skylight/core/normalizers/active_support/cache.rb +20 -0
  20. data/lib/skylight/core/normalizers/active_support/cache_clear.rb +16 -0
  21. data/lib/skylight/core/normalizers/active_support/cache_decrement.rb +16 -0
  22. data/lib/skylight/core/normalizers/active_support/cache_delete.rb +16 -0
  23. data/lib/skylight/core/normalizers/active_support/cache_exist.rb +16 -0
  24. data/lib/skylight/core/normalizers/active_support/cache_fetch_hit.rb +16 -0
  25. data/lib/skylight/core/normalizers/active_support/cache_generate.rb +16 -0
  26. data/lib/skylight/core/normalizers/active_support/cache_increment.rb +16 -0
  27. data/lib/skylight/core/normalizers/active_support/cache_read.rb +16 -0
  28. data/lib/skylight/core/normalizers/active_support/cache_read_multi.rb +16 -0
  29. data/lib/skylight/core/normalizers/active_support/cache_write.rb +16 -0
  30. data/lib/skylight/core/normalizers/coach/handler_finish.rb +36 -0
  31. data/lib/skylight/core/normalizers/coach/middleware_finish.rb +23 -0
  32. data/lib/skylight/core/normalizers/couch_potato/query.rb +20 -0
  33. data/lib/skylight/core/normalizers/data_mapper/sql.rb +12 -0
  34. data/lib/skylight/core/normalizers/default.rb +27 -0
  35. data/lib/skylight/core/normalizers/elasticsearch/request.rb +20 -0
  36. data/lib/skylight/core/normalizers/faraday/request.rb +37 -0
  37. data/lib/skylight/core/normalizers/grape/endpoint.rb +30 -0
  38. data/lib/skylight/core/normalizers/grape/endpoint_render.rb +26 -0
  39. data/lib/skylight/core/normalizers/grape/endpoint_run.rb +33 -0
  40. data/lib/skylight/core/normalizers/grape/endpoint_run_filters.rb +23 -0
  41. data/lib/skylight/core/normalizers/moped/query.rb +100 -0
  42. data/lib/skylight/core/normalizers/sequel/sql.rb +12 -0
  43. data/lib/skylight/core/normalizers/sql.rb +49 -0
  44. data/lib/skylight/core/normalizers.rb +170 -0
  45. data/lib/skylight/core/probes/action_controller.rb +31 -0
  46. data/lib/skylight/core/probes/action_view.rb +37 -0
  47. data/lib/skylight/core/probes/active_model_serializers.rb +55 -0
  48. data/lib/skylight/core/probes/elasticsearch.rb +37 -0
  49. data/lib/skylight/core/probes/excon/middleware.rb +72 -0
  50. data/lib/skylight/core/probes/excon.rb +26 -0
  51. data/lib/skylight/core/probes/faraday.rb +22 -0
  52. data/lib/skylight/core/probes/grape.rb +80 -0
  53. data/lib/skylight/core/probes/httpclient.rb +46 -0
  54. data/lib/skylight/core/probes/middleware.rb +58 -0
  55. data/lib/skylight/core/probes/mongo.rb +171 -0
  56. data/lib/skylight/core/probes/mongoid.rb +21 -0
  57. data/lib/skylight/core/probes/moped.rb +39 -0
  58. data/lib/skylight/core/probes/net_http.rb +64 -0
  59. data/lib/skylight/core/probes/redis.rb +71 -0
  60. data/lib/skylight/core/probes/sequel.rb +33 -0
  61. data/lib/skylight/core/probes/sinatra.rb +69 -0
  62. data/lib/skylight/core/probes/tilt.rb +27 -0
  63. data/lib/skylight/core/probes.rb +129 -0
  64. data/lib/skylight/core/railtie.rb +166 -0
  65. data/lib/skylight/core/subscriber.rb +124 -0
  66. data/lib/skylight/core/test.rb +98 -0
  67. data/lib/skylight/core/trace.rb +190 -0
  68. data/lib/skylight/core/user_config.rb +61 -0
  69. data/lib/skylight/core/util/allocation_free.rb +26 -0
  70. data/lib/skylight/core/util/clock.rb +56 -0
  71. data/lib/skylight/core/util/deploy.rb +132 -0
  72. data/lib/skylight/core/util/gzip.rb +21 -0
  73. data/lib/skylight/core/util/inflector.rb +112 -0
  74. data/lib/skylight/core/util/logging.rb +127 -0
  75. data/lib/skylight/core/util/platform.rb +77 -0
  76. data/lib/skylight/core/util/proxy.rb +13 -0
  77. data/lib/skylight/core/util.rb +14 -0
  78. data/lib/skylight/core/vendor/active_support/notifications.rb +207 -0
  79. data/lib/skylight/core/vendor/active_support/per_thread_registry.rb +52 -0
  80. data/lib/skylight/core/vendor/thread_safe/non_concurrent_cache_backend.rb +133 -0
  81. data/lib/skylight/core/vendor/thread_safe/synchronized_cache_backend.rb +76 -0
  82. data/lib/skylight/core/vendor/thread_safe.rb +126 -0
  83. data/lib/skylight/core/version.rb +6 -0
  84. data/lib/skylight/core/vm/gc.rb +70 -0
  85. data/lib/skylight/core.rb +99 -0
  86. metadata +254 -0
@@ -0,0 +1,49 @@
1
+ require "json"
2
+
3
+ module Skylight::Core
4
+ module Normalizers
5
+ # Normalizer for SQL requests
6
+ class SQL < Normalizer
7
+ CAT = "db.sql.query".freeze
8
+
9
+ # @param trace [Skylight::Messages::Trace::Builder] ignored, only present to match API
10
+ # @param name [String] ignored, only present to match API
11
+ # @param payload [Hash]
12
+ # @option payload [String] [:name] The SQL operation
13
+ # @option payload [Hash] [:binds] The bound parameters
14
+ # @return [Array]
15
+ def normalize(trace, name, payload)
16
+ case payload[:name]
17
+ when "SCHEMA".freeze, "CACHE".freeze
18
+ return :skip
19
+ else
20
+ name = CAT
21
+ title = payload[:name] || "SQL".freeze
22
+ end
23
+
24
+ binds = payload[:binds]
25
+
26
+ if binds && !binds.empty?
27
+ binds = binds.map { |_col, val| val.inspect }
28
+ end
29
+
30
+ begin
31
+ extracted_title, sql = extract_binds(trace.instrumenter, payload, binds)
32
+ [ name, extracted_title || title, sql ]
33
+ rescue => e
34
+ # FIXME: Rust errors get written to STDERR and don't come through here
35
+ if config[:log_sql_parse_errors]
36
+ config.logger.warn "failed to extract binds in SQL; sql=#{payload[:sql].inspect}; exception=#{e.inspect}"
37
+ end
38
+ [ name, title, nil ]
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def extract_binds(instrumenter, payload, _precalculated)
45
+ instrumenter.process_sql(payload[:sql])
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,170 @@
1
+ require 'skylight/core/normalizers/default'
2
+
3
+ module Skylight::Core
4
+ # @api private
5
+ # Convert AS::N events to Skylight events
6
+ module Normalizers
7
+
8
+ DEFAULT = Default.new
9
+
10
+ def self.register(name, klass)
11
+ (@registry ||= {})[name] = klass
12
+ klass
13
+ end
14
+
15
+ def self.build(config)
16
+ normalizers = {}
17
+
18
+ (@registry || {}).each do |k, klass|
19
+ unless klass.method_defined?(:normalize)
20
+ # TODO: Warn
21
+ next
22
+ end
23
+
24
+ normalizers[k] = klass.new(config)
25
+ end
26
+
27
+ Container.new(normalizers)
28
+ end
29
+
30
+ class Normalizer
31
+ def self.register(name)
32
+ Normalizers.register(name, self)
33
+ end
34
+
35
+ attr_reader :config
36
+
37
+ def initialize(config)
38
+ @config = config
39
+ setup if respond_to?(:setup)
40
+ end
41
+
42
+ def normalize(trace, name, payload)
43
+ :skip
44
+ end
45
+
46
+ def normalize_after(trace, span, name, payload)
47
+ end
48
+ end
49
+
50
+ # Base Normalizer for Rails rendering
51
+ class RenderNormalizer < Normalizer
52
+ include Util::AllocationFree
53
+
54
+ def setup
55
+ @paths = config['normalizers.render.view_paths'] || []
56
+ end
57
+
58
+ # Generic normalizer for renders
59
+ # @param category [String]
60
+ # @param payload [Hash]
61
+ # @option payload [String] :identifier
62
+ # @return [Array]
63
+ def normalize_render(category, payload)
64
+ if path = payload[:identifier]
65
+ title = relative_path(path)
66
+ path = nil if path == title
67
+ end
68
+
69
+ [ category, title, nil ]
70
+ end
71
+
72
+ def relative_path(path)
73
+ return path if relative_path?(path)
74
+
75
+ root = array_find(@paths) { |p| path.start_with?(p) }
76
+ type = :project
77
+
78
+ unless root
79
+ root = array_find(Gem.path) { |p| path.start_with?(p) }
80
+ type = :gem
81
+ end
82
+
83
+ if root
84
+ start = root.size
85
+ start += 1 if path.getbyte(start) == SEPARATOR_BYTE
86
+ if type == :gem
87
+ "$GEM_PATH/#{path[start, path.size]}"
88
+ else
89
+ path[start, path.size]
90
+ end
91
+ else
92
+ "Absolute Path"
93
+ end
94
+ end
95
+
96
+ private
97
+ def relative_path?(path)
98
+ !absolute_path?(path)
99
+ end
100
+
101
+ SEPARATOR_BYTE = File::SEPARATOR.ord
102
+
103
+ if File.const_defined?(:NULL) ? File::NULL == "NUL" : RbConfig::CONFIG['host_os'] =~ /mingw|mswin32/
104
+ # This is a DOSish environment
105
+ ALT_SEPARATOR_BYTE = File::ALT_SEPARATOR && File::ALT_SEPARATOR.ord
106
+ COLON_BYTE = ":".ord
107
+ def absolute_path?(path)
108
+ if alpha?(path.getbyte(0)) && path.getbyte(1) == COLON_BYTE
109
+ byte2 = path.getbyte(2)
110
+ byte2 == SEPARATOR_BYTE || byte2 == ALT_SEPARATOR_BYTE
111
+ end
112
+ end
113
+
114
+ def alpha?(byte)
115
+ byte >= 65 and byte <= 90 || byte >= 97 and byte <= 122
116
+ end
117
+ else
118
+ def absolute_path?(path)
119
+ path.getbyte(0) == SEPARATOR_BYTE
120
+ end
121
+ end
122
+ end
123
+
124
+ class Container
125
+ def initialize(normalizers)
126
+ @normalizers = normalizers
127
+ end
128
+
129
+ def keys
130
+ @normalizers.keys
131
+ end
132
+
133
+ def normalize(trace, name, payload)
134
+ normalizer_for(name).normalize(trace, name, payload)
135
+ end
136
+
137
+ def normalize_after(trace, span, name, payload)
138
+ normalizer_for(name).normalize_after(trace, span, name, payload)
139
+ end
140
+
141
+ def normalizer_for(name)
142
+ @normalizers[name] || DEFAULT
143
+ end
144
+ end
145
+
146
+ %w( action_controller/process_action
147
+ action_controller/send_file
148
+ action_view/render_collection
149
+ action_view/render_partial
150
+ action_view/render_template
151
+ active_model_serializers/render
152
+ active_record/instantiation
153
+ active_record/sql
154
+ active_support/cache
155
+ coach/handler_finish
156
+ coach/middleware_finish
157
+ couch_potato/query
158
+ data_mapper/sql
159
+ elasticsearch/request
160
+ faraday/request
161
+ grape/endpoint
162
+ moped/query
163
+ sequel/sql).each do |file|
164
+ require "skylight/core/normalizers/#{file}"
165
+ end
166
+
167
+ # The following are not required by default as they are of dubious usefulness:
168
+ # - active_job/enqueue_at
169
+ end
170
+ end
@@ -0,0 +1,31 @@
1
+ module Skylight::Core
2
+ module Probes
3
+ module ActionController
4
+ class Probe
5
+ def install
6
+ ::ActionController::Instrumentation.class_eval do
7
+ private
8
+ alias append_info_to_payload_without_sk append_info_to_payload
9
+ def append_info_to_payload(payload)
10
+ append_info_to_payload_without_sk(payload)
11
+ rendered_mime = begin
12
+ if respond_to?(:rendered_format)
13
+ rendered_format
14
+ elsif content_type.is_a?(Mime::Type)
15
+ content_type
16
+ elsif content_type.respond_to?(:to_s)
17
+ type_str = content_type.to_s.split(';').first
18
+ Mime::Type.lookup(type_str) unless type_str.blank?
19
+ end
20
+ end
21
+ payload[:rendered_format] = rendered_mime.try(:ref)
22
+ payload[:variant] = request.respond_to?(:variant) ? request.variant : nil
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ register("ActionController::Instrumentation", "action_controller/metal/instrumentation", ActionController::Probe.new)
30
+ end
31
+ end
@@ -0,0 +1,37 @@
1
+ module Skylight::Core
2
+ module Probes
3
+ module ActionView
4
+ class Probe
5
+ def install
6
+ ::ActionView::TemplateRenderer.class_eval do
7
+ alias render_with_layout_without_sk render_with_layout
8
+
9
+ def render_with_layout(path, locals, *args, &block) #:nodoc:
10
+ layout = nil
11
+
12
+ if path
13
+ if method(:find_layout).arity == 3
14
+ # Rails 5
15
+ layout = find_layout(path, locals.keys, [formats.first])
16
+ else
17
+ # Rails 4
18
+ layout = find_layout(path, locals.keys)
19
+ end
20
+ end
21
+
22
+ if layout
23
+ instrument(:template, :identifier => layout.identifier) do
24
+ render_with_layout_without_sk(path, locals, *args, &block)
25
+ end
26
+ else
27
+ render_with_layout_without_sk(path, locals, *args, &block)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ register("ActionView::TemplateRenderer", "action_view", ActionView::Probe.new)
36
+ end
37
+ end
@@ -0,0 +1,55 @@
1
+ module Skylight::Core
2
+ module Probes
3
+ module ActiveModelSerializers
4
+ class Probe
5
+ def install
6
+ version = nil
7
+
8
+ # File moved location between version
9
+ %w(serializer serializers).each do |dir|
10
+ begin
11
+ require "active_model/#{dir}/version"
12
+ rescue LoadError
13
+ end
14
+ end
15
+
16
+ if defined?(::ActiveModel::Serializer::VERSION)
17
+ version = Gem::Version.new(::ActiveModel::Serializer::VERSION)
18
+ end
19
+
20
+ if !version || version < Gem::Version.new("0.5.0")
21
+ # Using $stderr here isn't great, but we don't have a logger accessible
22
+ $stderr.puts "[SKYLIGHT] [#{Skylight::Core::VERSION}] Instrumention is only available for " \
23
+ "ActiveModelSerializers version 0.5.0 and greater."
24
+ return
25
+ end
26
+
27
+ # We don't actually support the RCs correctly, requires
28
+ # a release after 0.10.0.rc3
29
+ if version >= Gem::Version.new("0.10.0.rc1")
30
+ # AS::N is built in to newer versions
31
+ return
32
+ end
33
+
34
+ # End users could override as_json without calling super, but it's likely safer
35
+ # than overriding serializable_array/hash/object.
36
+
37
+ [::ActiveModel::Serializer, ::ActiveModel::ArraySerializer].each do |klass|
38
+ klass.class_eval do
39
+ alias as_json_without_sk as_json
40
+ def as_json(*args)
41
+ payload = { serializer: self.class }
42
+ ActiveSupport::Notifications.instrument('render.active_model_serializers', payload) do
43
+ as_json_without_sk(*args)
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ end
50
+ end
51
+ end
52
+
53
+ register("ActiveModel::Serializer", "active_model/serializer", ActiveModelSerializers::Probe.new)
54
+ end
55
+ end
@@ -0,0 +1,37 @@
1
+ module Skylight::Core
2
+ module Probes
3
+ module Elasticsearch
4
+ class Probe
5
+ def install
6
+ ::Elasticsearch::Transport::Transport::Base.class_eval do
7
+ alias perform_request_without_sk perform_request
8
+ def perform_request(method, path, *args, &block)
9
+ ActiveSupport::Notifications.instrument "request.elasticsearch",
10
+ name: 'Request',
11
+ method: method,
12
+ path: path do
13
+
14
+
15
+ # Prevent HTTP-related probes from firing
16
+ Skylight::Core::Normalizers::Faraday::Request.disable do
17
+ disable_skylight_probe(:NetHTTP) do
18
+ disable_skylight_probe(:HTTPClient) do
19
+ perform_request_without_sk(method, path, *args, &block)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ def disable_skylight_probe(class_name, &block)
27
+ klass = Probes.const_get(class_name).const_get(:Probe) rescue nil
28
+ klass ? klass.disable(&block) : block.call
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ register("Elasticsearch", "elasticsearch", Elasticsearch::Probe.new)
36
+ end
37
+ end
@@ -0,0 +1,72 @@
1
+ require 'skylight/core/formatters/http'
2
+
3
+ module Skylight::Core
4
+ module Probes
5
+ module Excon
6
+
7
+ # Middleware for Excon that instruments requests
8
+ class Middleware < ::Excon::Middleware::Base
9
+
10
+ # This probably won't work since config isn't defined
11
+ include Util::Logging
12
+
13
+ def initialize(*)
14
+ @requests = {}
15
+ super
16
+ end
17
+
18
+ # TODO:
19
+ # - Consider whether a LIFO queue would be sufficient
20
+ # - Check that errors can't be called without a request
21
+
22
+ def request_call(datum)
23
+ begin_instrumentation(datum)
24
+ super
25
+ end
26
+
27
+ def response_call(datum)
28
+ super
29
+ ensure
30
+ end_instrumentation(datum)
31
+ end
32
+
33
+ def error_call(datum)
34
+ super
35
+ ensure
36
+ end_instrumentation(datum)
37
+ end
38
+
39
+ private
40
+
41
+ def begin_instrumentation(datum)
42
+ method = datum[:method].to_s
43
+ scheme = datum[:scheme]
44
+ host = datum[:host]
45
+ # TODO: Maybe don't show other default ports like 443
46
+ port = datum[:port] != 80 ? datum[:port] : nil
47
+ path = datum[:path]
48
+ query = datum[:query]
49
+
50
+ opts = Formatters::HTTP.build_opts(method, scheme, host, port, path, query)
51
+
52
+ @requests[datum.object_id] = Skylight::Core::Fanout.instrument(opts)
53
+ rescue Exception => e
54
+ error "failed to begin instrumentation for Excon; msg=%s", e.message
55
+ end
56
+
57
+ def end_instrumentation(datum)
58
+ if request = @requests.delete(datum.object_id)
59
+ meta = { }
60
+ if datum[:error].is_a?(Exception)
61
+ meta[:exception_object] = datum[:error]
62
+ end
63
+ Skylight::Core::Fanout.done(request, meta)
64
+ end
65
+ rescue Exception => e
66
+ error "failed to end instrumentation for Excon; msg=%s", e.message
67
+ end
68
+
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,26 @@
1
+ module Skylight::Core
2
+ module Probes
3
+ module Excon
4
+ # Probe for instrumenting Excon requests. Installs {Excon::Middleware} to achieve this.
5
+ class Probe
6
+ def install
7
+ if defined?(::Excon::Middleware)
8
+ # Don't require until installation since it depends on Excon being loaded
9
+ require 'skylight/core/probes/excon/middleware'
10
+
11
+ idx = ::Excon.defaults[:middlewares].index(::Excon::Middleware::Instrumentor)
12
+
13
+ # TODO: Handle possibility of idx being nil
14
+ ::Excon.defaults[:middlewares].insert(idx, Probes::Excon::Middleware)
15
+ else
16
+ # Using $stderr here isn't great, but we don't have a logger accessible
17
+ $stderr.puts "[SKYLIGHT] [#{Skylight::Core::VERSION}] The installed version of Excon doesn't " \
18
+ "support Middlewares. The Excon probe will be disabled."
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ register("Excon", "excon", Excon::Probe.new)
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ module Skylight::Core
2
+ module Probes
3
+ module Faraday
4
+ class Probe
5
+ def install
6
+ ::Faraday::Connection.class_eval do
7
+ alias initialize_without_sk initialize
8
+
9
+ def initialize(*args, &block)
10
+ initialize_without_sk(*args, &block)
11
+
12
+ @builder.insert 0, ::Faraday::Request::Instrumentation
13
+ end
14
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+
20
+ register("Faraday", "faraday", Faraday::Probe.new)
21
+ end
22
+ end
@@ -0,0 +1,80 @@
1
+ module Skylight::Core
2
+ module Probes
3
+ module Grape
4
+ class Probe
5
+ def install
6
+ version = Gem::Version.new(::Grape::VERSION)
7
+
8
+ if version > Gem::Version.new("0.12.1")
9
+ # AS::N is built in to newer versions
10
+ return
11
+ end
12
+
13
+ if version < Gem::Version.new("0.10.0")
14
+ # Using $stderr here isn't great, but we don't have a logger accessible
15
+ $stderr.puts "[SKYLIGHT] [#{Skylight::Core::VERSION}] The Grape probe only works with version 0.10.0+ " \
16
+ "and will be disabled."
17
+
18
+ return
19
+ end
20
+
21
+ ::Grape::Endpoint.class_eval do
22
+ alias initialize_without_sk initialize
23
+ def initialize(*args, &block)
24
+ initialize_without_sk(*args, &block)
25
+
26
+ # This solution of wrapping the block is effective, but potentially fragile.
27
+ # A cleaner solution would be to call the original initialize with the already
28
+ # modified block. However, Grape does some odd stuff with the block binding
29
+ # that makes this difficult to reason about.
30
+ if original_block = @block
31
+ @block = lambda do |endpoint_instance|
32
+ ActiveSupport::Notifications.instrument('endpoint_render.grape', endpoint: endpoint_instance) do
33
+ original_block.call(endpoint_instance)
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ alias run_without_sk run
40
+ def run(*args)
41
+ ActiveSupport::Notifications.instrument('endpoint_run.grape', endpoint: self) do
42
+ run_without_sk(*args)
43
+ end
44
+ end
45
+
46
+ alias run_filters_without_sk run_filters
47
+ def run_filters(filters)
48
+ # Unfortunately, the type isn't provided to the method so we have
49
+ # to try to guess it by looking at the contents. This is only reliable
50
+ # if the filters aren't empty.
51
+ if filters && !filters.empty?
52
+ type = case filters
53
+ when befores then :before
54
+ when before_validations then :before_validation
55
+ when after_validations then :after_validation
56
+ when afters then :after
57
+ else :other
58
+ end
59
+ else
60
+ type = :unknown
61
+ end
62
+
63
+ payload = {
64
+ endpoint: self,
65
+ filters: filters,
66
+ type: type
67
+ }
68
+
69
+ ActiveSupport::Notifications.instrument('endpoint_run_filters.grape', payload) do
70
+ run_filters_without_sk(filters)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ register("Grape::Endpoint", "grape/endpoint", Grape::Probe.new)
79
+ end
80
+ end
@@ -0,0 +1,46 @@
1
+ require 'skylight/core/formatters/http'
2
+
3
+ module Skylight::Core
4
+ module Probes
5
+ module HTTPClient
6
+ class Probe
7
+ DISABLED_KEY = :__skylight_httpclient_disabled
8
+
9
+ def self.disable
10
+ Thread.current[DISABLED_KEY] = true
11
+ yield
12
+ ensure
13
+ Thread.current[DISABLED_KEY] = false
14
+ end
15
+
16
+ def self.disabled?
17
+ !!Thread.current[DISABLED_KEY]
18
+ end
19
+
20
+ def install
21
+ ::HTTPClient.class_eval do
22
+ # HTTPClient has request methods on the class object itself,
23
+ # but the internally instantiate a client and perform the method
24
+ # on that, so this instance method override will cover both
25
+ # `HTTPClient.get(...)` and `HTTPClient.new.get(...)`
26
+
27
+ alias do_request_without_sk do_request
28
+ def do_request(method, uri, query, body, header, &block)
29
+ if Probes::HTTPClient::Probe.disabled?
30
+ return do_request_without_sk(method, uri, query, body, header, &block)
31
+ end
32
+
33
+ opts = Formatters::HTTP.build_opts(method, uri.scheme, uri.host, uri.port, uri.path, uri.query)
34
+
35
+ Skylight::Core::Fanout.instrument(opts) do
36
+ do_request_without_sk(method, uri, query, body, header, &block)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end # class Probe
42
+ end # module Probes::HTTPClient
43
+
44
+ register("HTTPClient", "httpclient", HTTPClient::Probe.new)
45
+ end
46
+ end
@@ -0,0 +1,58 @@
1
+ module Skylight::Core
2
+ module Probes
3
+ module Middleware
4
+ class Probe
5
+ def install
6
+ ActionDispatch::MiddlewareStack::Middleware.class_eval do
7
+ alias build_without_sk build
8
+ def build(*args)
9
+ sk_instrument_middleware(build_without_sk(*args))
10
+ end
11
+
12
+ def sk_instrument_middleware(middleware)
13
+ return middleware if middleware.is_a?(Skylight::Core::Middleware)
14
+
15
+ # Not sure how this would actually happen
16
+ return middleware if middleware.respond_to?(:call_without_sk)
17
+
18
+ # On Rails 3, ActionDispatch::Session::CookieStore is frozen, for one
19
+ return middleware if middleware.frozen?
20
+
21
+ middleware.instance_eval do
22
+ alias call_without_sk call
23
+ def call(*args, &block)
24
+ traces = Skylight::Core::Fanout.registered.map do |r|
25
+ r.instrumenter ? r.instrumenter.current_trace : nil
26
+ end.compact
27
+
28
+ return call_without_sk(*args, &block) if traces.empty?
29
+
30
+ begin
31
+ name = self.class.name || "Anonymous Middleware"
32
+
33
+ traces.each{|t| t.endpoint = name }
34
+
35
+ spans = Skylight::Core::Fanout.instrument(title: name, category: "rack.middleware")
36
+ resp = call_without_sk(*args, &block)
37
+
38
+ Skylight::Core::Middleware.with_after_close(resp) do
39
+ Skylight::Core::Fanout.done(spans)
40
+ end
41
+ rescue Exception => e
42
+ # FIXME: Log this?
43
+ Skylight::Core::Fanout.done(spans, exception_object: e)
44
+ raise
45
+ end
46
+ end
47
+ end
48
+
49
+ middleware
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ register("ActionDispatch::MiddlewareStack::Middleware", "actionpack/action_dispatch", Middleware::Probe.new)
57
+ end
58
+ end