skylight 4.2.3 → 5.3.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.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +420 -331
  3. data/CLA.md +1 -1
  4. data/CONTRIBUTING.md +2 -8
  5. data/ERRORS.md +3 -0
  6. data/LICENSE.md +7 -17
  7. data/README.md +1 -1
  8. data/ext/extconf.rb +61 -56
  9. data/ext/libskylight.yml +8 -6
  10. data/ext/skylight_native.c +26 -100
  11. data/lib/skylight/api.rb +32 -21
  12. data/lib/skylight/cli/doctor.rb +64 -65
  13. data/lib/skylight/cli/helpers.rb +19 -19
  14. data/lib/skylight/cli/merger.rb +142 -138
  15. data/lib/skylight/cli.rb +48 -46
  16. data/lib/skylight/config.rb +640 -201
  17. data/lib/skylight/data/cacert.pem +730 -1023
  18. data/lib/skylight/deprecation.rb +17 -0
  19. data/lib/skylight/errors.rb +26 -9
  20. data/lib/skylight/extensions/source_location.rb +291 -0
  21. data/lib/skylight/extensions.rb +95 -0
  22. data/lib/skylight/formatters/http.rb +18 -0
  23. data/lib/skylight/gc.rb +99 -0
  24. data/lib/skylight/helpers.rb +81 -36
  25. data/lib/skylight/instrumenter.rb +336 -18
  26. data/lib/skylight/middleware.rb +147 -1
  27. data/lib/skylight/native.rb +60 -12
  28. data/lib/skylight/native_ext_fetcher.rb +13 -14
  29. data/lib/skylight/normalizers/action_controller/process_action.rb +68 -0
  30. data/lib/skylight/normalizers/action_controller/send_file.rb +51 -0
  31. data/lib/skylight/normalizers/action_dispatch/process_middleware.rb +22 -0
  32. data/lib/skylight/normalizers/action_dispatch/route_set.rb +27 -0
  33. data/lib/skylight/normalizers/action_view/render_collection.rb +24 -0
  34. data/lib/skylight/normalizers/action_view/render_layout.rb +25 -0
  35. data/lib/skylight/normalizers/action_view/render_partial.rb +23 -0
  36. data/lib/skylight/normalizers/action_view/render_template.rb +23 -0
  37. data/lib/skylight/normalizers/active_job/perform.rb +87 -0
  38. data/lib/skylight/normalizers/active_model_serializers/render.rb +32 -0
  39. data/lib/skylight/normalizers/active_record/instantiation.rb +16 -0
  40. data/lib/skylight/normalizers/active_record/sql.rb +20 -0
  41. data/lib/skylight/normalizers/active_storage.rb +28 -0
  42. data/lib/skylight/normalizers/active_support/cache.rb +11 -0
  43. data/lib/skylight/normalizers/active_support/cache_clear.rb +16 -0
  44. data/lib/skylight/normalizers/active_support/cache_decrement.rb +16 -0
  45. data/lib/skylight/normalizers/active_support/cache_delete.rb +16 -0
  46. data/lib/skylight/normalizers/active_support/cache_exist.rb +16 -0
  47. data/lib/skylight/normalizers/active_support/cache_fetch_hit.rb +16 -0
  48. data/lib/skylight/normalizers/active_support/cache_generate.rb +16 -0
  49. data/lib/skylight/normalizers/active_support/cache_increment.rb +16 -0
  50. data/lib/skylight/normalizers/active_support/cache_read.rb +16 -0
  51. data/lib/skylight/normalizers/active_support/cache_read_multi.rb +16 -0
  52. data/lib/skylight/normalizers/active_support/cache_write.rb +16 -0
  53. data/lib/skylight/normalizers/coach/handler_finish.rb +44 -0
  54. data/lib/skylight/normalizers/coach/middleware_finish.rb +33 -0
  55. data/lib/skylight/normalizers/couch_potato/query.rb +20 -0
  56. data/lib/skylight/normalizers/data_mapper/sql.rb +12 -0
  57. data/lib/skylight/normalizers/default.rb +24 -0
  58. data/lib/skylight/normalizers/elasticsearch/request.rb +20 -0
  59. data/lib/skylight/normalizers/faraday/request.rb +38 -0
  60. data/lib/skylight/normalizers/grape/endpoint.rb +28 -0
  61. data/lib/skylight/normalizers/grape/endpoint_render.rb +25 -0
  62. data/lib/skylight/normalizers/grape/endpoint_run.rb +39 -0
  63. data/lib/skylight/normalizers/grape/endpoint_run_filters.rb +20 -0
  64. data/lib/skylight/normalizers/grape/format_response.rb +20 -0
  65. data/lib/skylight/normalizers/graphiti/render.rb +22 -0
  66. data/lib/skylight/normalizers/graphiti/resolve.rb +31 -0
  67. data/lib/skylight/normalizers/graphql/base.rb +127 -0
  68. data/lib/skylight/normalizers/render.rb +79 -0
  69. data/lib/skylight/normalizers/sequel/sql.rb +12 -0
  70. data/lib/skylight/normalizers/shrine.rb +32 -0
  71. data/lib/skylight/normalizers/sql.rb +41 -0
  72. data/lib/skylight/normalizers.rb +157 -0
  73. data/lib/skylight/probes/action_controller.rb +52 -0
  74. data/lib/skylight/probes/action_dispatch/request_id.rb +33 -0
  75. data/lib/skylight/probes/action_dispatch/routing/route_set.rb +30 -0
  76. data/lib/skylight/probes/action_dispatch.rb +2 -0
  77. data/lib/skylight/probes/action_view.rb +42 -0
  78. data/lib/skylight/probes/active_job.rb +27 -0
  79. data/lib/skylight/probes/active_job_enqueue.rb +35 -0
  80. data/lib/skylight/probes/active_model_serializers.rb +50 -0
  81. data/lib/skylight/probes/active_record_async.rb +96 -0
  82. data/lib/skylight/probes/delayed_job.rb +144 -0
  83. data/lib/skylight/probes/elasticsearch.rb +36 -0
  84. data/lib/skylight/probes/excon/middleware.rb +65 -0
  85. data/lib/skylight/probes/excon.rb +25 -0
  86. data/lib/skylight/probes/faraday.rb +23 -0
  87. data/lib/skylight/probes/graphql.rb +38 -0
  88. data/lib/skylight/probes/httpclient.rb +44 -0
  89. data/lib/skylight/probes/middleware.rb +135 -0
  90. data/lib/skylight/probes/mongo.rb +156 -0
  91. data/lib/skylight/probes/mongoid.rb +13 -0
  92. data/lib/skylight/probes/net_http.rb +54 -0
  93. data/lib/skylight/probes/rack_builder.rb +37 -0
  94. data/lib/skylight/probes/redis.rb +51 -0
  95. data/lib/skylight/probes/sequel.rb +29 -0
  96. data/lib/skylight/probes/sinatra.rb +66 -0
  97. data/lib/skylight/probes/sinatra_add_middleware.rb +10 -10
  98. data/lib/skylight/probes/tilt.rb +25 -0
  99. data/lib/skylight/probes.rb +173 -0
  100. data/lib/skylight/railtie.rb +166 -28
  101. data/lib/skylight/sidekiq.rb +47 -0
  102. data/lib/skylight/sinatra.rb +1 -1
  103. data/lib/skylight/subscriber.rb +130 -0
  104. data/lib/skylight/test.rb +147 -0
  105. data/lib/skylight/trace.rb +325 -22
  106. data/lib/skylight/user_config.rb +58 -0
  107. data/lib/skylight/util/allocation_free.rb +26 -0
  108. data/lib/skylight/util/clock.rb +57 -0
  109. data/lib/skylight/util/component.rb +22 -22
  110. data/lib/skylight/util/deploy.rb +19 -24
  111. data/lib/skylight/util/gzip.rb +20 -0
  112. data/lib/skylight/util/http.rb +106 -113
  113. data/lib/skylight/util/instrumenter_method.rb +26 -0
  114. data/lib/skylight/util/logging.rb +136 -0
  115. data/lib/skylight/util/lru_cache.rb +36 -0
  116. data/lib/skylight/util/platform.rb +3 -7
  117. data/lib/skylight/util/ssl.rb +1 -25
  118. data/lib/skylight/util.rb +12 -0
  119. data/lib/skylight/vendor/cli/thor/rake_compat.rb +1 -1
  120. data/lib/skylight/version.rb +5 -1
  121. data/lib/skylight/vm/gc.rb +60 -0
  122. data/lib/skylight.rb +201 -14
  123. metadata +134 -18
