skylight 5.0.0.beta → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -6
- data/CONTRIBUTING.md +1 -1
- data/ext/extconf.rb +2 -2
- data/ext/libskylight.yml +7 -5
- data/lib/skylight.rb +9 -2
- data/lib/skylight/api.rb +3 -0
- data/lib/skylight/cli/doctor.rb +11 -13
- data/lib/skylight/config.rb +25 -32
- data/lib/skylight/deprecation.rb +3 -1
- data/lib/skylight/errors.rb +4 -4
- data/lib/skylight/extensions.rb +8 -0
- data/lib/skylight/extensions/source_location.rb +123 -81
- data/lib/skylight/formatters/http.rb +2 -1
- data/lib/skylight/helpers.rb +44 -35
- data/lib/skylight/instrumenter.rb +3 -2
- data/lib/skylight/middleware.rb +4 -4
- data/lib/skylight/native.rb +1 -1
- data/lib/skylight/native_ext_fetcher.rb +2 -2
- data/lib/skylight/normalizers.rb +6 -4
- data/lib/skylight/normalizers/action_controller/process_action.rb +1 -1
- data/lib/skylight/normalizers/action_dispatch/route_set.rb +1 -1
- data/lib/skylight/normalizers/active_job/perform.rb +5 -0
- data/lib/skylight/normalizers/graphql/base.rb +1 -0
- data/lib/skylight/normalizers/render.rb +1 -1
- data/lib/skylight/normalizers/shrine.rb +34 -0
- data/lib/skylight/normalizers/sql.rb +3 -2
- data/lib/skylight/probes.rb +38 -10
- data/lib/skylight/probes/active_job.rb +4 -6
- data/lib/skylight/probes/active_job_enqueue.rb +18 -14
- data/lib/skylight/probes/active_model_serializers.rb +2 -6
- data/lib/skylight/probes/delayed_job.rb +112 -25
- data/lib/skylight/probes/elasticsearch.rb +1 -1
- data/lib/skylight/probes/excon/middleware.rb +4 -4
- data/lib/skylight/probes/middleware.rb +2 -1
- data/lib/skylight/probes/mongo.rb +2 -1
- data/lib/skylight/probes/net_http.rb +0 -1
- data/lib/skylight/probes/redis.rb +6 -3
- data/lib/skylight/railtie.rb +1 -1
- data/lib/skylight/sidekiq.rb +12 -7
- data/lib/skylight/subscriber.rb +1 -1
- data/lib/skylight/trace.rb +10 -4
- data/lib/skylight/util/deploy.rb +3 -6
- data/lib/skylight/util/instrumenter_method.rb +11 -11
- data/lib/skylight/util/logging.rb +6 -6
- data/lib/skylight/util/lru_cache.rb +1 -3
- data/lib/skylight/util/platform.rb +1 -1
- data/lib/skylight/version.rb +1 -1
- metadata +27 -13
- data/lib/skylight/fanout.rb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 66c646baeaeddece5a75140908f8c7d1aec447945903ff5681c1e4de58839e79
|
4
|
+
data.tar.gz: a3fe10a6b05963038f40f4b82d77cd545c42fa546d63cd440a21eb3839280b71
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ca6b42cfa51a39f1cccfc8f169a9bdc1560ec9774423c1ca335ec6fc26bd70d47f40c62d9e645ea626b00626e962a9ef3dbda49c693de0a56fef10bc04ab1d76
|
7
|
+
data.tar.gz: ccb18fe7cc713c03544db2a0d1dc5c42d86fe5ee14c0db11e208884203b7141cd39032bdb75f5c7eaf341b1ded6e9c761e8529e1bf50d1c499b1bab85a545fcd
|
data/CHANGELOG.md
CHANGED
@@ -1,18 +1,38 @@
|
|
1
|
-
## 5.0.0
|
2
|
-
|
3
|
-
* [
|
4
|
-
* [
|
5
|
-
* [
|
1
|
+
## 5.0.0 (March 5, 2021)
|
2
|
+
|
3
|
+
* [FEATURE] Add normalizer for Shrine events (thanks @janko!)
|
4
|
+
* [FEATURE] Source Locations detection and reporting is now enabled by default (can be disabled with `SKYLIGHT_ENABLE_SOURCE_LOCATIONS=false`)
|
5
|
+
* [FEATURE] Configuration for the Source Locations caches via `SYLIGHT_SOURCE_LOCATION_CACHE_SIZE`
|
6
|
+
|
7
|
+
* [IMPROVEMENT] Improve keyword argument handling in Skylight::Helpers (thanks @lukebooth!)
|
8
|
+
* [IMPROVEMENT] Replace a Kernel.puts with Skylight.log (thanks @johnnyshields!)
|
9
|
+
* [IMPROVEMENT] Various updates to the SQL lexer
|
10
|
+
* [IMPROVEMENT] Reduce volume of log messages sent to the native logger in debug level
|
11
|
+
* [IMPROVEMENT] Optimizations for the Source Locations extension
|
12
|
+
* [IMPROVEMENT] Improved Delayed::Job probe
|
6
13
|
* [IMPROVEMENT] Maintain method visibility when instrumenting with `instrument_method`
|
7
14
|
* [IMPROVEMENT] Update probes to use `Module#prepend` where possible
|
8
15
|
* [IMPROVEMENT] New tokio-based skylightd
|
9
16
|
* [IMPROVEMENT] Support `render_layout` notifications in Rails 6.1
|
10
17
|
* [IMPROVEMENT] Support `ActionMailer::MailDeliveryJob` in Rails 6.1
|
11
|
-
* [IMPROVEMENT] Better logging config
|
18
|
+
* [IMPROVEMENT] Better logging config. `SKYLIGHT_NATIVE_LOG_LEVEL` now defaults to `warn`.
|
19
|
+
|
20
|
+
* [BREAKING] Rename `environment` keyword argument to `priority_key`. Note `env` has not changed.
|
21
|
+
* [BREAKING] Drop support for Ruby 2.4
|
22
|
+
* [BREAKING] Merge skylight-core into skylight. All classes previously namespaced under `Skylight::Core` have been moved to `Skylight`.
|
23
|
+
* [BREAKING] Remove `Skylight::Util::Inflector`
|
24
|
+
* [BREAKING] Drop support for Rails 4
|
25
|
+
* [BREAKING] Drop support for Ruby 2.3
|
26
|
+
|
27
|
+
* [BUGFIX] Fix issue with missing metadata in MongoDB probe
|
28
|
+
* [BUGFIX] Resolve an inability to parse certain SQL queries containing arrays
|
12
29
|
* [BUGFIX] Allow multiple probes to be registered under the same key
|
13
30
|
* [BUGFIX] Do not refer to Redis constant until the probe is installed
|
14
31
|
* [BUGFIX] Fix nested calls to `Normalizers::Faraday::Request.disable`
|
15
32
|
|
33
|
+
## 4.3.2 (December 14, 2020)
|
34
|
+
* [BUGFIX] Backport an ActionView fix from Skylight 5 (makes Skylight 4 compatible with Rails 6.1)
|
35
|
+
|
16
36
|
## 4.3.1 (June 24, 2020)
|
17
37
|
* [BUGFIX] Fix an issue in which `Mime::NullType` would result in an exception
|
18
38
|
|
data/CONTRIBUTING.md
CHANGED
@@ -16,7 +16,7 @@ If you prefer to run tests in your own environment, you may do so as follows:
|
|
16
16
|
|
17
17
|
```shell
|
18
18
|
# Select a gemfile and bundle install
|
19
|
-
export BUNDLE_GEMFILE=$PWD/gemfiles/
|
19
|
+
export BUNDLE_GEMFILE=$PWD/gemfiles/rails-5.2.x/Gemfile
|
20
20
|
bundle install
|
21
21
|
# Run the test suite (takes 5-10 minutes)
|
22
22
|
bundle exec rspec
|
data/ext/extconf.rb
CHANGED
@@ -42,7 +42,7 @@ SKYLIGHT_CHECKSUM = ENV["SKYLIGHT_CHECKSUM"]
|
|
42
42
|
SKYLIGHT_EXT_STRICT = ENV.key?("SKYLIGHT_EXT_STRICT") && ENV["SKYLIGHT_EXT_STRICT"] =~ /^true$/i
|
43
43
|
|
44
44
|
# Setup logger
|
45
|
-
LOG = Logger.new(MultiIO.new(
|
45
|
+
LOG = Logger.new(MultiIO.new($stdout, File.open(SKYLIGHT_INSTALL_LOG, "a")))
|
46
46
|
|
47
47
|
# Handles terminating in the case of a failure. If we have a bug, we do not
|
48
48
|
# want to break our customer's deploy, but extconf.rb requires a Makefile to be
|
@@ -69,7 +69,7 @@ end
|
|
69
69
|
if Platform::OS == "darwin"
|
70
70
|
# If the user installs Xcode-only, they have to approve the
|
71
71
|
# license or no "xc*" tool will work.
|
72
|
-
if `/usr/bin/xcrun clang 2>&1` =~ /license/ && !$CHILD_STATUS.success?
|
72
|
+
if `/usr/bin/xcrun clang 2>&1` =~ /license/ && !$CHILD_STATUS.success? # rubocop:disable Style/SoleNestedConditional
|
73
73
|
fail <<~MESSAGE
|
74
74
|
You have not agreed to the Xcode license and so we are unable to build the native agent.
|
75
75
|
To resolve this, you can agree to the license by opening Xcode.app or running:
|
data/ext/libskylight.yml
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
|
1
|
+
# commit: ea59cc7bdbbee0f69d1cf7e69827974a9ea67645
|
2
|
+
---
|
3
|
+
version: "5.0.0-488d432"
|
2
4
|
checksums:
|
3
|
-
x86-linux: "
|
4
|
-
x86_64-linux: "
|
5
|
-
x86_64-linux-musl: "
|
6
|
-
x86_64-darwin: "
|
5
|
+
x86-linux: "3c16b6db1508f35720258551783fbcd30fd231638bad316ea76748d659838399"
|
6
|
+
x86_64-linux: "94383aa3359c3f2e9c0e3c16ee263d46c5673dd255f8842e6acadf5ec3131c06"
|
7
|
+
x86_64-linux-musl: "d2e2e2e61c321315f9bcaa157426f33aef8ffc2330ba46b2cdcbd9442e65f728"
|
8
|
+
x86_64-darwin: "bcc925d0bcbae83a484f35dbc9729dcf262e1f3a8c29b8d387d0f58ad8f3afa3"
|
data/lib/skylight.rb
CHANGED
@@ -101,7 +101,7 @@ module Skylight
|
|
101
101
|
message: e.message, klass: e.class)]
|
102
102
|
end
|
103
103
|
|
104
|
-
if config
|
104
|
+
if config.respond_to?("log_#{level}") && config.respond_to?(:log_trace)
|
105
105
|
config.send("log_#{level}", message)
|
106
106
|
config.log_trace e.backtrace.join("\n")
|
107
107
|
else
|
@@ -194,10 +194,17 @@ module Skylight
|
|
194
194
|
opts = {}
|
195
195
|
end
|
196
196
|
|
197
|
+
# NOTE: unless we have `:internal` (indicating a built-in Skylight instrument block),
|
198
|
+
# or we already have a `source_file` or `source_line` (probably set by `instrument_method`),
|
199
|
+
# we set the caller location to the second item on the stack
|
200
|
+
# (immediate caller of the `instrument` method).
|
201
|
+
unless opts[:source_file] || opts[:source_line] || opts[:internal]
|
202
|
+
opts = opts.merge(sk_instrument_location: caller_locations(1..1).first)
|
203
|
+
end
|
204
|
+
|
197
205
|
meta ||= {}
|
198
206
|
|
199
207
|
instrumenter.extensions.process_instrument_options(opts, meta)
|
200
|
-
|
201
208
|
instrumenter.instrument(category, title, desc, meta, &block)
|
202
209
|
end
|
203
210
|
|
data/lib/skylight/api.rb
CHANGED
data/lib/skylight/cli/doctor.rb
CHANGED
@@ -85,20 +85,18 @@ module Skylight
|
|
85
85
|
say "Checking for valid configuration"
|
86
86
|
|
87
87
|
indent do
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
say "This may occur if you are configuring with ENV variables and didn't set them in this shell."
|
98
|
-
end
|
99
|
-
|
100
|
-
done!
|
88
|
+
config.validate!
|
89
|
+
say "Configuration is valid", :green
|
90
|
+
rescue ConfigError => e
|
91
|
+
encountered_error!
|
92
|
+
|
93
|
+
say "Configuration is invalid", :red
|
94
|
+
indent do
|
95
|
+
say e.message, :red
|
96
|
+
say "This may occur if you are configuring with ENV variables and didn't set them in this shell."
|
101
97
|
end
|
98
|
+
|
99
|
+
done!
|
102
100
|
end
|
103
101
|
|
104
102
|
puts "\n"
|
data/lib/skylight/config.rb
CHANGED
@@ -44,6 +44,7 @@ module Skylight
|
|
44
44
|
-"LOG_LEVEL" => :log_level,
|
45
45
|
-"ALERT_LOG_FILE" => :alert_log_file,
|
46
46
|
-"NATIVE_LOG_FILE" => :native_log_file,
|
47
|
+
-"NATIVE_LOG_LEVEL" => :native_log_level,
|
47
48
|
-"LOG_SQL_PARSE_ERRORS" => :log_sql_parse_errors,
|
48
49
|
|
49
50
|
# == Proxy ==
|
@@ -110,7 +111,8 @@ module Skylight
|
|
110
111
|
-"HEROKU_DYNO_INFO_PATH" => :'heroku.dyno_info_path',
|
111
112
|
|
112
113
|
# == Source Location ==
|
113
|
-
-"SOURCE_LOCATION_IGNORED_GEMS" => :source_location_ignored_gems
|
114
|
+
-"SOURCE_LOCATION_IGNORED_GEMS" => :source_location_ignored_gems,
|
115
|
+
-"SOURCE_LOCATION_CACHE_SIZE" => :source_location_cache_size
|
114
116
|
}.freeze
|
115
117
|
|
116
118
|
KEY_TO_NATIVE_ENV = {
|
@@ -119,9 +121,7 @@ module Skylight
|
|
119
121
|
native_log_level: "LOG_LEVEL"
|
120
122
|
}.freeze
|
121
123
|
|
122
|
-
SERVER_VALIDATE = %i[
|
123
|
-
enable_source_locations
|
124
|
-
].freeze
|
124
|
+
SERVER_VALIDATE = %i[].freeze
|
125
125
|
|
126
126
|
DEFAULT_IGNORED_SOURCE_LOCATION_GEMS = [
|
127
127
|
-"skylight",
|
@@ -145,12 +145,13 @@ module Skylight
|
|
145
145
|
log_level: -"INFO",
|
146
146
|
alert_log_file: -"-",
|
147
147
|
log_sql_parse_errors: true,
|
148
|
+
native_log_level: -"warn",
|
148
149
|
|
149
150
|
# Features
|
150
151
|
enable_segments: true,
|
151
152
|
enable_sidekiq: false,
|
152
153
|
sinatra_route_prefixes: false,
|
153
|
-
enable_source_locations:
|
154
|
+
enable_source_locations: true,
|
154
155
|
|
155
156
|
# Deploys
|
156
157
|
'heroku.dyno_info_path': -"/etc/heroku/dyno",
|
@@ -243,7 +244,7 @@ module Skylight
|
|
243
244
|
end
|
244
245
|
|
245
246
|
# @api private
|
246
|
-
attr_reader :
|
247
|
+
attr_reader :priority_key
|
247
248
|
|
248
249
|
# @api private
|
249
250
|
def initialize(*args)
|
@@ -253,16 +254,16 @@ module Skylight
|
|
253
254
|
attrs = args.pop.dup
|
254
255
|
end
|
255
256
|
|
256
|
-
@values
|
257
|
+
@values = {}
|
257
258
|
@priority = {}
|
258
|
-
@
|
259
|
+
@priority_regexp = nil
|
259
260
|
@alert_logger = nil
|
260
261
|
@logger = nil
|
261
262
|
|
262
263
|
p = attrs.delete(:priority)
|
263
264
|
|
264
|
-
if (@
|
265
|
-
@
|
265
|
+
if (@priority_key = args[0])
|
266
|
+
@priority_regexp = /^#{Regexp.escape(priority_key)}\.(.+)$/
|
266
267
|
end
|
267
268
|
|
268
269
|
attrs.each do |k, v|
|
@@ -277,7 +278,8 @@ module Skylight
|
|
277
278
|
def self.load(opts = {}, env = ENV)
|
278
279
|
attrs = {}
|
279
280
|
path = opts.delete(:file)
|
280
|
-
|
281
|
+
priority_key = opts.delete(:priority_key)
|
282
|
+
priority_key ||= opts[:env] # if a priority_key is not given, use env if available
|
281
283
|
|
282
284
|
if path
|
283
285
|
error = nil
|
@@ -295,11 +297,14 @@ module Skylight
|
|
295
297
|
raise ConfigError, "could not load config file; msg=#{error}" if error
|
296
298
|
end
|
297
299
|
|
300
|
+
# The key-value pairs in this `priority` option are inserted into the
|
301
|
+
# config's @priority hash *after* anything listed under priority_key;
|
302
|
+
# i.e., ENV takes precendence over priority_key
|
298
303
|
if env
|
299
304
|
attrs[:priority] = remap_env(env)
|
300
305
|
end
|
301
306
|
|
302
|
-
config = new(
|
307
|
+
config = new(priority_key, attrs)
|
303
308
|
|
304
309
|
opts.each do |k, v|
|
305
310
|
config[k] = v
|
@@ -439,7 +444,7 @@ module Skylight
|
|
439
444
|
end
|
440
445
|
end
|
441
446
|
|
442
|
-
if @
|
447
|
+
if @priority_regexp && k =~ @priority_regexp
|
443
448
|
@priority[$1.to_sym] = val
|
444
449
|
end
|
445
450
|
|
@@ -549,23 +554,13 @@ module Skylight
|
|
549
554
|
when /^warn$/i then Logger::WARN
|
550
555
|
when /^error$/i then Logger::ERROR
|
551
556
|
when /^fatal$/i then Logger::FATAL
|
552
|
-
else Logger::ERROR
|
557
|
+
else Logger::ERROR # rubocop:disable Lint/DuplicateBranch
|
553
558
|
end
|
554
559
|
end
|
555
560
|
end
|
556
561
|
|
557
562
|
def native_log_level
|
558
|
-
|
559
|
-
if trace?
|
560
|
-
"trace"
|
561
|
-
else
|
562
|
-
case log_level
|
563
|
-
when Logger::DEBUG then "debug"
|
564
|
-
when Logger::INFO then "info"
|
565
|
-
when Logger::WARN then "warn"
|
566
|
-
else "error"
|
567
|
-
end
|
568
|
-
end
|
563
|
+
get(:native_log_level).to_s.downcase
|
569
564
|
end
|
570
565
|
|
571
566
|
def logger
|
@@ -586,7 +581,7 @@ module Skylight
|
|
586
581
|
end
|
587
582
|
end
|
588
583
|
|
589
|
-
attr_writer :logger
|
584
|
+
attr_writer :logger, :alert_logger
|
590
585
|
|
591
586
|
def alert_logger
|
592
587
|
@alert_logger ||= MUTEX.synchronize do
|
@@ -601,8 +596,6 @@ module Skylight
|
|
601
596
|
end
|
602
597
|
end
|
603
598
|
|
604
|
-
attr_writer :alert_logger
|
605
|
-
|
606
599
|
def enable_segments?
|
607
600
|
!!get(:enable_segments)
|
608
601
|
end
|
@@ -638,13 +631,13 @@ module Skylight
|
|
638
631
|
|
639
632
|
Logger.new(out, progname: "Skylight", level: level)
|
640
633
|
rescue
|
641
|
-
Logger.new(
|
634
|
+
Logger.new($stdout, progname: "Skylight", level: level)
|
642
635
|
end
|
643
636
|
|
644
637
|
def load_logger
|
645
638
|
unless (l = @logger)
|
646
639
|
out = get(:log_file)
|
647
|
-
out =
|
640
|
+
out = $stdout if out == "-"
|
648
641
|
l = create_logger(out, level: log_level)
|
649
642
|
end
|
650
643
|
|
@@ -690,7 +683,7 @@ module Skylight
|
|
690
683
|
corrected_config = res.corrected_config
|
691
684
|
|
692
685
|
# Use defaults if no corrected config is available. This will happen if the request failed.
|
693
|
-
corrected_config ||=
|
686
|
+
corrected_config ||= SERVER_VALIDATE.map { |k| [k, self.class.default_values.fetch(k)] }.to_h
|
694
687
|
|
695
688
|
config_to_update = corrected_config.reject { |k, v| get(k) == v }
|
696
689
|
unless config_to_update.empty?
|
@@ -700,7 +693,7 @@ module Skylight
|
|
700
693
|
|
701
694
|
# This is a weird way to handle priorities
|
702
695
|
# See https://github.com/tildeio/direwolf-agent/issues/275
|
703
|
-
k = "#{
|
696
|
+
k = "#{priority_key}.#{k}" if priority_key
|
704
697
|
|
705
698
|
set(k, v)
|
706
699
|
end
|
data/lib/skylight/deprecation.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/deprecation"
|
2
4
|
|
3
5
|
module Skylight
|
4
|
-
SKYLIGHT_GEM_ROOT = File.expand_path(
|
6
|
+
SKYLIGHT_GEM_ROOT = "#{File.expand_path('../..', __dir__)}/"
|
5
7
|
|
6
8
|
class Deprecation < ActiveSupport::Deprecation
|
7
9
|
private
|
data/lib/skylight/errors.rb
CHANGED
@@ -13,10 +13,10 @@ module Skylight
|
|
13
13
|
end
|
14
14
|
|
15
15
|
Skylight.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
16
|
-
class #{name}Error < NativeError
|
17
|
-
def self.code; #{code}; end
|
18
|
-
def self.message; #{message.to_json}; end
|
19
|
-
end
|
16
|
+
class #{name}Error < NativeError # class SqlLexError < NativeError
|
17
|
+
def self.code; #{code}; end # def self.code; 4; end
|
18
|
+
def self.message; #{message.to_json}; end # def self.message; "Failed to lex SQL query."; end
|
19
|
+
end # end
|
20
20
|
RUBY
|
21
21
|
|
22
22
|
klass = Skylight.const_get("#{name}Error")
|
data/lib/skylight/extensions.rb
CHANGED
@@ -32,6 +32,12 @@ module Skylight
|
|
32
32
|
!!extensions.detect { |x| x.is_a?(ext_class) }
|
33
33
|
end
|
34
34
|
|
35
|
+
def process_trace_meta(meta)
|
36
|
+
extensions.each do |ext|
|
37
|
+
ext.process_trace_meta(meta)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
35
41
|
# meta is a mutable hash that will be passed to the instrumenter.
|
36
42
|
# This method bridges Skylight.instrument and instrumenter.instrument.
|
37
43
|
def process_instrument_options(opts, meta)
|
@@ -83,6 +89,8 @@ module Skylight
|
|
83
89
|
@config = config
|
84
90
|
end
|
85
91
|
|
92
|
+
def process_trace_meta(_meta); end
|
93
|
+
|
86
94
|
def process_instrument_options(_opts, _meta); end
|
87
95
|
|
88
96
|
def process_normalizer_meta(_payload, _meta, **opts); end
|
@@ -11,24 +11,46 @@ module Skylight
|
|
11
11
|
include Util::Logging
|
12
12
|
|
13
13
|
META_KEYS = %i[source_location source_file source_line].freeze
|
14
|
+
MAX_CALLER_DEPTH = 75
|
14
15
|
|
15
16
|
def initialize(*)
|
16
17
|
super
|
17
|
-
|
18
|
-
@
|
18
|
+
cache_size = (config[:source_location_cache_size] || 1000).to_i
|
19
|
+
@caller_cache = Util::LruCache.new(cache_size)
|
20
|
+
@instance_method_source_location_cache = Util::LruCache.new(cache_size)
|
19
21
|
gem_require_trie # memoize this at startup
|
20
22
|
end
|
21
23
|
|
24
|
+
def process_trace_meta(meta)
|
25
|
+
unless meta[:source_location] || meta[:source_file]
|
26
|
+
warn "Ignoring source_line without source_file" if meta[:source_line]
|
27
|
+
if (location = find_caller)
|
28
|
+
meta[:source_file] = location.absolute_path
|
29
|
+
meta[:source_line] = location.lineno
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
22
34
|
def process_instrument_options(opts, meta)
|
23
35
|
source_location = opts[:source_location] || opts[:meta]&.[](:source_location)
|
24
36
|
source_file = opts[:source_file] || opts[:meta]&.[](:source_file)
|
25
37
|
source_line = opts[:source_line] || opts[:meta]&.[](:source_line)
|
38
|
+
source_name_hint, const_name, method_name = opts[:source_location_hint] ||
|
39
|
+
opts[:meta]&.[](:source_location_hint)
|
40
|
+
instrument_location = opts[:sk_instrument_location]
|
26
41
|
|
27
42
|
if source_location
|
28
43
|
meta[:source_location] = source_location
|
44
|
+
elsif source_name_hint
|
45
|
+
source_location = dispatch_hinted_source_location(source_name_hint, const_name, method_name)
|
46
|
+
meta[:source_file], meta[:source_line] = source_location
|
47
|
+
meta.delete(:source_location_hint) if source_location
|
29
48
|
elsif source_file
|
30
49
|
meta[:source_file] = source_file
|
31
50
|
meta[:source_line] = source_line
|
51
|
+
elsif instrument_location && project_path?(instrument_location.absolute_path)
|
52
|
+
meta[:source_file] = instrument_location.absolute_path
|
53
|
+
meta[:source_line] = instrument_location.lineno
|
32
54
|
else
|
33
55
|
warn "Ignoring source_line without source_file" if source_line
|
34
56
|
if (location = find_caller(cache_key: opts.hash))
|
@@ -41,14 +63,21 @@ module Skylight
|
|
41
63
|
end
|
42
64
|
|
43
65
|
def process_normalizer_meta(payload, meta, **opts)
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
66
|
+
if opts[:source_location] && (opts[:source_file] || opts[:source_line])
|
67
|
+
warn "Found both source_location and source_file or source_line in normalizer\n" \
|
68
|
+
" location=#{opts[:source_location]}; file=#{opts[:source_file]}; line=#{opts[:source_line]}"
|
69
|
+
end
|
70
|
+
|
71
|
+
sl =
|
72
|
+
if (source_name, constant_name, method_name = opts[:source_location_hint])
|
73
|
+
dispatch_hinted_source_location(
|
74
|
+
source_name,
|
75
|
+
constant_name,
|
76
|
+
method_name
|
77
|
+
)
|
78
|
+
elsif opts[:source_file]
|
79
|
+
[opts[:source_file], opts[:source_line]]
|
80
|
+
end
|
52
81
|
|
53
82
|
sl ||= source_location(payload, meta, cache_key: opts[:cache_key])
|
54
83
|
|
@@ -91,86 +120,92 @@ module Skylight
|
|
91
120
|
|
92
121
|
protected
|
93
122
|
|
94
|
-
|
95
|
-
|
96
|
-
return unless const_name && method_name
|
123
|
+
def dispatch_hinted_source_location(source_name, const_name, method_name)
|
124
|
+
return unless const_name && method_name
|
97
125
|
|
98
|
-
|
99
|
-
|
126
|
+
instance_method_source_location(const_name, method_name, source_name: source_name)
|
127
|
+
end
|
100
128
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
129
|
+
# from normalizers.rb
|
130
|
+
# Returns an array of file and line
|
131
|
+
def source_location(payload, meta, cache_key: nil)
|
132
|
+
# FIXME: what should precedence be?
|
133
|
+
if meta.is_a?(Hash) && meta[:source_location]
|
134
|
+
meta.delete(:source_location)
|
135
|
+
elsif payload.is_a?(Hash) && payload[:sk_source_location]
|
136
|
+
payload[:sk_source_location]
|
137
|
+
elsif (location = find_caller(cache_key: cache_key))
|
138
|
+
[location.absolute_path, location.lineno]
|
139
|
+
end
|
111
140
|
end
|
112
|
-
end
|
113
141
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
142
|
+
def find_caller(cache_key: nil)
|
143
|
+
# starting at 4 to skip Skylight extension processing logic
|
144
|
+
locations = ::Kernel.caller_locations(4..MAX_CALLER_DEPTH)
|
145
|
+
|
146
|
+
if cache_key
|
147
|
+
localized_cache_key = [cache_key, locations.map(&:lineno)].hash
|
148
|
+
@caller_cache.fetch(localized_cache_key) { find_caller_inner(locations) }
|
149
|
+
else
|
150
|
+
find_caller_inner(locations)
|
151
|
+
end
|
119
152
|
end
|
120
|
-
end
|
121
153
|
|
122
|
-
|
123
|
-
|
124
|
-
return false unless path.start_with?(config.root.to_s)
|
125
|
-
# Must not be Bundler's vendor location
|
126
|
-
return false if defined?(Bundler) && path.start_with?(Bundler.bundle_path.to_s)
|
127
|
-
# Must not be Ruby files
|
128
|
-
return false if path.include?("/ruby-#{RUBY_VERSION}/lib/ruby/")
|
154
|
+
def project_path?(path)
|
155
|
+
return false unless path
|
129
156
|
|
130
|
-
|
131
|
-
|
132
|
-
|
157
|
+
# Must be in the project root
|
158
|
+
return false unless path.start_with?(config.root.to_s)
|
159
|
+
# Must not be Bundler's vendor location
|
160
|
+
return false if defined?(Bundler) && path.start_with?(Bundler.bundle_path.to_s)
|
161
|
+
# Must not be Ruby files
|
162
|
+
return false if path.include?("/ruby-#{RUBY_VERSION}/lib/ruby/")
|
133
163
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
164
|
+
# So it must be a project file
|
165
|
+
true
|
166
|
+
end
|
167
|
+
|
168
|
+
def instance_method_source_location(constant_name, method_name, source_name: :instance_method)
|
169
|
+
@instance_method_source_location_cache.fetch([constant_name, method_name, source_name]) do
|
170
|
+
if (constant = ::ActiveSupport::Dependencies.safe_constantize(constant_name))
|
171
|
+
if constant.instance_methods.include?(:"before_instrument_#{method_name}")
|
172
|
+
method_name = :"before_instrument_#{method_name}"
|
173
|
+
end
|
174
|
+
begin
|
175
|
+
unbound_method = case source_name
|
176
|
+
when :instance_method
|
177
|
+
find_instance_method(constant, method_name)
|
178
|
+
when :own_instance_method
|
179
|
+
find_own_instance_method(constant, method_name)
|
180
|
+
when :instance_method_super
|
181
|
+
find_instance_method_super(constant, method_name)
|
182
|
+
when :class_method
|
183
|
+
find_class_method(constant, method_name)
|
184
|
+
end
|
185
|
+
|
186
|
+
unbound_method&.source_location
|
187
|
+
rescue NameError
|
188
|
+
nil
|
189
|
+
end
|
153
190
|
end
|
154
191
|
end
|
155
192
|
end
|
156
|
-
end
|
157
193
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
end
|
194
|
+
def sanitize_source_location(path, line)
|
195
|
+
# Do this first since gems may be vendored in the app repo. However, it might be slower.
|
196
|
+
# Should we cache matches?
|
197
|
+
if (gem_name = find_source_gem(path))
|
198
|
+
path = gem_name
|
199
|
+
line = nil
|
200
|
+
elsif project_path?(path)
|
201
|
+
# Get relative path to root
|
202
|
+
path = Pathname.new(path).relative_path_from(config.root).to_s
|
203
|
+
else
|
204
|
+
return
|
205
|
+
end
|
171
206
|
|
172
|
-
|
173
|
-
|
207
|
+
line ? "#{path}:#{line}" : path
|
208
|
+
end
|
174
209
|
|
175
210
|
private
|
176
211
|
|
@@ -198,21 +233,25 @@ module Skylight
|
|
198
233
|
end
|
199
234
|
|
200
235
|
def find_source_gem(path)
|
236
|
+
return nil unless path
|
237
|
+
|
201
238
|
trie = gem_require_trie
|
202
239
|
|
203
240
|
path.split(File::SEPARATOR).each do |segment|
|
204
241
|
trie = trie[segment]
|
205
|
-
|
242
|
+
break unless trie
|
206
243
|
return trie[:name] if trie[:name]
|
207
244
|
end
|
208
245
|
|
209
246
|
nil
|
210
247
|
end
|
211
248
|
|
212
|
-
def find_caller_inner
|
249
|
+
def find_caller_inner(locations)
|
213
250
|
# Start at file before this one
|
214
|
-
|
215
|
-
|
251
|
+
# NOTE: We could start farther back now to avoid more Skylight files
|
252
|
+
locations.find do |l|
|
253
|
+
absolute_path = l.absolute_path
|
254
|
+
find_source_gem(absolute_path) || project_path?(absolute_path)
|
216
255
|
end
|
217
256
|
end
|
218
257
|
|
@@ -244,6 +283,9 @@ module Skylight
|
|
244
283
|
constant.instance_method(method_name)
|
245
284
|
end
|
246
285
|
|
286
|
+
def find_class_method(constant, method_name)
|
287
|
+
constant.method(method_name)
|
288
|
+
end
|
247
289
|
end
|
248
290
|
end
|
249
291
|
end
|