elastic-apm 3.3.0 → 3.4.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.
- checksums.yaml +4 -4
- data/.ci/.jenkins_exclude.yml +4 -4
- data/.ci/.jenkins_ruby.yml +1 -1
- data/.ci/Jenkinsfile +5 -3
- data/.ci/jobs/apm-agent-ruby-downstream.yml +1 -0
- data/.ci/jobs/apm-agent-ruby-linting-mbp.yml +1 -0
- data/.ci/jobs/apm-agent-ruby-mbp.yml +1 -0
- data/.ci/prepare-git-context.sh +5 -2
- data/.github/ISSUE_TEMPLATE/Bug_report.md +38 -0
- data/.github/ISSUE_TEMPLATE/Feature_request.md +17 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +14 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +6 -0
- data/CHANGELOG.asciidoc +25 -1
- data/Gemfile +6 -2
- data/bench/sql.rb +49 -0
- data/bin/build_docs +1 -1
- data/codecov.yml +32 -0
- data/docs/api.asciidoc +37 -0
- data/docs/configuration.asciidoc +18 -1
- data/docs/supported-technologies.asciidoc +20 -1
- data/lib/elastic_apm.rb +29 -5
- data/lib/elastic_apm/agent.rb +6 -2
- data/lib/elastic_apm/child_durations.rb +9 -4
- data/lib/elastic_apm/config.rb +8 -1
- data/lib/elastic_apm/config/options.rb +3 -4
- data/lib/elastic_apm/context/response.rb +10 -2
- data/lib/elastic_apm/instrumenter.rb +20 -11
- data/lib/elastic_apm/normalizers/rails/active_record.rb +12 -5
- data/lib/elastic_apm/rails.rb +1 -10
- data/lib/elastic_apm/railtie.rb +1 -1
- data/lib/elastic_apm/span.rb +3 -2
- data/lib/elastic_apm/span/context.rb +26 -44
- data/lib/elastic_apm/span/context/db.rb +19 -0
- data/lib/elastic_apm/span/context/destination.rb +44 -0
- data/lib/elastic_apm/span/context/http.rb +26 -0
- data/lib/elastic_apm/spies/elasticsearch.rb +18 -5
- data/lib/elastic_apm/spies/faraday.rb +36 -18
- data/lib/elastic_apm/spies/http.rb +16 -2
- data/lib/elastic_apm/spies/mongo.rb +5 -0
- data/lib/elastic_apm/spies/net_http.rb +27 -7
- data/lib/elastic_apm/spies/sequel.rb +25 -15
- data/lib/elastic_apm/spies/shoryuken.rb +48 -0
- data/lib/elastic_apm/spies/sneakers.rb +57 -0
- data/lib/elastic_apm/sql.rb +19 -0
- data/lib/elastic_apm/sql/signature.rb +152 -0
- data/lib/elastic_apm/sql/tokenizer.rb +247 -0
- data/lib/elastic_apm/sql/tokens.rb +46 -0
- data/lib/elastic_apm/sql_summarizer.rb +1 -2
- data/lib/elastic_apm/transaction.rb +11 -11
- data/lib/elastic_apm/transport/connection/proxy_pipe.rb +2 -2
- data/lib/elastic_apm/transport/headers.rb +4 -0
- data/lib/elastic_apm/transport/serializers/span_serializer.rb +24 -7
- data/lib/elastic_apm/version.rb +1 -1
- metadata +16 -3
- data/.github/workflows/main.yml +0 -14
@@ -4,53 +4,35 @@ module ElasticAPM
|
|
4
4
|
class Span
|
5
5
|
# @api private
|
6
6
|
class Context
|
7
|
-
def initialize(
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
def initialize(
|
8
|
+
db: nil,
|
9
|
+
destination: nil,
|
10
|
+
http: nil,
|
11
|
+
labels: {},
|
12
|
+
sync: nil
|
13
|
+
)
|
14
|
+
@sync = sync
|
15
|
+
@db = db && Db.new(**db)
|
16
|
+
@http = http && Http.new(**http)
|
17
|
+
@destination =
|
18
|
+
case destination
|
19
|
+
when Destination then destination
|
20
|
+
when Hash then Destination.new(**destination)
|
21
|
+
end
|
11
22
|
@labels = labels
|
12
23
|
end
|
13
24
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
@type = type
|
22
|
-
@user = user
|
23
|
-
end
|
24
|
-
|
25
|
-
attr_accessor :instance, :statement, :type, :user
|
26
|
-
end
|
27
|
-
|
28
|
-
# @api private
|
29
|
-
class Http
|
30
|
-
def initialize(url: nil, status_code: nil, method: nil)
|
31
|
-
@url = sanitize_url(url)
|
32
|
-
@status_code = status_code
|
33
|
-
@method = method
|
34
|
-
end
|
35
|
-
|
36
|
-
attr_accessor :url, :status_code, :method
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
def sanitize_url(url)
|
41
|
-
uri = URI(url)
|
42
|
-
|
43
|
-
return url unless uri.userinfo
|
44
|
-
|
45
|
-
format(
|
46
|
-
'%s://%s@%s%s',
|
47
|
-
uri.scheme,
|
48
|
-
uri.user,
|
49
|
-
uri.hostname,
|
50
|
-
uri.path
|
51
|
-
)
|
52
|
-
end
|
53
|
-
end
|
25
|
+
attr_reader(
|
26
|
+
:db,
|
27
|
+
:destination,
|
28
|
+
:http,
|
29
|
+
:labels,
|
30
|
+
:sync
|
31
|
+
)
|
54
32
|
end
|
55
33
|
end
|
56
34
|
end
|
35
|
+
|
36
|
+
require 'elastic_apm/span/context/db'
|
37
|
+
require 'elastic_apm/span/context/http'
|
38
|
+
require 'elastic_apm/span/context/destination'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElasticAPM
|
4
|
+
class Span
|
5
|
+
class Context
|
6
|
+
# @api private
|
7
|
+
class Db
|
8
|
+
def initialize(instance: nil, statement: nil, type: nil, user: nil)
|
9
|
+
@instance = instance
|
10
|
+
@statement = statement
|
11
|
+
@type = type
|
12
|
+
@user = user
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_accessor :instance, :statement, :type, :user
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElasticAPM
|
4
|
+
class Span
|
5
|
+
class Context
|
6
|
+
# @api private
|
7
|
+
class Destination
|
8
|
+
def initialize(name: nil, resource: nil, type: nil)
|
9
|
+
@name = name
|
10
|
+
@resource = resource
|
11
|
+
@type = type
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :name, :resource, :type
|
15
|
+
|
16
|
+
def self.from_uri(uri_or_str, type: 'external')
|
17
|
+
uri = normalize(uri_or_str)
|
18
|
+
|
19
|
+
new(
|
20
|
+
name: only_scheme_and_host(uri),
|
21
|
+
resource: "#{uri.host}:#{uri.port}",
|
22
|
+
type: type
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.only_scheme_and_host(uri_or_str)
|
27
|
+
uri = normalize(uri_or_str)
|
28
|
+
uri.path = ''
|
29
|
+
uri.password = uri.query = uri.fragment = nil
|
30
|
+
uri.to_s
|
31
|
+
end
|
32
|
+
|
33
|
+
class << self
|
34
|
+
private
|
35
|
+
|
36
|
+
def normalize(uri_or_str)
|
37
|
+
return uri_or_str.dup if uri_or_str.is_a?(URI)
|
38
|
+
URI(uri_or_str)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElasticAPM
|
4
|
+
class Span
|
5
|
+
class Context
|
6
|
+
# @api private
|
7
|
+
class Http
|
8
|
+
def initialize(url: nil, status_code: nil, method: nil)
|
9
|
+
@url = sanitize_url(url)
|
10
|
+
@status_code = status_code
|
11
|
+
@method = method
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_accessor :url, :status_code, :method
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def sanitize_url(uri_or_str)
|
19
|
+
uri = uri_or_str.is_a?(URI) ? uri_or_str.dup : URI(uri_or_str)
|
20
|
+
uri.password = nil
|
21
|
+
uri.to_s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -6,7 +6,9 @@ module ElasticAPM
|
|
6
6
|
# @api private
|
7
7
|
class ElasticsearchSpy
|
8
8
|
NAME_FORMAT = '%s %s'
|
9
|
-
TYPE = 'db
|
9
|
+
TYPE = 'db'
|
10
|
+
SUBTYPE = 'elasticsearch'
|
11
|
+
|
10
12
|
def install
|
11
13
|
::Elasticsearch::Transport::Client.class_eval do
|
12
14
|
alias perform_request_without_apm perform_request
|
@@ -14,11 +16,22 @@ module ElasticAPM
|
|
14
16
|
def perform_request(method, path, *args, &block)
|
15
17
|
name = format(NAME_FORMAT, method, path)
|
16
18
|
statement = args[0].is_a?(String) ? args[0] : args[0].to_json
|
17
|
-
context = Span::Context.new(db: { statement: statement })
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
context = Span::Context.new(
|
21
|
+
db: { statement: statement },
|
22
|
+
destination: {
|
23
|
+
name: SUBTYPE,
|
24
|
+
resource: SUBTYPE,
|
25
|
+
type: TYPE
|
26
|
+
}
|
27
|
+
)
|
28
|
+
|
29
|
+
ElasticAPM.with_span(
|
30
|
+
name,
|
31
|
+
TYPE,
|
32
|
+
subtype: SUBTYPE,
|
33
|
+
context: context
|
34
|
+
) { perform_request_without_apm(method, path, *args, &block) }
|
22
35
|
end
|
23
36
|
end
|
24
37
|
end
|
@@ -16,7 +16,6 @@ module ElasticAPM
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
20
19
|
# rubocop:disable Metrics/CyclomaticComplexity
|
21
20
|
def install
|
22
21
|
::Faraday::Connection.class_eval do
|
@@ -27,40 +26,59 @@ module ElasticAPM
|
|
27
26
|
return run_request_without_apm(method, url, body, headers, &block)
|
28
27
|
end
|
29
28
|
|
30
|
-
|
31
|
-
url_prefix.host
|
32
|
-
elsif url.nil?
|
33
|
-
tmp_request = build_request(method) do |req|
|
34
|
-
yield(req) if block_given?
|
35
|
-
end
|
36
|
-
URI(tmp_request.path).host
|
37
|
-
else
|
38
|
-
URI(url).host
|
39
|
-
end
|
29
|
+
uri = URI(build_url(url))
|
40
30
|
|
41
|
-
|
31
|
+
# If url is set inside block it isn't available until yield,
|
32
|
+
# so we temporarily build the request to yield. This could be a
|
33
|
+
# problem if the block has side effects as it will be yielded twice
|
34
|
+
# ~mikker
|
35
|
+
unless uri.host
|
36
|
+
tmp_request = build_request(method) do |req|
|
37
|
+
yield(req) if block_given?
|
38
|
+
end
|
39
|
+
uri = URI(tmp_request.path)
|
40
|
+
end
|
41
|
+
|
42
|
+
host = uri.host
|
43
|
+
|
44
|
+
upcased_method = method.to_s.upcase
|
45
|
+
|
46
|
+
destination = ElasticAPM::Span::Context::Destination.from_uri(uri)
|
47
|
+
|
48
|
+
context =
|
49
|
+
ElasticAPM::Span::Context.new(
|
50
|
+
http: { url: uri, method: upcased_method },
|
51
|
+
destination: destination
|
52
|
+
)
|
42
53
|
|
43
54
|
ElasticAPM.with_span(
|
44
|
-
|
55
|
+
"#{upcased_method} #{host}",
|
45
56
|
TYPE,
|
46
57
|
subtype: SUBTYPE,
|
47
|
-
action:
|
58
|
+
action: upcased_method,
|
59
|
+
context: context
|
48
60
|
) do |span|
|
49
61
|
ElasticAPM::Spies::FaradaySpy.without_net_http do
|
50
62
|
trace_context = span&.trace_context || transaction.trace_context
|
51
63
|
|
52
|
-
|
53
|
-
|
64
|
+
result =
|
65
|
+
run_request_without_apm(method, url, body, headers) do |req|
|
66
|
+
req['Elastic-Apm-Traceparent'] = trace_context.to_header
|
54
67
|
|
55
|
-
|
68
|
+
yield req if block_given?
|
69
|
+
end
|
70
|
+
|
71
|
+
if (http = span&.context&.http)
|
72
|
+
http.status_code = result.status.to_s
|
56
73
|
end
|
74
|
+
|
75
|
+
result
|
57
76
|
end
|
58
77
|
end
|
59
78
|
end
|
60
79
|
end
|
61
80
|
end
|
62
81
|
# rubocop:enable Metrics/CyclomaticComplexity
|
63
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
64
82
|
end
|
65
83
|
|
66
84
|
register 'Faraday', 'faraday', FaradaySpy.new
|
@@ -19,17 +19,31 @@ module ElasticAPM
|
|
19
19
|
method = req.verb.to_s.upcase
|
20
20
|
host = req.uri.host
|
21
21
|
|
22
|
+
destination =
|
23
|
+
ElasticAPM::Span::Context::Destination.from_uri(req.uri)
|
24
|
+
context = ElasticAPM::Span::Context.new(
|
25
|
+
http: { url: req.uri, method: method },
|
26
|
+
destination: destination
|
27
|
+
)
|
28
|
+
|
22
29
|
name = "#{method} #{host}"
|
23
30
|
|
24
31
|
ElasticAPM.with_span(
|
25
32
|
name,
|
26
33
|
TYPE,
|
27
34
|
subtype: SUBTYPE,
|
28
|
-
action: method
|
35
|
+
action: method,
|
36
|
+
context: context
|
29
37
|
) do |span|
|
30
38
|
trace_context = span&.trace_context || transaction.trace_context
|
31
39
|
req['Elastic-Apm-Traceparent'] = trace_context.to_header
|
32
|
-
perform_without_apm(req, options)
|
40
|
+
result = perform_without_apm(req, options)
|
41
|
+
|
42
|
+
if (http = span&.context&.http)
|
43
|
+
http.status_code = result.status.to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
result
|
33
47
|
end
|
34
48
|
end
|
35
49
|
end
|
@@ -8,6 +8,7 @@ module ElasticAPM
|
|
8
8
|
KEY = :__elastic_apm_net_http_disabled
|
9
9
|
TYPE = 'ext'
|
10
10
|
SUBTYPE = 'net_http'
|
11
|
+
|
11
12
|
class << self
|
12
13
|
def disabled=(disabled)
|
13
14
|
Thread.current[KEY] = disabled
|
@@ -28,6 +29,7 @@ module ElasticAPM
|
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
32
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
31
33
|
def install
|
32
34
|
Net::HTTP.class_eval do
|
33
35
|
alias request_without_apm request
|
@@ -36,30 +38,48 @@ module ElasticAPM
|
|
36
38
|
unless (transaction = ElasticAPM.current_transaction)
|
37
39
|
return request_without_apm(req, body, &block)
|
38
40
|
end
|
41
|
+
|
39
42
|
if ElasticAPM::Spies::NetHTTPSpy.disabled?
|
40
43
|
return request_without_apm(req, body, &block)
|
41
44
|
end
|
42
45
|
|
43
|
-
host
|
44
|
-
method = req.method
|
46
|
+
host = req['host']&.split(':')&.first || address
|
47
|
+
method = req.method.to_s.upcase
|
48
|
+
path, query = req.path.split('?')
|
49
|
+
|
50
|
+
cls = use_ssl? ? URI::HTTPS : URI::HTTP
|
51
|
+
uri = cls.build([nil, host, port, path, query, nil])
|
45
52
|
|
46
|
-
|
53
|
+
destination =
|
54
|
+
ElasticAPM::Span::Context::Destination.from_uri(uri)
|
47
55
|
|
48
|
-
|
56
|
+
context =
|
57
|
+
ElasticAPM::Span::Context.new(
|
58
|
+
http: { url: uri, method: method },
|
59
|
+
destination: destination
|
60
|
+
)
|
49
61
|
|
50
62
|
ElasticAPM.with_span(
|
51
|
-
|
63
|
+
"#{method} #{host}",
|
52
64
|
TYPE,
|
53
65
|
subtype: SUBTYPE,
|
54
|
-
action: method
|
66
|
+
action: method,
|
67
|
+
context: context
|
55
68
|
) do |span|
|
56
69
|
trace_context = span&.trace_context || transaction.trace_context
|
57
70
|
req['Elastic-Apm-Traceparent'] = trace_context.to_header
|
58
|
-
request_without_apm(req, body, &block)
|
71
|
+
result = request_without_apm(req, body, &block)
|
72
|
+
|
73
|
+
if (http = span&.context&.http)
|
74
|
+
http.status_code = result.code
|
75
|
+
end
|
76
|
+
|
77
|
+
result
|
59
78
|
end
|
60
79
|
end
|
61
80
|
end
|
62
81
|
end
|
82
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
63
83
|
end
|
64
84
|
|
65
85
|
register 'Net::HTTP', 'net/http', NetHTTPSpy.new
|
@@ -1,22 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'elastic_apm/
|
3
|
+
require 'elastic_apm/sql'
|
4
4
|
|
5
5
|
module ElasticAPM
|
6
6
|
# @api private
|
7
7
|
module Spies
|
8
8
|
# @api private
|
9
9
|
class SequelSpy
|
10
|
-
TYPE = 'db
|
10
|
+
TYPE = 'db'
|
11
|
+
ACTION = 'query'
|
11
12
|
|
12
13
|
def self.summarizer
|
13
|
-
@summarizer
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.build_context(sql, opts)
|
17
|
-
Span::Context.new(
|
18
|
-
db: { statement: sql, type: 'sql', user: opts[:user] }
|
19
|
-
)
|
14
|
+
@summarizer = Sql.summarizer
|
20
15
|
end
|
21
16
|
|
22
17
|
def install
|
@@ -25,16 +20,31 @@ module ElasticAPM
|
|
25
20
|
::Sequel::Database.class_eval do
|
26
21
|
alias log_connection_yield_without_apm log_connection_yield
|
27
22
|
|
28
|
-
def log_connection_yield(sql,
|
23
|
+
def log_connection_yield(sql, connection, args = nil, &block)
|
29
24
|
unless ElasticAPM.current_transaction
|
30
|
-
return log_connection_yield_without_apm(
|
25
|
+
return log_connection_yield_without_apm(
|
26
|
+
sql, connection, args, &block
|
27
|
+
)
|
31
28
|
end
|
32
29
|
|
33
|
-
|
34
|
-
|
35
|
-
|
30
|
+
subtype = database_type.to_s
|
31
|
+
|
32
|
+
name =
|
33
|
+
ElasticAPM::Spies::SequelSpy.summarizer.summarize sql
|
34
|
+
|
35
|
+
context = ElasticAPM::Span::Context.new(
|
36
|
+
db: { statement: sql, type: 'sql', user: opts[:user] },
|
37
|
+
destination: { name: subtype, resource: subtype, type: TYPE }
|
38
|
+
)
|
36
39
|
|
37
|
-
ElasticAPM.with_span(
|
40
|
+
ElasticAPM.with_span(
|
41
|
+
name,
|
42
|
+
TYPE,
|
43
|
+
subtype: subtype,
|
44
|
+
action: ACTION,
|
45
|
+
context: context,
|
46
|
+
&block
|
47
|
+
)
|
38
48
|
end
|
39
49
|
end
|
40
50
|
end
|