skylight 3.1.4 → 5.3.4
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +465 -294
- data/CLA.md +1 -1
- data/CONTRIBUTING.md +11 -3
- data/ERRORS.md +3 -0
- data/LICENSE.md +8 -18
- data/README.md +1 -2
- data/bin/skylight +1 -1
- data/ext/extconf.rb +118 -122
- data/ext/libskylight.yml +8 -6
- data/ext/skylight_native.c +56 -100
- data/lib/skylight/api.rb +41 -27
- data/lib/skylight/cli/doctor.rb +68 -70
- data/lib/skylight/cli/helpers.rb +3 -5
- data/lib/skylight/cli/merger.rb +99 -92
- data/lib/skylight/cli.rb +40 -43
- data/lib/skylight/config.rb +656 -201
- data/lib/skylight/data/cacert.pem +730 -1023
- data/lib/skylight/deprecation.rb +17 -0
- data/lib/skylight/errors.rb +34 -16
- data/lib/skylight/extensions/source_location.rb +291 -0
- data/lib/skylight/extensions.rb +95 -0
- data/lib/skylight/formatters/http.rb +18 -0
- data/lib/skylight/gc.rb +99 -0
- data/lib/skylight/helpers.rb +82 -39
- data/lib/skylight/instrumenter.rb +339 -9
- data/lib/skylight/middleware.rb +147 -1
- data/lib/skylight/native.rb +71 -23
- data/lib/skylight/native_ext_fetcher.rb +39 -47
- data/lib/skylight/normalizers/action_controller/process_action.rb +68 -0
- data/lib/skylight/normalizers/action_controller/send_file.rb +51 -0
- data/lib/skylight/normalizers/action_dispatch/process_middleware.rb +22 -0
- data/lib/skylight/normalizers/action_dispatch/route_set.rb +27 -0
- data/lib/skylight/normalizers/action_view/render_collection.rb +24 -0
- data/lib/skylight/normalizers/action_view/render_layout.rb +25 -0
- data/lib/skylight/normalizers/action_view/render_partial.rb +23 -0
- data/lib/skylight/normalizers/action_view/render_template.rb +23 -0
- data/lib/skylight/normalizers/active_job/perform.rb +87 -0
- data/lib/skylight/normalizers/active_model_serializers/render.rb +32 -0
- data/lib/skylight/normalizers/active_record/instantiation.rb +16 -0
- data/lib/skylight/normalizers/active_record/sql.rb +20 -0
- data/lib/skylight/normalizers/active_storage.rb +28 -0
- data/lib/skylight/normalizers/active_support/cache.rb +11 -0
- data/lib/skylight/normalizers/active_support/cache_clear.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_decrement.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_delete.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_exist.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_fetch_hit.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_generate.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_increment.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_read.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_read_multi.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_write.rb +16 -0
- data/lib/skylight/normalizers/coach/handler_finish.rb +44 -0
- data/lib/skylight/normalizers/coach/middleware_finish.rb +33 -0
- data/lib/skylight/normalizers/couch_potato/query.rb +20 -0
- data/lib/skylight/normalizers/data_mapper/sql.rb +12 -0
- data/lib/skylight/normalizers/default.rb +24 -0
- data/lib/skylight/normalizers/elasticsearch/request.rb +20 -0
- data/lib/skylight/normalizers/faraday/request.rb +38 -0
- data/lib/skylight/normalizers/grape/endpoint.rb +28 -0
- data/lib/skylight/normalizers/grape/endpoint_render.rb +25 -0
- data/lib/skylight/normalizers/grape/endpoint_run.rb +39 -0
- data/lib/skylight/normalizers/grape/endpoint_run_filters.rb +20 -0
- data/lib/skylight/normalizers/grape/format_response.rb +20 -0
- data/lib/skylight/normalizers/graphiti/render.rb +22 -0
- data/lib/skylight/normalizers/graphiti/resolve.rb +31 -0
- data/lib/skylight/normalizers/graphql/base.rb +127 -0
- data/lib/skylight/normalizers/render.rb +79 -0
- data/lib/skylight/normalizers/sequel/sql.rb +12 -0
- data/lib/skylight/normalizers/shrine.rb +32 -0
- data/lib/skylight/normalizers/sql.rb +41 -0
- data/lib/skylight/normalizers.rb +157 -0
- data/lib/skylight/probes/action_controller.rb +52 -0
- data/lib/skylight/probes/action_dispatch/request_id.rb +33 -0
- data/lib/skylight/probes/action_dispatch/routing/route_set.rb +30 -0
- data/lib/skylight/probes/action_dispatch.rb +2 -0
- data/lib/skylight/probes/action_view.rb +42 -0
- data/lib/skylight/probes/active_job.rb +27 -0
- data/lib/skylight/probes/active_job_enqueue.rb +35 -0
- data/lib/skylight/probes/active_model_serializers.rb +50 -0
- data/lib/skylight/probes/active_record_async.rb +96 -0
- data/lib/skylight/probes/delayed_job.rb +144 -0
- data/lib/skylight/probes/elasticsearch.rb +45 -0
- data/lib/skylight/probes/excon/middleware.rb +65 -0
- data/lib/skylight/probes/excon.rb +25 -0
- data/lib/skylight/probes/faraday.rb +23 -0
- data/lib/skylight/probes/graphql.rb +38 -0
- data/lib/skylight/probes/httpclient.rb +44 -0
- data/lib/skylight/probes/middleware.rb +135 -0
- data/lib/skylight/probes/mongo.rb +169 -0
- data/lib/skylight/probes/mongoid.rb +6 -0
- data/lib/skylight/probes/net_http.rb +54 -0
- data/lib/skylight/probes/rack_builder.rb +37 -0
- data/lib/skylight/probes/redis.rb +68 -0
- data/lib/skylight/probes/sequel.rb +29 -0
- data/lib/skylight/probes/sinatra.rb +66 -0
- data/lib/skylight/probes/sinatra_add_middleware.rb +10 -10
- data/lib/skylight/probes/tilt.rb +25 -0
- data/lib/skylight/probes.rb +172 -0
- data/lib/skylight/railtie.rb +172 -15
- data/lib/skylight/sidekiq.rb +47 -0
- data/lib/skylight/sinatra.rb +2 -2
- data/lib/skylight/subscriber.rb +130 -0
- data/lib/skylight/test.rb +147 -0
- data/lib/skylight/trace.rb +331 -15
- data/lib/skylight/user_config.rb +60 -0
- data/lib/skylight/util/allocation_free.rb +26 -0
- data/lib/skylight/util/clock.rb +57 -0
- data/lib/skylight/util/component.rb +47 -9
- data/lib/skylight/util/deploy.rb +24 -40
- data/lib/skylight/util/gzip.rb +20 -0
- data/lib/skylight/util/hostname.rb +4 -4
- data/lib/skylight/util/http.rb +62 -71
- data/lib/skylight/util/instrumenter_method.rb +26 -0
- data/lib/skylight/util/logging.rb +136 -0
- data/lib/skylight/util/lru_cache.rb +36 -0
- data/lib/skylight/util/platform.rb +74 -0
- data/lib/skylight/util/proxy.rb +13 -0
- data/lib/skylight/util/ssl.rb +4 -28
- data/lib/skylight/util.rb +12 -0
- data/lib/skylight/vendor/cli/thor/rake_compat.rb +1 -1
- data/lib/skylight/version.rb +5 -1
- data/lib/skylight/vm/gc.rb +60 -0
- data/lib/skylight.rb +213 -24
- metadata +171 -53
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
module Normalizers
|
|
3
|
+
module Grape
|
|
4
|
+
class EndpointRunFilters < Endpoint
|
|
5
|
+
register "endpoint_run_filters.grape"
|
|
6
|
+
|
|
7
|
+
CAT = "app.grape.filters".freeze
|
|
8
|
+
|
|
9
|
+
def normalize(_trace, _name, payload)
|
|
10
|
+
filters = payload[:filters]
|
|
11
|
+
type = payload[:type]
|
|
12
|
+
|
|
13
|
+
return :skip if (!filters || filters.empty?) || !type
|
|
14
|
+
|
|
15
|
+
[CAT, "#{type.to_s.capitalize} Filters", nil]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
module Normalizers
|
|
3
|
+
module Grape
|
|
4
|
+
class FormatResponse < Normalizer
|
|
5
|
+
register "format_response.grape"
|
|
6
|
+
|
|
7
|
+
CAT = "view.grape.format_response".freeze
|
|
8
|
+
|
|
9
|
+
def normalize(_trace, _name, payload)
|
|
10
|
+
if (formatter = payload[:formatter])
|
|
11
|
+
title = formatter.is_a?(Module) ? formatter.to_s : formatter.class.to_s
|
|
12
|
+
[CAT, title, nil]
|
|
13
|
+
else
|
|
14
|
+
:skip
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Skylight
|
|
4
|
+
module Normalizers
|
|
5
|
+
module Graphiti
|
|
6
|
+
class Render < Normalizer
|
|
7
|
+
register "render.graphiti"
|
|
8
|
+
|
|
9
|
+
CAT = "view.render.graphiti"
|
|
10
|
+
ANONYMOUS = "<Anonymous Resource>"
|
|
11
|
+
|
|
12
|
+
def normalize(_trace, _name, payload)
|
|
13
|
+
resource_class = payload[:proxy]&.resource&.class
|
|
14
|
+
title = "Render #{resource_class&.name || ANONYMOUS}"
|
|
15
|
+
desc = nil
|
|
16
|
+
|
|
17
|
+
[CAT, title, desc]
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Skylight
|
|
4
|
+
module Normalizers
|
|
5
|
+
module Graphiti
|
|
6
|
+
class Resolve < Normalizer
|
|
7
|
+
register "resolve.graphiti"
|
|
8
|
+
|
|
9
|
+
CAT = "app.resolve.graphiti"
|
|
10
|
+
|
|
11
|
+
ANONYMOUS_RESOURCE = "<Anonymous Resource>"
|
|
12
|
+
ANONYMOUS_ADAPTER = "<Anonymous Adapter>"
|
|
13
|
+
|
|
14
|
+
def normalize(_trace, _name, payload)
|
|
15
|
+
resource = payload[:resource]
|
|
16
|
+
|
|
17
|
+
if (sideload = payload[:sideload])
|
|
18
|
+
type = sideload.type.to_s.split("_").map(&:capitalize).join(" ")
|
|
19
|
+
desc = "Custom Scope" if sideload.class.scope_proc
|
|
20
|
+
else
|
|
21
|
+
type = "Primary"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
title = "Resolve #{type} #{resource.class.name || ANONYMOUS_RESOURCE}"
|
|
25
|
+
|
|
26
|
+
[CAT, title, desc]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/inflector"
|
|
4
|
+
|
|
5
|
+
module Skylight::Normalizers::GraphQL
|
|
6
|
+
# Some AS::N events in GraphQL are not super useful.
|
|
7
|
+
# We are purposefully ignoring the following keys (and you probably shouldn't add them):
|
|
8
|
+
# - "graphql.analyze_multiplex"
|
|
9
|
+
# - "graphql.execute_field" (very frequently called)
|
|
10
|
+
# - "graphql.execute_field_lazy"
|
|
11
|
+
|
|
12
|
+
class Base < Skylight::Normalizers::Normalizer
|
|
13
|
+
ANONYMOUS = "[anonymous]"
|
|
14
|
+
CAT = "app.graphql"
|
|
15
|
+
|
|
16
|
+
if defined?(::GraphQL::VERSION) && Gem::Version.new(::GraphQL::VERSION) >= Gem::Version.new("1.10")
|
|
17
|
+
def self.register_graphql
|
|
18
|
+
register("#{key}.graphql")
|
|
19
|
+
end
|
|
20
|
+
else
|
|
21
|
+
def self.register_graphql
|
|
22
|
+
register("graphql.#{key}")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.inherited(klass)
|
|
27
|
+
super
|
|
28
|
+
klass.const_set(:KEY, ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(klass.name)).freeze)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.key
|
|
32
|
+
self::KEY
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def normalize(_trace, _name, _payload)
|
|
36
|
+
[CAT, "graphql.#{key}", nil]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def key
|
|
42
|
+
self.class.key
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def extract_query_name(query)
|
|
46
|
+
query&.context&.[](:skylight_endpoint) || query&.operation_name || ANONYMOUS
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
class Lex < Base
|
|
51
|
+
register_graphql
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class Parse < Base
|
|
55
|
+
register_graphql
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class Validate < Base
|
|
59
|
+
register_graphql
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
class ExecuteMultiplex < Base
|
|
63
|
+
register_graphql
|
|
64
|
+
|
|
65
|
+
def normalize_after(trace, _span, _name, payload)
|
|
66
|
+
# This is in normalize_after because the queries may not have
|
|
67
|
+
# an assigned operation name before they are executed.
|
|
68
|
+
# For example, if you send a single query with a defined operation name, e.g.:
|
|
69
|
+
# ```graphql
|
|
70
|
+
# query MyNamedQuery { user(id: 1) { name } }
|
|
71
|
+
# ```
|
|
72
|
+
# ... but do _not_ send the operationName request param, the GraphQL docs[1]
|
|
73
|
+
# specify that the executor should use the operation name from the definition.
|
|
74
|
+
#
|
|
75
|
+
# In graphql-ruby's case, the calculation of the operation name is lazy, and
|
|
76
|
+
# has not been done yet at the point where execute_multiplex starts.
|
|
77
|
+
# [1] https://graphql.org/learn/serving-over-http/#post-request
|
|
78
|
+
queries, has_errors =
|
|
79
|
+
payload[:multiplex]
|
|
80
|
+
.queries
|
|
81
|
+
.each_with_object([Set.new, Set.new]) do |query, (names, errors)|
|
|
82
|
+
names << extract_query_name(query)
|
|
83
|
+
errors << query.static_errors.any?
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
trace.endpoint = "graphql:#{queries.sort.join("+")}"
|
|
87
|
+
trace.compound_response_error_status =
|
|
88
|
+
if has_errors.all?
|
|
89
|
+
:all
|
|
90
|
+
elsif has_errors.any?
|
|
91
|
+
:partial
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
class AnalyzeQuery < Base
|
|
97
|
+
register_graphql
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
class ExecuteQuery < Base
|
|
101
|
+
register_graphql
|
|
102
|
+
|
|
103
|
+
def normalize(trace, _name, payload)
|
|
104
|
+
query_name = extract_query_name(payload[:query])
|
|
105
|
+
|
|
106
|
+
meta = { mute_children: true } if query_name == ANONYMOUS
|
|
107
|
+
|
|
108
|
+
# This is probably always overriden by execute_multiplex#normalize_after,
|
|
109
|
+
# but in the case of a single query, it will be the same value anyway.
|
|
110
|
+
trace.endpoint = "graphql:#{query_name}"
|
|
111
|
+
|
|
112
|
+
[CAT, "graphql.#{key}: #{query_name}", nil, meta]
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
class ExecuteQueryLazy < ExecuteQuery
|
|
117
|
+
register_graphql
|
|
118
|
+
|
|
119
|
+
def normalize(trace, _name, payload)
|
|
120
|
+
if payload[:query]
|
|
121
|
+
super
|
|
122
|
+
elsif payload[:multiplex]
|
|
123
|
+
[CAT, "graphql.#{key}.multiplex", nil]
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
module Normalizers
|
|
3
|
+
# Base Normalizer for Rails rendering
|
|
4
|
+
class RenderNormalizer < Normalizer
|
|
5
|
+
include Skylight::Util::AllocationFree
|
|
6
|
+
|
|
7
|
+
def setup
|
|
8
|
+
@paths = []
|
|
9
|
+
|
|
10
|
+
Gem.path.each do |path|
|
|
11
|
+
@paths << "#{path}/bundler/gems".freeze
|
|
12
|
+
@paths << "#{path}/gems".freeze
|
|
13
|
+
@paths << path
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
@paths.concat(Array(config["normalizers.render.view_paths"]))
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Generic normalizer for renders
|
|
20
|
+
# @param category [String]
|
|
21
|
+
# @param payload [Hash]
|
|
22
|
+
# @option payload [String] :identifier
|
|
23
|
+
# @return [Array]
|
|
24
|
+
def normalize_render(category, payload)
|
|
25
|
+
if (path = payload[:identifier])
|
|
26
|
+
title = relative_path(path)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
[category, title, nil]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def relative_path(path)
|
|
33
|
+
return path if relative_path?(path)
|
|
34
|
+
|
|
35
|
+
if (root = array_find(@paths) { |p| path.start_with?(p) })
|
|
36
|
+
start = root.size
|
|
37
|
+
start += 1 if path.getbyte(start) == SEPARATOR_BYTE
|
|
38
|
+
|
|
39
|
+
path[start, path.size].sub(
|
|
40
|
+
# Matches a Gem Version or 12-digit hex (sha)
|
|
41
|
+
# that is preceeded by a `-` and followed by `/`
|
|
42
|
+
# Also matches 'app/views/' if it exists
|
|
43
|
+
%r{-(?:#{Gem::Version::VERSION_PATTERN}|[0-9a-f]{12})/(?:app/views/)*},
|
|
44
|
+
": ".freeze
|
|
45
|
+
)
|
|
46
|
+
else
|
|
47
|
+
"Absolute Path".freeze
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def relative_path?(path)
|
|
54
|
+
!absolute_path?(path)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
SEPARATOR_BYTE = File::SEPARATOR.ord
|
|
58
|
+
|
|
59
|
+
if File.const_defined?(:NULL) ? File::NULL == "NUL" : RbConfig::CONFIG["host_os"] =~ /mingw|mswin32/
|
|
60
|
+
# This is a DOSish environment
|
|
61
|
+
ALT_SEPARATOR_BYTE = File::ALT_SEPARATOR&.ord
|
|
62
|
+
COLON_BYTE = ":".ord
|
|
63
|
+
SEPARATOR_BYTES = [SEPARATOR_BYTE, ALT_SEPARATOR_BYTE].freeze
|
|
64
|
+
|
|
65
|
+
def absolute_path?(path)
|
|
66
|
+
SEPARATOR_BYTES.include?(path.getbyte(2)) if alpha?(path.getbyte(0)) && path.getbyte(1) == COLON_BYTE
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def alpha?(byte)
|
|
70
|
+
(byte >= 65 && byte <= 90) || (byte >= 97 && byte <= 122)
|
|
71
|
+
end
|
|
72
|
+
else
|
|
73
|
+
def absolute_path?(path)
|
|
74
|
+
path.getbyte(0) == SEPARATOR_BYTE
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
module Normalizers
|
|
3
|
+
class Shrine < Normalizer
|
|
4
|
+
TITLES = {
|
|
5
|
+
"upload.shrine" => "Upload",
|
|
6
|
+
"download.shrine" => "Download",
|
|
7
|
+
"open.shrine" => "Open",
|
|
8
|
+
"exists.shrine" => "Exists",
|
|
9
|
+
"delete.shrine" => "Delete",
|
|
10
|
+
"metadata.shrine" => "Metadata",
|
|
11
|
+
"mime_type.shrine" => "MIME Type",
|
|
12
|
+
"image_dimensions.shrine" => "Image Dimensions",
|
|
13
|
+
"signature.shrine" => "Signature",
|
|
14
|
+
"extension.shrine" => "Extension",
|
|
15
|
+
"derivation.shrine" => "Derivation",
|
|
16
|
+
"derivatives.shrine" => "Derivatives",
|
|
17
|
+
"data_uri.shrine" => "Data URI",
|
|
18
|
+
"remote_url.shrine" => "Remote URL"
|
|
19
|
+
}.freeze
|
|
20
|
+
|
|
21
|
+
TITLES.each_key { |key| register key }
|
|
22
|
+
|
|
23
|
+
def normalize(_trace, name, _payload)
|
|
24
|
+
title = ["Shrine", TITLES[name]].join(" ")
|
|
25
|
+
|
|
26
|
+
cat = "app.#{name.split(".").reverse.join(".")}"
|
|
27
|
+
|
|
28
|
+
[cat, title, nil]
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Skylight
|
|
6
|
+
module Normalizers
|
|
7
|
+
# Normalizer for SQL requests
|
|
8
|
+
class SQL < Normalizer
|
|
9
|
+
CAT = "db.sql.query"
|
|
10
|
+
|
|
11
|
+
# @param trace [Skylight::Messages::Trace::Builder] ignored, only present to match API
|
|
12
|
+
# @param name [String] ignored, only present to match API
|
|
13
|
+
# @param payload [Hash]
|
|
14
|
+
# @option payload [String] [:name] The SQL operation
|
|
15
|
+
# @option payload [Hash] [:binds] The bound parameters
|
|
16
|
+
# @return [Array]
|
|
17
|
+
def normalize(_trace, _name, payload)
|
|
18
|
+
return :skip if payload[:name] == "SCHEMA" || payload[:name] == "CACHE"
|
|
19
|
+
|
|
20
|
+
title = payload[:name] || "SQL"
|
|
21
|
+
|
|
22
|
+
# We can only handle UTF-8 encoded strings.
|
|
23
|
+
# (Construction method here avoids extra allocations)
|
|
24
|
+
sql = String.new.concat("<sk-sql>", payload[:sql], "</sk-sql>").force_encoding(Encoding::UTF_8)
|
|
25
|
+
|
|
26
|
+
unless sql.valid_encoding?
|
|
27
|
+
if config[:log_sql_parse_errors]
|
|
28
|
+
config.logger.error "[#{Skylight::SqlLexError.formatted_code}] Unable to extract binds from non-UTF-8 " \
|
|
29
|
+
"query. " \
|
|
30
|
+
"encoding=#{payload[:sql].encoding.name} " \
|
|
31
|
+
"sql=#{payload[:sql].inspect} "
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
sql = nil
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
[CAT, title, sql]
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
# @api private
|
|
3
|
+
# Convert AS::N events to Skylight events
|
|
4
|
+
module Normalizers
|
|
5
|
+
def self.registry
|
|
6
|
+
@registry ||= {}
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.register(name, klass, opts = {})
|
|
10
|
+
enabled = opts[:enabled] != false
|
|
11
|
+
registry[name] = [klass, enabled]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.unregister(name)
|
|
15
|
+
@registry.delete(name)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.enable(*names, enabled: true)
|
|
19
|
+
names.each do |name|
|
|
20
|
+
matches = registry.select { |n, _| n =~ /(^|\.)#{name}$/ }
|
|
21
|
+
raise ArgumentError, "no normalizers match #{name}" if matches.empty?
|
|
22
|
+
|
|
23
|
+
matches.each_value { |v| v[1] = enabled }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.disable(*names)
|
|
28
|
+
enable(*names, enabled: false)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.build(config)
|
|
32
|
+
normalizers = {}
|
|
33
|
+
|
|
34
|
+
registry.each do |key, (klass, enabled)|
|
|
35
|
+
next unless enabled
|
|
36
|
+
|
|
37
|
+
unless klass.method_defined?(:normalize)
|
|
38
|
+
# TODO: Warn
|
|
39
|
+
next
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
normalizers[key] = klass.new(config)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
Container.new(normalizers)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class Normalizer
|
|
49
|
+
def self.register(name, opts = {})
|
|
50
|
+
Normalizers.register(name, self, opts)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
attr_reader :config
|
|
54
|
+
|
|
55
|
+
include Util::Logging
|
|
56
|
+
|
|
57
|
+
def initialize(config)
|
|
58
|
+
@config = config
|
|
59
|
+
setup if respond_to?(:setup)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def normalize(_trace, _name, _payload)
|
|
63
|
+
:skip
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def normalize_with_meta(trace, name, payload)
|
|
67
|
+
# If we have a normal response but no meta, add it
|
|
68
|
+
cat, title, desc, meta = ret = normalize(trace, name, payload)
|
|
69
|
+
return cat if cat == :skip
|
|
70
|
+
|
|
71
|
+
meta ||= {}
|
|
72
|
+
cache_key = ret.hash
|
|
73
|
+
process_meta(trace, name, payload, meta, cache_key: cache_key)
|
|
74
|
+
|
|
75
|
+
[cat, title, desc, meta]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def normalize_after(trace, span, name, payload); end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def process_meta(trace, _name, payload, meta, cache_key: nil)
|
|
83
|
+
trace.instrumenter.extensions.process_normalizer_meta(
|
|
84
|
+
payload,
|
|
85
|
+
meta,
|
|
86
|
+
cache_key: cache_key,
|
|
87
|
+
**process_meta_options(payload)
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def process_meta_options(_payload)
|
|
92
|
+
{}
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
require "skylight/normalizers/default"
|
|
97
|
+
DEFAULT = Default.new
|
|
98
|
+
|
|
99
|
+
class Container
|
|
100
|
+
def initialize(normalizers)
|
|
101
|
+
@normalizers = normalizers
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def keys
|
|
105
|
+
@normalizers.keys
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def each_key(&block)
|
|
109
|
+
@normalizers.each_key(&block)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def normalize(trace, name, payload)
|
|
113
|
+
normalizer_for(name).normalize_with_meta(trace, name, payload)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def normalize_after(trace, span, name, payload)
|
|
117
|
+
normalizer_for(name).normalize_after(trace, span, name, payload)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def normalizer_for(name)
|
|
121
|
+
# We never expect to hit the default case since we only register listeners
|
|
122
|
+
# for items that we know have normalizers. For now, though, we'll play it
|
|
123
|
+
# safe and provide a fallback.
|
|
124
|
+
@normalizers.fetch(name, DEFAULT)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
%w[
|
|
129
|
+
action_controller/process_action
|
|
130
|
+
action_controller/send_file
|
|
131
|
+
action_dispatch/process_middleware
|
|
132
|
+
action_dispatch/route_set
|
|
133
|
+
action_view/render_collection
|
|
134
|
+
action_view/render_partial
|
|
135
|
+
action_view/render_template
|
|
136
|
+
action_view/render_layout
|
|
137
|
+
active_job/perform
|
|
138
|
+
active_model_serializers/render
|
|
139
|
+
active_record/instantiation
|
|
140
|
+
active_record/sql
|
|
141
|
+
active_storage
|
|
142
|
+
active_support/cache
|
|
143
|
+
coach/handler_finish
|
|
144
|
+
coach/middleware_finish
|
|
145
|
+
couch_potato/query
|
|
146
|
+
data_mapper/sql
|
|
147
|
+
elasticsearch/request
|
|
148
|
+
faraday/request
|
|
149
|
+
grape/endpoint
|
|
150
|
+
graphiti/resolve
|
|
151
|
+
graphiti/render
|
|
152
|
+
graphql/base
|
|
153
|
+
sequel/sql
|
|
154
|
+
shrine
|
|
155
|
+
].each { |file| require "skylight/normalizers/#{file}" }
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
module Probes
|
|
3
|
+
module ActionController
|
|
4
|
+
class Probe
|
|
5
|
+
def install
|
|
6
|
+
# Prepending doesn't work here since this a module that's already been included
|
|
7
|
+
::ActionController::Instrumentation.class_eval do
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
alias_method :append_info_to_payload_without_sk, :append_info_to_payload
|
|
11
|
+
def append_info_to_payload(payload)
|
|
12
|
+
append_info_to_payload_without_sk(payload)
|
|
13
|
+
|
|
14
|
+
payload[:sk_rendered_format] = sk_rendered_mime.try(:ref)
|
|
15
|
+
payload[:sk_variant] = request.respond_to?(:variant) ? request.variant : nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def sk_rendered_mime
|
|
19
|
+
if respond_to?(:media_type)
|
|
20
|
+
mt = media_type
|
|
21
|
+
return mt && Mime::Type.lookup(mt)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
if content_type.is_a?(Mime::Type)
|
|
25
|
+
content_type
|
|
26
|
+
elsif content_type.respond_to?(:to_s)
|
|
27
|
+
type_str = content_type.to_s.split(";").first
|
|
28
|
+
Mime::Type.lookup(type_str) unless type_str.blank?
|
|
29
|
+
elsif respond_to?(:rendered_format) && rendered_format
|
|
30
|
+
rendered_format
|
|
31
|
+
end
|
|
32
|
+
rescue StandardError
|
|
33
|
+
# There are cases in which actionpack can return
|
|
34
|
+
# a stringified representation of a Mime::NullType instance,
|
|
35
|
+
# which is invalid for a number of reasons. This string raises
|
|
36
|
+
# errors when piped through Mime::Type.lookup, so it's probably
|
|
37
|
+
# best to just return nil in those cases.
|
|
38
|
+
nil
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
register(
|
|
46
|
+
:action_controller,
|
|
47
|
+
"ActionController::Instrumentation",
|
|
48
|
+
"action_controller/metal/instrumentation",
|
|
49
|
+
ActionController::Probe.new
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
module Probes
|
|
3
|
+
module ActionDispatch
|
|
4
|
+
module RequestId
|
|
5
|
+
module Instrumentation
|
|
6
|
+
def call(env)
|
|
7
|
+
@skylight_request_id = env["skylight.request_id"]
|
|
8
|
+
super
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def internal_request_id
|
|
14
|
+
@skylight_request_id || super
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class Probe
|
|
19
|
+
def install
|
|
20
|
+
::ActionDispatch::RequestId.prepend(Instrumentation)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
register(
|
|
27
|
+
:action_dispatch,
|
|
28
|
+
"ActionDispatch::RequestId",
|
|
29
|
+
"action_dispatch/middleware/request_id",
|
|
30
|
+
ActionDispatch::RequestId::Probe.new
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Skylight
|
|
4
|
+
module Probes
|
|
5
|
+
module ActionDispatch
|
|
6
|
+
module Routing
|
|
7
|
+
module RouteSet
|
|
8
|
+
module Instrumentation
|
|
9
|
+
def call(env)
|
|
10
|
+
ActiveSupport::Notifications.instrument("route_set.action_dispatch") { super }
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class Probe
|
|
15
|
+
def install
|
|
16
|
+
::ActionDispatch::Routing::RouteSet.prepend(Instrumentation)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
register(
|
|
24
|
+
:rails_router,
|
|
25
|
+
"ActionDispatch::Routing::RouteSet",
|
|
26
|
+
"action_dispatch/routing/route_set",
|
|
27
|
+
ActionDispatch::Routing::RouteSet::Probe.new
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|