@@ -1,14 +1,9 @@
1
- require "skylight/core/railtie"
1
+ require "skylight"
2
+ require "rails"
2
3
 
3
4
  module Skylight
5
+ # @api private
4
6
  class Railtie < Rails::Railtie
5
- include Skylight::Core::Railtie
6
-
7
- # rubocop:disable Style/SingleLineMethods, Layout/EmptyLineBetweenDefs
8
- def self.config_class; Skylight::Config end
9
- def self.middleware_class; Skylight::Middleware end
10
- # rubocop:enable Style/SingleLineMethods, Layout/EmptyLineBetweenDefs
11
-
12
7
  config.skylight = ActiveSupport::OrderedOptions.new
13
8
 
14
9
  # The environments in which skylight should be enabled
@@ -21,7 +16,15 @@ module Skylight
21
16
  # net_http, action_controller, action_dispatch, action_view, and middleware are on by default
22
17
  # See https://www.skylight.io/support/getting-more-from-skylight#available-instrumentation-options
23
18
  # for a full list.
24
- config.skylight.probes = %w[net_http action_controller action_dispatch action_view middleware active_job_enqueue]
19
+ config.skylight.probes = %w[
20
+ net_http
21
+ action_controller
22
+ action_dispatch
23
+ action_view
24
+ middleware
25
+ active_job_enqueue
26
+ active_record_async
27
+ ]
25
28
 
