elastic-apm 3.5.0 → 3.6.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/Jenkinsfile +141 -121
- data/.ci/docker/jruby/11-jdk/Dockerfile +40 -0
- data/.ci/docker/jruby/12-jdk/Dockerfile +40 -0
- data/.ci/docker/jruby/13-jdk/Dockerfile +40 -0
- data/.ci/docker/jruby/7-jdk/Dockerfile +40 -0
- data/.ci/docker/jruby/8-jdk/Dockerfile +40 -0
- data/.ci/docker/jruby/README.md +31 -0
- data/.ci/docker/jruby/run.sh +73 -0
- data/.ci/docker/jruby/test.sh +13 -0
- data/.gitignore +3 -1
- data/.rubocop.yml +11 -2
- data/CHANGELOG.asciidoc +16 -0
- data/CONTRIBUTING.md +6 -4
- data/Gemfile +14 -9
- data/Rakefile +10 -5
- data/docker-compose.yml +5 -0
- data/docs/api.asciidoc +14 -2
- data/docs/configuration.asciidoc +5 -11
- data/docs/graphql.asciidoc +23 -0
- data/docs/index.asciidoc +4 -0
- data/docs/supported-technologies.asciidoc +51 -1
- data/docs/upgrading.asciidoc +45 -0
- data/lib/elastic_apm.rb +12 -0
- data/lib/elastic_apm/config.rb +7 -1
- data/lib/elastic_apm/graphql.rb +74 -0
- data/lib/elastic_apm/grpc.rb +82 -0
- data/lib/elastic_apm/resque.rb +12 -0
- data/lib/elastic_apm/span/context/destination.rb +20 -4
- data/lib/elastic_apm/spies/resque.rb +43 -0
- data/lib/elastic_apm/sql.rb +4 -4
- data/lib/elastic_apm/stacktrace_builder.rb +6 -1
- data/lib/elastic_apm/trace_context.rb +34 -11
- data/lib/elastic_apm/transport/serializers/span_serializer.rb +3 -1
- data/lib/elastic_apm/version.rb +1 -1
- metadata +16 -3
- data/CHANGELOG.md +0 -1
@@ -0,0 +1,45 @@
|
|
1
|
+
[[upgrading]]
|
2
|
+
== Upgrading
|
3
|
+
Upgrades between minor versions of the agent, like from 2.1 to 2.2 are always backwards compatible.
|
4
|
+
Upgrades that involve a major version bump often come with some backwards incompatible changes.
|
5
|
+
|
6
|
+
Before upgrading the agent, be sure to review the:
|
7
|
+
|
8
|
+
* <<release-notes,Agent release notes>>
|
9
|
+
* {apm-overview-ref-v}/agent-server-compatibility.html[Agent and Server compatibility chart]
|
10
|
+
|
11
|
+
[float]
|
12
|
+
[[end-of-life-dates]]
|
13
|
+
=== End of life dates
|
14
|
+
|
15
|
+
We love all our products, but sometimes we must say goodbye to a release so that we can continue moving
|
16
|
+
forward on future development and innovation.
|
17
|
+
Our https://www.elastic.co/support/eol[End of life policy] defines how long a given release is considered supported,
|
18
|
+
as well as how long a release is considered still in active development or maintenance.
|
19
|
+
The table below is a simplified description of this policy.
|
20
|
+
|
21
|
+
[options="header"]
|
22
|
+
|====
|
23
|
+
|Agent version |EOL Date |Maintained until
|
24
|
+
|3.5.x |2021-07-17 | 3.6
|
25
|
+
|3.4.x |2021-07-10 | 3.5
|
26
|
+
|3.3.x |2021-06-04 | 3.4
|
27
|
+
|3.2.x |2021-05-19 | 3.3
|
28
|
+
|3.1.x |2021-04-21 | 3.2
|
29
|
+
|3.0.x |2021-04-08 | 3.1
|
30
|
+
|2.12.x |2021-04-01 |4.0
|
31
|
+
|2.11.x |2021-03-23 |2.12
|
32
|
+
|2.10.x |2021-03-03 |2.11
|
33
|
+
|2.9.x |2020-12-25 |2.10
|
34
|
+
|2.8.x |2020-11-20 |2.9
|
35
|
+
|2.7.x |2020-11-07 |2.8
|
36
|
+
|2.6.x |2020-09-19 |2.7
|
37
|
+
|2.5.x |2020-09-01 |2.6
|
38
|
+
|2.4.x |2020-08-27 |2.5
|
39
|
+
|2.3.x |2020-07-29 |2.4
|
40
|
+
|2.2.x |2020-07-22 |2.3
|
41
|
+
|2.1.x |2020-06-04 |2.2
|
42
|
+
|2.0.x |2020-05-14 |2.1
|
43
|
+
|1.1.x |2020-03-07 |3.0
|
44
|
+
|1.0.x |2019-12-29 |1.1
|
45
|
+
|====
|
data/lib/elastic_apm.rb
CHANGED
@@ -22,10 +22,12 @@ require 'elastic_apm/instrumenter'
|
|
22
22
|
require 'elastic_apm/util'
|
23
23
|
|
24
24
|
require 'elastic_apm/middleware'
|
25
|
+
require 'elastic_apm/graphql'
|
25
26
|
|
26
27
|
require 'elastic_apm/rails' if defined?(::Rails::Railtie)
|
27
28
|
require 'elastic_apm/sinatra' if defined?(::Sinatra)
|
28
29
|
require 'elastic_apm/grape' if defined?(::Grape)
|
30
|
+
require 'elastic_apm/grpc' if defined?(::GRPC)
|
29
31
|
|
30
32
|
# ElasticAPM
|
31
33
|
module ElasticAPM
|
@@ -45,6 +47,16 @@ module ElasticAPM
|
|
45
47
|
Agent.stop
|
46
48
|
end
|
47
49
|
|
50
|
+
# Restarts the ElasticAPM Agent using the same config or a new
|
51
|
+
# one, if it is provided.
|
52
|
+
# Starts the agent if it is not running.
|
53
|
+
# Stops and starts the agent if it is running.
|
54
|
+
def restart(config = {})
|
55
|
+
config ||= agent&.config
|
56
|
+
stop if running?
|
57
|
+
start(config)
|
58
|
+
end
|
59
|
+
|
48
60
|
# @return [Boolean] Whether there's an [Agent] running
|
49
61
|
def running?
|
50
62
|
Agent.running?
|
data/lib/elastic_apm/config.rb
CHANGED
@@ -70,7 +70,7 @@ module ElasticAPM
|
|
70
70
|
option :transaction_max_spans, type: :int, default: 500
|
71
71
|
option :transaction_sample_rate, type: :float, default: 1.0
|
72
72
|
option :use_elastic_traceparent_header, type: :bool, default: true
|
73
|
-
option :
|
73
|
+
option :use_legacy_sql_parser, type: :bool, default: false
|
74
74
|
option :verify_server_cert, type: :bool, default: true
|
75
75
|
|
76
76
|
# rubocop:enable Metrics/LineLength, Layout/ExtraSpacing
|
@@ -119,6 +119,7 @@ module ElasticAPM
|
|
119
119
|
net_http
|
120
120
|
rake
|
121
121
|
redis
|
122
|
+
resque
|
122
123
|
sequel
|
123
124
|
shoryuken
|
124
125
|
sidekiq
|
@@ -221,6 +222,11 @@ module ElasticAPM
|
|
221
222
|
self.disable_instrumentations = value
|
222
223
|
end
|
223
224
|
|
225
|
+
def use_experimental_sql_parser=(value)
|
226
|
+
warn '[DEPRECATED] The new SQL parser is now the default. To use the old one, '
|
227
|
+
'use use_legacy_sql_parser and please report why you wish to do so.'
|
228
|
+
end
|
229
|
+
|
224
230
|
private
|
225
231
|
|
226
232
|
def load_config_file
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElasticAPM
|
4
|
+
# @api private
|
5
|
+
module GraphQL
|
6
|
+
TYPE = 'app'
|
7
|
+
SUBTYPE = 'graphql'
|
8
|
+
|
9
|
+
PREFIX = 'GraphQL: '
|
10
|
+
UNNAMED = '[unnamed]'
|
11
|
+
MULTIPLE_QUERIES = '[multiple-queries]'
|
12
|
+
CONCATENATOR = '+'
|
13
|
+
MAX_NUMBER_OF_QUERIES_FOR_NAME = 5
|
14
|
+
|
15
|
+
KEYS_TO_NAME = {
|
16
|
+
'lex' => 'graphql.lex',
|
17
|
+
'parse' => 'graphql.parse',
|
18
|
+
'validate' => 'graphql.validate',
|
19
|
+
'analyze_multiplex' => 'graphql.analyze_multiplex',
|
20
|
+
'analyze_query' => 'graphql.analyze_query',
|
21
|
+
'execute_multiplex' => 'graphql.execute_multiplex',
|
22
|
+
'execute_query' => 'graphql.execute_query',
|
23
|
+
'execute_query_lazy' => 'graphql.execute_query_lazy',
|
24
|
+
'authorized_lazy' => 'graphql.authorized_lazy',
|
25
|
+
'resolve_type' => 'graphql.resolve_type',
|
26
|
+
'resolve_type_lazy' => 'graphql.resolve_type_lazy'
|
27
|
+
|
28
|
+
# These are available but deliberately left out as they are mostly noise.
|
29
|
+
# "execute_field" => "graphql.execute_field",
|
30
|
+
# "execute_field_lazy" => "graphql.execute_field_lazy",
|
31
|
+
# "authorized" => "graphql.authorized",
|
32
|
+
}.freeze
|
33
|
+
|
34
|
+
def self.trace(key, data)
|
35
|
+
return yield unless KEYS_TO_NAME.key?(key)
|
36
|
+
return yield unless (transaction = ElasticAPM.current_transaction)
|
37
|
+
|
38
|
+
if key == 'execute_query'
|
39
|
+
transaction.name =
|
40
|
+
"#{PREFIX}#{query_name(data[:query])}"
|
41
|
+
end
|
42
|
+
|
43
|
+
results =
|
44
|
+
ElasticAPM.with_span(
|
45
|
+
KEYS_TO_NAME.fetch(key, key),
|
46
|
+
TYPE, subtype: SUBTYPE, action: key
|
47
|
+
) { yield }
|
48
|
+
|
49
|
+
if key == 'execute_multiplex'
|
50
|
+
transaction.name = "#{PREFIX}#{concat_names(results)}"
|
51
|
+
end
|
52
|
+
|
53
|
+
results
|
54
|
+
end
|
55
|
+
|
56
|
+
class << self
|
57
|
+
private
|
58
|
+
|
59
|
+
def query_name(query)
|
60
|
+
query.operation_name || UNNAMED
|
61
|
+
end
|
62
|
+
|
63
|
+
def concat_names(results)
|
64
|
+
if results.length > MAX_NUMBER_OF_QUERIES_FOR_NAME
|
65
|
+
return MULTIPLE_QUERIES
|
66
|
+
end
|
67
|
+
|
68
|
+
results.map do |result|
|
69
|
+
query_name(result.query)
|
70
|
+
end.join(CONCATENATOR)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElasticAPM
|
4
|
+
# @api private
|
5
|
+
class GRPC
|
6
|
+
# @api private
|
7
|
+
class ClientInterceptor < ::GRPC::ClientInterceptor
|
8
|
+
TYPE = 'external'
|
9
|
+
SUBTYPE = 'grpc'
|
10
|
+
|
11
|
+
# rubocop:disable Lint/UnusedMethodArgument
|
12
|
+
def request_response(request:, call:, method:, metadata:)
|
13
|
+
return yield unless (transaction = ElasticAPM.current_transaction)
|
14
|
+
if (trace_context = transaction.trace_context)
|
15
|
+
trace_context.apply_headers { |k, v| metadata[k.downcase] = v }
|
16
|
+
end
|
17
|
+
|
18
|
+
ElasticAPM.with_span(
|
19
|
+
method, TYPE,
|
20
|
+
subtype: SUBTYPE,
|
21
|
+
context: span_context(call)
|
22
|
+
) do
|
23
|
+
yield
|
24
|
+
end
|
25
|
+
end
|
26
|
+
# rubocop:enable Lint/UnusedMethodArgument
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def span_context(call)
|
31
|
+
peer = call.instance_variable_get(:@wrapped)&.peer
|
32
|
+
return unless peer
|
33
|
+
|
34
|
+
split_peer = URI.split(peer)
|
35
|
+
destination = ElasticAPM::Span::Context::Destination.new(
|
36
|
+
type: TYPE,
|
37
|
+
name: SUBTYPE,
|
38
|
+
resource: peer,
|
39
|
+
address: split_peer[0],
|
40
|
+
port: split_peer[6]
|
41
|
+
)
|
42
|
+
ElasticAPM::Span::Context.new(destination: destination)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# @api private
|
47
|
+
class ServerInterceptor < ::GRPC::ClientInterceptor
|
48
|
+
TYPE = 'request'
|
49
|
+
|
50
|
+
# rubocop:disable Lint/UnusedMethodArgument
|
51
|
+
def request_response(request:, call:, method:)
|
52
|
+
transaction = start_transaction(call)
|
53
|
+
yield
|
54
|
+
transaction.done 'success'
|
55
|
+
rescue ::Exception => e
|
56
|
+
ElasticAPM.report(e, handled: false)
|
57
|
+
transaction.done 'error'
|
58
|
+
raise
|
59
|
+
ensure
|
60
|
+
ElasticAPM.end_transaction
|
61
|
+
end
|
62
|
+
# rubocop:enable Lint/UnusedMethodArgument
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def start_transaction(call)
|
67
|
+
ElasticAPM.start_transaction(
|
68
|
+
'grpc',
|
69
|
+
'request',
|
70
|
+
trace_context: trace_context(call)
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
def trace_context(call)
|
75
|
+
TraceContext.parse(metadata: call.metadata)
|
76
|
+
rescue TraceContext::InvalidTraceparentHeader
|
77
|
+
warn "Couldn't parse invalid trace context header: #{header.inspect}"
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElasticAPM
|
4
|
+
# Defines a before_first_fork hook for starting the ElasticAPM agent
|
5
|
+
# with Resque.
|
6
|
+
module Resque
|
7
|
+
::Resque.before_first_fork do
|
8
|
+
::Resque.logger.debug('Starting ElasticAPM agent')
|
9
|
+
ElasticAPM.start
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -5,21 +5,37 @@ module ElasticAPM
|
|
5
5
|
class Context
|
6
6
|
# @api private
|
7
7
|
class Destination
|
8
|
-
def initialize(
|
8
|
+
def initialize(
|
9
|
+
name: nil,
|
10
|
+
resource: nil,
|
11
|
+
type: nil,
|
12
|
+
address: nil,
|
13
|
+
port: nil
|
14
|
+
)
|
9
15
|
@name = name
|
10
16
|
@resource = resource
|
11
17
|
@type = type
|
18
|
+
@address = address
|
19
|
+
@port = port
|
12
20
|
end
|
13
21
|
|
14
|
-
attr_reader
|
22
|
+
attr_reader(
|
23
|
+
:name,
|
24
|
+
:resource,
|
25
|
+
:type,
|
26
|
+
:address,
|
27
|
+
:port
|
28
|
+
)
|
15
29
|
|
16
|
-
def self.from_uri(uri_or_str, type: 'external')
|
30
|
+
def self.from_uri(uri_or_str, type: 'external', port: nil)
|
17
31
|
uri = normalize(uri_or_str)
|
18
32
|
|
19
33
|
new(
|
20
34
|
name: only_scheme_and_host(uri),
|
21
35
|
resource: "#{uri.host}:#{uri.port}",
|
22
|
-
type: type
|
36
|
+
type: type,
|
37
|
+
address: uri.hostname,
|
38
|
+
port: port || uri.port
|
23
39
|
)
|
24
40
|
end
|
25
41
|
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElasticAPM
|
4
|
+
# @api private
|
5
|
+
module Spies
|
6
|
+
# @api private
|
7
|
+
class ResqueSpy
|
8
|
+
TYPE = 'Resque'
|
9
|
+
|
10
|
+
def install
|
11
|
+
install_perform_spy
|
12
|
+
install_after_fork_hook
|
13
|
+
end
|
14
|
+
|
15
|
+
def install_after_fork_hook
|
16
|
+
::Resque.after_fork do
|
17
|
+
ElasticAPM.restart
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def install_perform_spy
|
22
|
+
::Resque::Job.class_eval do
|
23
|
+
alias :perform_without_elastic_apm :perform
|
24
|
+
|
25
|
+
def perform
|
26
|
+
name = @payload && @payload['class']&.name
|
27
|
+
transaction = ElasticAPM.start_transaction(name, TYPE)
|
28
|
+
perform_without_elastic_apm
|
29
|
+
transaction.done 'success'
|
30
|
+
rescue ::Exception => e
|
31
|
+
ElasticAPM.report(e, handled: false)
|
32
|
+
transaction.done 'error'
|
33
|
+
raise
|
34
|
+
ensure
|
35
|
+
ElasticAPM.end_transaction
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
register 'Resque', 'resque', ResqueSpy.new
|
42
|
+
end
|
43
|
+
end
|
data/lib/elastic_apm/sql.rb
CHANGED
@@ -7,12 +7,12 @@ module ElasticAPM
|
|
7
7
|
# both implementations ~mikker
|
8
8
|
def self.summarizer
|
9
9
|
@summarizer ||=
|
10
|
-
if ElasticAPM.agent&.config&.
|
11
|
-
require 'elastic_apm/sql/signature'
|
12
|
-
Sql::Signature::Summarizer.new
|
13
|
-
else
|
10
|
+
if ElasticAPM.agent&.config&.use_legacy_sql_parser
|
14
11
|
require 'elastic_apm/sql_summarizer'
|
15
12
|
SqlSummarizer.new
|
13
|
+
else
|
14
|
+
require 'elastic_apm/sql/signature'
|
15
|
+
Sql::Signature::Summarizer.new
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
@@ -12,7 +12,12 @@ module ElasticAPM
|
|
12
12
|
RUBY_VERS_REGEX = %r{ruby(/gems)?[-/](\d+\.)+\d}.freeze
|
13
13
|
JRUBY_ORG_REGEX = %r{org/jruby}.freeze
|
14
14
|
|
15
|
-
GEMS_PATH =
|
15
|
+
GEMS_PATH =
|
16
|
+
if defined?(Bundler) && Bundler.default_bundle_dir
|
17
|
+
Bundler.bundle_path.to_s
|
18
|
+
else
|
19
|
+
Gem.dir
|
20
|
+
end
|
16
21
|
|
17
22
|
def initialize(config)
|
18
23
|
@config = config
|
@@ -25,15 +25,30 @@ module ElasticAPM
|
|
25
25
|
:version, :trace_id, :id, :parent_id, :ensure_parent_id, :recorded?
|
26
26
|
|
27
27
|
class << self
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
29
|
+
def parse(legacy_header = nil, env: nil, metadata: nil)
|
30
|
+
unless legacy_header || env || metadata
|
31
|
+
raise ArgumentError, 'TraceContext expects env:, metadata: ' \
|
32
|
+
'or single argument header string'
|
32
33
|
end
|
33
34
|
|
34
|
-
|
35
|
+
if legacy_header
|
36
|
+
legacy_parse_from_header(legacy_header)
|
37
|
+
elsif env
|
38
|
+
trace_context_from_env(env)
|
39
|
+
elsif metadata
|
40
|
+
trace_context_from_metadata(metadata)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
44
|
+
|
45
|
+
private
|
35
46
|
|
36
|
-
|
47
|
+
def trace_context_from_env(env)
|
48
|
+
return unless (
|
49
|
+
header =
|
50
|
+
env['HTTP_ELASTIC_APM_TRACEPARENT'] || env['HTTP_TRACEPARENT']
|
51
|
+
)
|
37
52
|
|
38
53
|
parent = TraceContext::Traceparent.parse(header)
|
39
54
|
|
@@ -45,16 +60,24 @@ module ElasticAPM
|
|
45
60
|
new(traceparent: parent, tracestate: state)
|
46
61
|
end
|
47
62
|
|
48
|
-
|
63
|
+
def trace_context_from_metadata(metadata)
|
64
|
+
return unless (header = metadata['elastic-apm-traceparent'] ||
|
65
|
+
metadata['traceparent'])
|
66
|
+
|
67
|
+
parent = TraceContext::Traceparent.parse(header)
|
68
|
+
|
69
|
+
state =
|
70
|
+
if (header = metadata['tracestate'])
|
71
|
+
TraceContext::Tracestate.parse(header)
|
72
|
+
end
|
73
|
+
|
74
|
+
new(traceparent: parent, tracestate: state)
|
75
|
+
end
|
49
76
|
|
50
77
|
def legacy_parse_from_header(header)
|
51
78
|
parent = Traceparent.parse(header)
|
52
79
|
new(traceparent: parent)
|
53
80
|
end
|
54
|
-
|
55
|
-
def get_traceparent_header(env)
|
56
|
-
env['HTTP_ELASTIC_APM_TRACEPARENT'] || env['HTTP_TRACEPARENT']
|
57
|
-
end
|
58
81
|
end
|
59
82
|
|
60
83
|
def child
|