26
29
  # The position in the middleware stack to place Skylight
27
30
  # Default is first, but can be `{ after: Middleware::Name }` or `{ before: Middleware::Name }`
@@ -33,34 +36,169 @@ module Skylight
33
36
 
34
37
  private
35
38
 
36
- def activate?(sk_config)
37
- return false unless super && sk_config
38
- show_worker_activation_warning(sk_config)
39
- true
40
- end
39
+ # We must have an opt-in signal
40
+ def show_worker_activation_warning(sk_config)
41
+ reasons = []
42
+ reasons << "the 'active_job' probe is enabled" if sk_rails_config.probes.include?("active_job")
43
+ reasons << "the 'delayed_job' probe is enabled" if sk_rails_config.probes.include?("delayed_job")
44
+ reasons << "SKYLIGHT_ENABLE_SIDEKIQ is set" if sk_config.enable_sidekiq?
45
+
46
+ return if reasons.empty?
47
+
48
+ sk_config.logger.warn("Activating Skylight for Background Jobs because #{reasons.to_sentence}")
49
+ end
50
+
51
+ def log_prefix
52
+ "[SKYLIGHT] [#{Skylight::VERSION}]"
53
+ end
54
+
55
+ def development_warning
56
+ "#{log_prefix} Running Skylight in development mode. No data will be reported until you deploy your app.\n" \
57
+ "(To disable this message for all local apps, run `skylight disable_dev_warning`.)"
58
+ end
41
59
 
42
- # We must have an opt-in signal
43
- def show_worker_activation_warning(sk_config)
44
- reasons = []
45
- reasons << "the 'active_job' probe is enabled" if sk_rails_config.probes.include?("active_job")
46
- reasons << "the 'delayed_job' probe is enabled" if sk_rails_config.probes.include?("delayed_job")
47
- reasons << "SKYLIGHT_ENABLE_SIDEKIQ is set" if sk_config.enable_sidekiq?
60
+ def run_initializer(app)
61
+ # Load probes even when agent is inactive to catch probe related bugs sooner
62
+ load_probes
48
63
 
49
- return if reasons.empty?
64
+ config = load_skylight_config(app)
50
65
 
51
- sk_config.logger.warn("Activating Skylight for Background Jobs because #{reasons.to_sentence}")
66
+ if activate?(config)
67
+ if config
68
+ if Skylight.start!(config)
69
+ set_middleware_position(app, config)
70
+ Rails.logger.info "#{log_prefix} Skylight agent enabled"
71
+ else
72
+ Rails.logger.info "#{log_prefix} Unable to start, see the Skylight logs for more details"
73
+ end
74
+ end
75
+ elsif Rails.env.development?
76
+ log_warning config, development_warning unless config.user_config.disable_dev_warning?
77
+ elsif !Rails.env.test?
78
+ unless config.user_config.disable_env_warning?
79
+ log_warning config,
80
+ "#{log_prefix} You are running in the #{Rails.env} environment but haven't added it " \
81
+ "to config.skylight.environments, so no data will be sent to Skylight servers."
82
+ end
52
83
  end
84
+ rescue Skylight::ConfigError => e
85
+ Rails.logger.error "#{log_prefix} #{e.message}; disabling Skylight agent"
86
+ end
53
87
 
54
- def development_warning
55
- super + "\n(To disable this message for all local apps, run `skylight disable_dev_warning`.)"
88
+ def log_warning(config, msg)
89
+ config ? config.alert_logger.warn(msg) : Rails.logger.warn(msg)
90
+ end
91
+
92
+ def existent_paths(paths)
93
+ paths.respond_to?(:existent) ? paths.existent : paths.select { |f| File.exist?(f) }
94
+ end
95
+
96
+ def load_skylight_config(app)
97
+ path = config_path(app)
98
+ path = nil unless File.exist?(path)
99
+
100
+ unless (tmp = app.config.paths["tmp"].first)
101
+ Rails.logger.error "#{log_prefix} tmp directory missing from rails configuration"
102
+ return nil
56
103
  end
57
104
 
58
- def load_skylight_config(app)
59
- super.tap do |sk_config|
60
- if sk_config && sk_config[:report_rails_env]
61
- sk_config[:env] ||= Rails.env.to_s
105
+ config = Config.load(file: path, priority_key: Rails.env.to_s)
106
+ config[:root] = Rails.root
107
+
108
+ configure_logging(config, app)
109
+
110
+ config[:"daemon.sockdir_path"] ||= tmp
111
+ config[:"normalizers.render.view_paths"] = existent_paths(app.config.paths["app/views"]) + [Rails.root.to_s]
112
+
113
+ config[:env] ||= Rails.env.to_s if config[:report_rails_env]
114
+
115
+ config
116
+ end
117
+
118
+ def configure_logging(config, app)
119
+ if (logger = sk_rails_config(app).logger)
120
+ config.logger = logger
121
+ else
122
+ # Configure the log file destination
123
+ if (log_file = sk_rails_config(app).log_file)
124
+ config["log_file"] = log_file
125
+ end
126
+
127
+ if (native_log_file = sk_rails_config(app).native_log_file)
128
+ config["native_log_file"] = native_log_file
129
+ end
130
+
131
+ config["log_file"] = File.join(Rails.root, "log/skylight.log") if !config.key?("log_file") && !config.on_heroku?
132
+
133
+ # Configure the log level
134
+ if (level = sk_rails_config(app).log_level)
135
+ config["log_level"] = level
136
+ elsif !config.key?("log_level")
137
+ if (level = app.config.log_level)
138
+ config["log_level"] = level
62
139
  end
63
140
  end
64
141
  end
142
+ end
143
+
144
+ def config_path(app)
145
+ File.expand_path(sk_rails_config.config_path, app.root)
146
+ end
147
+
148
+ def environments
149
+ Array(sk_rails_config.environments).map { |e| e&.to_s }.compact
150
+ end
151
+
152
+ def activate?(sk_config)
153
+ return false unless sk_config
154
+
155
+ key = "SKYLIGHT_ENABLED"
156
+ activate = ENV.key?(key) ? ENV[key] !~ /^false$/i : environments.include?(Rails.env.to_s)
157
+
158
+ show_worker_activation_warning(sk_config) if activate
159
+
160
+ activate
161
+ end
162
+
163
+ def load_probes
164
+ probes = sk_rails_config.probes || []
165
+ Skylight::Probes.probe(*probes)
166
+ end
167
+
168
+ def middleware_position
169
+ if sk_rails_config.middleware_position.is_a?(Hash)
170
+ sk_rails_config.middleware_position.symbolize_keys
171
+ else
172
+ sk_rails_config.middleware_position
173
+ end
174
+ end
175
+
176
+ def insert_middleware(app, config)
177
+ if middleware_position.key?(:after)
178
+ app.middleware.insert_after(middleware_position[:after], Skylight::Middleware, config: config)
179
+ elsif middleware_position.key?(:before)
180
+ app.middleware.insert_before(middleware_position[:before], Skylight::Middleware, config: config)
181
+ else
182
+ raise "The middleware position you have set is invalid. Please be sure " \
183
+ "`config.skylight.middleware_position` is set up correctly."
184
+ end
185
+ end
186
+
187
+ def set_middleware_position(app, config)
188
+ if middleware_position.is_a?(Integer)
189
+ app.middleware.insert middleware_position, Skylight::Middleware, config: config
190
+ elsif middleware_position.is_a?(Hash) && middleware_position.keys.count == 1
191
+ insert_middleware(app, config)
192
+ elsif middleware_position.nil?
193
+ app.middleware.insert 0, Skylight::Middleware, config: config
194
+ else
195
+ raise "The middleware position you have set is invalid. Please be sure " \
196
+ "`config.skylight.middleware_position` is set up correctly."
197
+ end
198
+ end
199
+
200
+ def sk_rails_config(target = self)
201
+ target.config.skylight
202
+ end
65
203
  end
66
204
  end
@@ -0,0 +1,47 @@
1
+ module Skylight
2
+ module Sidekiq
3
+ def self.add_middleware
4
+ unless defined?(::Sidekiq)
5
+ Skylight.warn "Skylight for Sidekiq is active, but Sidekiq is not defined."
6
+ return
7
+ end
8
+
9
+ ::Sidekiq.configure_server do |sidekiq_config|
10
+ Skylight.debug "Adding Sidekiq Middleware"
11
+
12
+ sidekiq_config.server_middleware do |chain|
13
+ # Put it at the front
14
+ chain.prepend ServerMiddleware
15
+ end
16
+ end
17
+ end
18
+
19
+ class ServerMiddleware
20
+ include Util::Logging
21
+
22
+ def call(worker, job, queue)
23
+ t { "Sidekiq middleware beginning trace" }
24
+ title = job["wrapped"] || job["class"]
25
+
26
+ # TODO: Using hints here would be ideal but requires further refactoring
27
+ meta =
28
+ if (source_location = worker.method(:perform).source_location)
29
+ { source_file: source_location[0], source_line: source_location[1] }
30
+ end
31
+
32
+ Skylight.trace(title, "app.sidekiq.worker", title, meta: meta, segment: queue, component: :worker) do |trace|
33
+ yield
34
+ rescue Exception # includes Sidekiq::Shutdown
35
+ trace.segment = "error" if trace
36
+ raise
37
+ end
38
+ end
39
+ end
40
+
41
+ ActiveSupport::Notifications.subscribe(
42
+ "started_instrumenter.skylight"
43
+ ) do |_name, _started, _finished, _unique_id, payload|
44
+ add_middleware if payload[:instrumenter].config.enable_sidekiq?
45
+ end
46
+ end
47
+ end
@@ -1,2 +1,2 @@
1
1
  require "skylight"
2
- Skylight.probe(:sinatra_add_middleware, :sinatra, :tilt, :sequel)
2
+ Skylight.probe(:sinatra_add_middleware, :sinatra, :tilt, :sequel, :rack_builder)
@@ -0,0 +1,130 @@
1
+ module Skylight
2
+ # @api private
3
+ class Subscriber
4
+ include Util::Logging
5
+
6
+ attr_reader :config, :normalizers
7
+
8
+ def initialize(config, instrumenter)
9
+ @config = config
10
+ @normalizers = Normalizers.build(config)
11
+ @instrumenter = instrumenter
12
+ @subscribers = []
13
+ end
14
+
15
+ def register!
16
+ unregister!
17
+ @normalizers.each_key { |key| @subscribers << ActiveSupport::Notifications.subscribe(key, self) }
18
+ end
19
+
20
+ def unregister!
21
+ ActiveSupport::Notifications.unsubscribe @subscribers.shift until @subscribers.empty?
22
+ end
23
+
24
+ #
25
+ #
26
+ # ===== ActiveSupport::Notifications API
27
+ #
28
+ #
29
+
30
+ class Notification
31
+ attr_reader :name, :span
32
+
33
+ def initialize(name, span)
34
+ @name = name
35
+ @span = span
36
+ end
37
+ end
38
+
39
+ # cargo-culted from Rails's ConnectionAdapter
40
+ EXCEPTION_NEVER = { Exception => :never }.freeze # :nodoc:
41
+ EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze # :nodoc:
42
+ private_constant :EXCEPTION_NEVER, :EXCEPTION_IMMEDIATE
43
+ def with_trace(trace, &block) # :nodoc:
44
+ Thread.handle_interrupt(EXCEPTION_NEVER) do
45
+ previous_trace = @trace
46
+ @trace = trace
47
+ Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block)
48
+ ensure
49
+ @trace = previous_trace
50
+ end
51
+ end
52
+
53
+ def start(name, _id, payload)
54
+ return if @instrumenter.disabled?
55
+ return unless (trace = current_trace)
56
+
57
+ _start(trace, name, payload)
58
+ end
59
+
60
+ def finish(name, _id, payload)
61
+ return if @instrumenter.disabled?
62
+ return unless (trace = current_trace)
63
+
64
+ while (curr = trace.notifications.pop)
65
+ next unless curr.name == name
66
+
67
+ meta = {}
68
+ meta[:exception] = payload[:exception] if payload[:exception]
69
+ meta[:exception_object] = payload[:exception_object] if payload[:exception_object]
70
+ trace.done(curr.span, meta) if curr.span
71
+ normalize_after(trace, curr.span, name, payload)
72
+ return
73
+ end
74
+ rescue Exception => e
75
+ error "Subscriber#finish error; msg=%s", e.message
76
+ debug "trace=%s", trace.inspect
77
+ debug "in: name=%s", name.inspect
78
+ debug "in: payload=%s", payload.inspect
79
+ t { e.backtrace.join("\n") }
80
+ nil
81
+ end
82
+
83
+ def publish(name, *args)
84
+ # Ignored for now because nothing in rails uses it
85
+ end
86
+
87
+ def publish_event(event)
88
+ # Ignored for now because only ActiveRecord::FutureResult uses it and we handle that with probes
89
+ end
90
+
91
+ private
92
+
93
+ def current_trace
94
+ @trace || @instrumenter.current_trace
95
+ end
96
+
97
+ def normalize(*args)
98
+ @normalizers.normalize(*args)
99
+ end
100
+
101
+ def normalize_after(*args)
102
+ @normalizers.normalize_after(*args)
103
+ end
104
+
105
+ def _start(trace, name, payload)
106
+ result = normalize(trace, name, payload)
107
+
108
+ unless result == :skip
109
+ case result.size
110
+ when 3, 4
111
+ cat, title, desc, meta = result
112
+ else
113
+ raise "Invalid normalizer result: #{result.inspect}"
114
+ end
115
+
116
+ span = trace.instrument(cat, title, desc, meta)
117
+ end
118
+ rescue Exception => e
119
+ error "Subscriber#start error; msg=%s", e.message
120
+ debug "trace=%s", trace.inspect
121
+ debug "in: name=%s", name.inspect
122
+ debug "in: payload=%s", payload.inspect
123
+ debug "out: cat=%s, title=%s, desc=%s", cat.inspect, name.inspect, desc.inspect
124
+ t { e.backtrace.join("\n") }
125
+ nil
126
+ ensure
127
+ trace.notifications << Notification.new(name, span)
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,147 @@
1
+ module Skylight
2
+ module Test
3
+ module Mocking
4
+ def mock!(config_opts = {}, &callback)
5
+ config_opts[:mock_submission] ||= callback || proc {}
6
+
7
+ config_class =
8
+ Class.new(Config) do
9
+ def validate_with_server
10
+ true
11
+ end
12
+ end
13
+
14
+ config = config_class.load(config_opts)
15
+ config[:authentication] ||= "zomg"
16
+
17
+ class_eval do
18
+ unless const_defined?(:OriginalInstrumenter)
19
+ const_set :OriginalInstrumenter, Instrumenter
20
+ remove_const :Instrumenter
21
+ const_set(
22
+ :Instrumenter,
23
+ Class.new(OriginalInstrumenter) do
24
+ def self.name
25
+ "Mocked Instrumenter"
26
+ end
27
+
28
+ def native_start
29
+ true
30
+ end
31
+
32
+ def native_submit_trace(trace)
33
+ config[:mock_submission].call(trace)
34
+ end
35
+
36
+ def native_stop; end
37
+ end
38
+ )
39
+
40
+ const_set :OriginalTrace, Trace
41
+ remove_const :Trace
42
+ const_set(
43
+ :Trace,
44
+ Class.new(OriginalTrace) do
45
+ def self.native_new(start, _uuid, endpoint, meta)
46
+ inst = super
47
+ inst.instance_variable_set(:@start, start)
48
+ inst.instance_variable_set(:@endpoint, endpoint)
49
+ inst.instance_variable_set(:@starting_endpoint, endpoint)
50
+ inst.instance_variable_set(:@meta, meta)
51
+ inst
52
+ end
53
+
54
+ attr_reader :endpoint, :starting_endpoint, :meta
55
+
56
+ def mock_spans
57
+ @mock_spans ||= []
58
+ end
59
+
60
+ def filter_spans
61
+ if block_given?
62
+ mock_spans.select { |span| yield span }
63
+ else
64
+ mock_spans.reject { |span| span[:cat] == "noise.gc" }
65
+ end
66
+ end
67
+
68
+ def native_get_uuid
69
+ @uuid
70
+ end
71
+
72
+ def uuid=(value)
73
+ @uuid = value
74
+ end
75
+
76
+ def native_get_started_at
77
+ @start
78
+ end
79
+
80
+ def native_set_endpoint(endpoint)
81
+ @endpoint = endpoint
82
+ end
83
+
84
+ def native_set_component(component)
85
+ @component = component
86
+ end
87
+
88
+ def native_start_span(time, cat)
89
+ span = { start: time, cat: cat }
90
+ mock_spans << span
91
+
92
+ # Return integer like the native method does
93
+ mock_spans.index(span)
94
+ end
95
+
96
+ def native_span_set_title(span, title)
97
+ mock_spans[span][:title] = title
98
+ end
99
+
100
+ def native_span_set_description(span, desc)
101
+ mock_spans[span][:desc] = desc
102
+ end
103
+
104
+ def native_span_set_meta(span, meta)
105
+ mock_spans[span][:meta] = meta
106
+ end
107
+
108
+ def native_span_started(span); end
109
+
110
+ def native_span_set_exception(span, exception_object, exception)
111
+ mock_spans[span][:exception_object] = exception_object
112
+ mock_spans[span][:exception] = exception
113
+ end
114
+
115
+ def native_stop_span(span, time)
116
+ span = mock_spans[span]
117
+ span[:duration] = time - span[:start]
118
+ nil
119
+ end
120
+
121
+ def native_use_pruning
122
+ @using_native_pruning = true
123
+ end
124
+ end
125
+ )
126
+ end
127
+ end
128
+
129
+ start!(config)
130
+ end
131
+
132
+ def unmock!
133
+ if const_defined?(:OriginalInstrumenter)
134
+ class_eval do
135
+ remove_const :Instrumenter
136
+ const_set :Instrumenter, OriginalInstrumenter
137
+ remove_const :OriginalInstrumenter
138
+
139
+ remove_const :Trace
140
+ const_set :Trace, OriginalTrace
141
+ remove_const :OriginalTrace
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end