elastic-apm 3.15.1 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.ci/.jenkins_exclude.yml +13 -0
  3. data/.ci/.jenkins_ruby.yml +0 -1
  4. data/.ci/Jenkinsfile +1 -0
  5. data/.github/dependabot.yml +16 -0
  6. data/.rubocop.yml +0 -7
  7. data/CHANGELOG.asciidoc +77 -4
  8. data/Gemfile +4 -1
  9. data/README.md +12 -0
  10. data/SECURITY.md +7 -0
  11. data/bench/report.rb +1 -1
  12. data/bin/run-bdd +17 -0
  13. data/bin/run-tests +1 -1
  14. data/docker-compose.yml +1 -1
  15. data/docs/configuration.asciidoc +62 -147
  16. data/docs/release-notes.asciidoc +1 -0
  17. data/docs/upgrading.asciidoc +0 -27
  18. data/lib/elastic_apm.rb +12 -1
  19. data/lib/elastic_apm/agent.rb +7 -3
  20. data/lib/elastic_apm/central_config.rb +8 -7
  21. data/lib/elastic_apm/config.rb +2 -88
  22. data/lib/elastic_apm/config/regexp_list.rb +1 -1
  23. data/lib/elastic_apm/config/wildcard_pattern_list.rb +1 -1
  24. data/lib/elastic_apm/context/response.rb +1 -3
  25. data/lib/elastic_apm/fields.rb +88 -0
  26. data/lib/elastic_apm/grpc.rb +2 -4
  27. data/lib/elastic_apm/instrumenter.rb +1 -1
  28. data/lib/elastic_apm/metadata/cloud_info.rb +32 -5
  29. data/lib/elastic_apm/metadata/system_info.rb +14 -4
  30. data/lib/elastic_apm/metrics/cpu_mem_set.rb +1 -1
  31. data/lib/elastic_apm/normalizers.rb +2 -2
  32. data/lib/elastic_apm/normalizers/rails/active_record.rb +3 -3
  33. data/lib/elastic_apm/opentracing.rb +3 -2
  34. data/lib/elastic_apm/span.rb +26 -1
  35. data/lib/elastic_apm/span/context.rb +2 -1
  36. data/lib/elastic_apm/span/context/destination.rb +53 -40
  37. data/lib/elastic_apm/span_helpers.rb +6 -8
  38. data/lib/elastic_apm/spies.rb +20 -0
  39. data/lib/elastic_apm/spies/action_dispatch.rb +10 -9
  40. data/lib/elastic_apm/spies/azure_storage_table.rb +148 -0
  41. data/lib/elastic_apm/spies/dynamo_db.rb +12 -12
  42. data/lib/elastic_apm/spies/elasticsearch.rb +32 -29
  43. data/lib/elastic_apm/spies/faraday.rb +83 -63
  44. data/lib/elastic_apm/spies/http.rb +33 -34
  45. data/lib/elastic_apm/spies/mongo.rb +5 -3
  46. data/lib/elastic_apm/spies/net_http.rb +59 -56
  47. data/lib/elastic_apm/spies/rake.rb +28 -26
  48. data/lib/elastic_apm/spies/redis.rb +11 -10
  49. data/lib/elastic_apm/spies/resque.rb +18 -21
  50. data/lib/elastic_apm/spies/s3.rb +14 -15
  51. data/lib/elastic_apm/spies/sequel.rb +42 -48
  52. data/lib/elastic_apm/spies/sidekiq.rb +13 -15
  53. data/lib/elastic_apm/spies/sinatra.rb +20 -21
  54. data/lib/elastic_apm/spies/sns.rb +39 -44
  55. data/lib/elastic_apm/spies/sqs.rb +21 -31
  56. data/lib/elastic_apm/spies/tilt.rb +10 -9
  57. data/lib/elastic_apm/sql/tokenizer.rb +21 -5
  58. data/lib/elastic_apm/stacktrace_builder.rb +3 -1
  59. data/lib/elastic_apm/subscriber.rb +1 -0
  60. data/lib/elastic_apm/trace_context.rb +5 -13
  61. data/lib/elastic_apm/trace_context/traceparent.rb +5 -6
  62. data/lib/elastic_apm/transport/connection.rb +1 -1
  63. data/lib/elastic_apm/transport/connection/http.rb +4 -2
  64. data/lib/elastic_apm/transport/connection/proxy_pipe.rb +1 -2
  65. data/lib/elastic_apm/transport/filters/hash_sanitizer.rb +5 -23
  66. data/lib/elastic_apm/transport/serializers/metadata_serializer.rb +3 -2
  67. data/lib/elastic_apm/transport/serializers/metricset_serializer.rb +2 -2
  68. data/lib/elastic_apm/transport/serializers/span_serializer.rb +12 -12
  69. data/lib/elastic_apm/transport/user_agent.rb +3 -2
  70. data/lib/elastic_apm/transport/worker.rb +1 -1
  71. data/lib/elastic_apm/util/deep_dup.rb +1 -1
  72. data/lib/elastic_apm/version.rb +1 -1
  73. metadata +12 -9
  74. data/lib/elastic_apm/sql.rb +0 -36
  75. data/lib/elastic_apm/sql_summarizer.rb +0 -53
@@ -22,41 +22,43 @@ module ElasticAPM
22
22
  module Spies
23
23
  # @api private
24
24
  class RakeSpy
25
- def install
26
- ::Rake::Task.class_eval do
27
- alias execute_without_apm execute
28
-
29
- def execute(*args)
30
- agent = ElasticAPM.start
31
-
32
- unless agent && agent.config.instrumented_rake_tasks.include?(name)
33
- return execute_without_apm(*args)
34
- end
25
+ # @api private
26
+ module Ext
27
+ def execute(*args)
28
+ agent = ElasticAPM.start
35
29
 
36
- transaction =
37
- ElasticAPM.start_transaction("Rake::Task[#{name}]", 'Rake')
30
+ unless agent && agent.config.instrumented_rake_tasks.include?(name)
31
+ return super(*args)
32
+ end
38
33
 
39
- begin
40
- result = execute_without_apm(*args)
34
+ transaction =
35
+ ElasticAPM.start_transaction("Rake::Task[#{name}]", 'Rake')
41
36
 
42
- transaction&.result = 'success'
43
- transaction&.outcome = Transaction::Outcome::SUCCESS
44
- rescue StandardError => e
45
- transaction&.result = 'error'
46
- transaction&.outcome = Transaction::Outcome::FAILURE
47
- ElasticAPM.report(e)
37
+ begin
38
+ result = super(*args)
48
39
 
49
- raise
50
- ensure
51
- ElasticAPM.end_transaction
52
- ElasticAPM.stop
53
- end
40
+ transaction&.result = 'success'
41
+ transaction&.outcome = Transaction::Outcome::SUCCESS
42
+ rescue StandardError => e
43
+ transaction&.result = 'error'
44
+ transaction&.outcome = Transaction::Outcome::FAILURE
45
+ ElasticAPM.report(e)
54
46
 
55
- result
47
+ raise
48
+ ensure
49
+ ElasticAPM.end_transaction
50
+ ElasticAPM.stop
56
51
  end
52
+
53
+ result
57
54
  end
58
55
  end
56
+
57
+ def install
58
+ ::Rake::Task.prepend(Ext)
59
+ end
59
60
  end
61
+
60
62
  register 'Rake::Task', 'rake', RakeSpy.new
61
63
  end
62
64
  end
@@ -22,21 +22,22 @@ module ElasticAPM
22
22
  module Spies
23
23
  # @api private
24
24
  class RedisSpy
25
- def install
26
- ::Redis::Client.class_eval do
27
- alias call_without_apm call
28
-
29
- def call(command, &block)
30
- name = command[0].upcase
25
+ # @api private
26
+ module Ext
27
+ def call(command, &block)
28
+ name = command[0].upcase
31
29
 
32
- return call_without_apm(command, &block) if command[0] == :auth
30
+ return super(command, &block) if command[0] == :auth
33
31
 
34
- ElasticAPM.with_span(name.to_s, 'db.redis') do
35
- call_without_apm(command, &block)
36
- end
32
+ ElasticAPM.with_span(name.to_s, 'db.redis') do
33
+ super(command, &block)
37
34
  end
38
35
  end
39
36
  end
37
+
38
+ def install
39
+ ::Redis::Client.prepend(Ext)
40
+ end
40
41
  end
41
42
 
42
43
  register 'Redis', 'redis', RedisSpy.new
@@ -24,29 +24,26 @@ module ElasticAPM
24
24
  class ResqueSpy
25
25
  TYPE = 'Resque'
26
26
 
27
- def install
28
- install_perform_spy
27
+ # @api private
28
+ module Ext
29
+ def perform
30
+ name = @payload && @payload['class']&.to_s
31
+ transaction = ElasticAPM.start_transaction(name, TYPE)
32
+ super
33
+ transaction&.done 'success'
34
+ transaction&.outcome = Transaction::Outcome::SUCCESS
35
+ rescue ::Exception => e
36
+ ElasticAPM.report(e, handled: false)
37
+ transaction&.done 'error'
38
+ transaction&.outcome = Transaction::Outcome::FAILURE
39
+ raise
40
+ ensure
41
+ ElasticAPM.end_transaction
42
+ end
29
43
  end
30
44
 
31
- def install_perform_spy
32
- ::Resque::Job.class_eval do
33
- alias :perform_without_elastic_apm :perform
34
-
35
- def perform
36
- name = @payload && @payload['class']&.to_s
37
- transaction = ElasticAPM.start_transaction(name, TYPE)
38
- perform_without_elastic_apm
39
- transaction&.done 'success'
40
- transaction&.outcome = Transaction::Outcome::SUCCESS
41
- rescue ::Exception => e
42
- ElasticAPM.report(e, handled: false)
43
- transaction&.done 'error'
44
- transaction&.outcome = Transaction::Outcome::FAILURE
45
- raise
46
- ensure
47
- ElasticAPM.end_transaction
48
- end
49
- end
45
+ def install
46
+ ::Resque::Job.prepend(Ext)
50
47
  end
51
48
  end
52
49
 
@@ -14,7 +14,7 @@
14
14
  # KIND, either express or implied. See the License for the
15
15
  # specific language governing permissions and limitations
16
16
  # under the License.
17
-
17
+ #
18
18
  # frozen_string_literal: true
19
19
 
20
20
  module ElasticAPM
@@ -76,24 +76,20 @@ module ElasticAPM
76
76
  end
77
77
 
78
78
 
79
- def install
80
- ::Aws::S3::Client.class_eval do
79
+ # @api private
80
+ module Ext
81
+ def self.prepended(mod)
81
82
  # Alias all available operations
82
- api.operation_names.each do |operation_name|
83
- alias :"#{operation_name}_without_apm" :"#{operation_name}"
84
-
83
+ mod.api.operation_names.each do |operation_name|
85
84
  define_method(operation_name) do |params = {}, options = {}, &block|
86
85
  bucket_name = ElasticAPM::Spies::S3Spy.bucket_name(params)
87
- cloud = ElasticAPM::Span::Context::Destination::Cloud.new(
88
- region: ElasticAPM::Spies::S3Spy.accesspoint_region(params) || config.region
89
- )
86
+ region = ElasticAPM::Spies::S3Spy.accesspoint_region(params) || config.region
90
87
 
88
+ resource = "#{SUBTYPE}/#{bucket_name || 'unknown-bucket'}"
91
89
  context = ElasticAPM::Span::Context.new(
92
90
  destination: {
93
- cloud: cloud,
94
- resource: bucket_name,
95
- type: TYPE,
96
- name: SUBTYPE
91
+ service: { resource: resource },
92
+ cloud: { region: region }
97
93
  }
98
94
  )
99
95
 
@@ -105,14 +101,17 @@ module ElasticAPM
105
101
  context: context
106
102
  ) do
107
103
  ElasticAPM::Spies::S3Spy.without_net_http do
108
- original_method = method("#{operation_name}_without_apm")
109
- original_method.call(params, options, &block)
104
+ super(params, options, &block)
110
105
  end
111
106
  end
112
107
  end
113
108
  end
114
109
  end
115
110
  end
111
+
112
+ def install
113
+ ::Aws::S3::Client.prepend(Ext)
114
+ end
116
115
  end
117
116
 
118
117
  register(
@@ -17,7 +17,7 @@
17
17
 
18
18
  # frozen_string_literal: true
19
19
 
20
- require 'elastic_apm/sql'
20
+ require 'elastic_apm/sql/signature'
21
21
 
22
22
  module ElasticAPM
23
23
  # @api private
@@ -28,64 +28,58 @@ module ElasticAPM
28
28
  ACTION = 'query'
29
29
 
30
30
  def self.summarizer
31
- @summarizer ||= Sql.summarizer
31
+ @summarizer ||= Sql::Signature::Summarizer.new
32
32
  end
33
33
 
34
- # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
35
- def install
36
- require 'sequel/database/logging'
37
-
38
- ::Sequel::Database.class_eval do
39
- alias log_connection_yield_without_apm log_connection_yield
40
-
41
- def log_connection_yield(sql, connection, args = nil, &block)
42
- unless ElasticAPM.current_transaction
43
- return log_connection_yield_without_apm(
44
- sql, connection, args, &block
45
- )
46
- end
34
+ # @api private
35
+ module Ext
36
+ # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
37
+ def log_connection_yield(sql, connection, args = nil, &block)
38
+ unless ElasticAPM.current_transaction
39
+ return super(sql, connection, args, &block)
40
+ end
47
41
 
48
- subtype = database_type.to_s
42
+ subtype = database_type.to_s
49
43
 
50
- name =
51
- ElasticAPM::Spies::SequelSpy.summarizer.summarize sql
44
+ name =
45
+ ElasticAPM::Spies::SequelSpy.summarizer.summarize sql
52
46
 
53
- context = ElasticAPM::Span::Context.new(
54
- db: { statement: sql, type: 'sql', user: opts[:user] },
55
- destination: { name: subtype, resource: subtype, type: TYPE }
56
- )
47
+ context = ElasticAPM::Span::Context.new(
48
+ db: { statement: sql, type: 'sql', user: opts[:user] },
49
+ destination: { service: { resource: subtype } }
50
+ )
57
51
 
58
- span = ElasticAPM.start_span(
59
- name,
60
- TYPE,
61
- subtype: subtype,
62
- action: ACTION,
63
- context: context
64
- )
65
- log_connection_yield_without_apm(
66
- sql,
67
- connection,
68
- args,
69
- &block
70
- ).tap do |result|
71
- if name =~ /^(UPDATE|DELETE)/
72
- if connection.respond_to?(:changes)
73
- span.context.db.rows_affected = connection.changes
74
- elsif result.is_a?(Integer)
75
- span.context.db.rows_affected = result
76
- end
52
+ span = ElasticAPM.start_span(
53
+ name,
54
+ TYPE,
55
+ subtype: subtype,
56
+ action: ACTION,
57
+ context: context
58
+ )
59
+ super(sql, connection, args, &block).tap do |result|
60
+ if /^(UPDATE|DELETE)/.match?(name)
61
+ if connection.respond_to?(:changes)
62
+ span.context.db.rows_affected = connection.changes
63
+ elsif result.is_a?(Integer)
64
+ span.context.db.rows_affected = result
77
65
  end
78
66
  end
79
- rescue
80
- span&.outcome = Span::Outcome::FAILURE
81
- raise
82
- ensure
83
- span&.outcome ||= Span::Outcome::SUCCESS
84
- ElasticAPM.end_span
85
67
  end
68
+ rescue
69
+ span&.outcome = Span::Outcome::FAILURE
70
+ raise
71
+ ensure
72
+ span&.outcome ||= Span::Outcome::SUCCESS
73
+ ElasticAPM.end_span
86
74
  end
75
+ # rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
76
+ end
77
+
78
+ def install
79
+ require 'sequel/database/logging'
80
+
81
+ ::Sequel::Database.prepend(Ext)
87
82
  end
88
- # rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
89
83
  end
90
84
 
91
85
  register 'Sequel', 'sequel', SequelSpy.new
@@ -65,34 +65,32 @@ module ElasticAPM
65
65
  end
66
66
  end
67
67
 
68
- def install_processor
69
- require 'sidekiq/processor'
70
-
71
- Sidekiq::Processor.class_eval do
72
- alias start_without_apm start
73
- alias terminate_without_apm terminate
74
-
75
- def start
76
- result = start_without_apm
77
-
68
+ # @api private
69
+ module Ext
70
+ def start
71
+ super.tap do
78
72
  # Already running from Railtie if Rails
79
73
  if ElasticAPM.running?
80
74
  ElasticAPM.agent.config.logger = Sidekiq.logger
81
75
  else
82
76
  ElasticAPM.start
83
77
  end
84
-
85
- result
86
78
  end
79
+ end
87
80
 
88
- def terminate
89
- terminate_without_apm
90
-
81
+ def terminate
82
+ super.tap do
91
83
  ElasticAPM.stop
92
84
  end
93
85
  end
94
86
  end
95
87
 
88
+ def install_processor
89
+ require 'sidekiq/processor'
90
+
91
+ Sidekiq::Processor.prepend(Ext)
92
+ end
93
+
96
94
  def install
97
95
  install_processor
98
96
  install_middleware
@@ -22,35 +22,34 @@ module ElasticAPM
22
22
  module Spies
23
23
  # @api private
24
24
  class SinatraSpy
25
- def install
26
- ::Sinatra::Base.class_eval do
27
- alias dispatch_without_apm! dispatch!
28
- alias compile_template_without_apm compile_template
29
-
30
- def dispatch!(*args, &block)
31
- dispatch_without_apm!(*args, &block).tap do
32
- next unless (transaction = ElasticAPM.current_transaction)
33
- next unless (route = env['sinatra.route'])
34
-
35
- transaction.name = route
36
- end
25
+ # @api private
26
+ module Ext
27
+ def dispatch!(*args, &block)
28
+ super(*args, &block).tap do
29
+ next unless (transaction = ElasticAPM.current_transaction)
30
+ next unless (route = env['sinatra.route'])
31
+
32
+ transaction.name = route
37
33
  end
34
+ end
38
35
 
39
- def compile_template(engine, data, opts, *args, &block)
40
- opts[:__elastic_apm_template_name] =
41
- case data
42
- when Symbol then data.to_s
43
- else format('Inline %s', engine)
44
- end
36
+ def compile_template(engine, data, opts, *args, &block)
37
+ opts[:__elastic_apm_template_name] =
38
+ case data
39
+ when Symbol then data.to_s
40
+ else format('Inline %s', engine)
41
+ end
45
42
 
46
- compile_template_without_apm(engine, data, opts, *args, &block)
47
- end
43
+ super(engine, data, opts, *args, &block)
48
44
  end
49
45
  end
46
+
47
+ def install
48
+ ::Sinatra::Base.prepend(Ext)
49
+ end
50
50
  end
51
51
 
52
52
  register 'Sinatra::Base', 'sinatra/base', SinatraSpy.new
53
-
54
53
  require 'elastic_apm/spies/tilt'
55
54
  end
56
55
  end
@@ -39,7 +39,7 @@ module ElasticAPM
39
39
 
40
40
  def self.get_topic(params)
41
41
  return '<PHONE_NUMBER>' if params[:phone_number]
42
-
42
+
43
43
  last_after_slash_or_colon(
44
44
  params[:topic_arn] || params[:target_arn]
45
45
  )
@@ -64,62 +64,57 @@ module ElasticAPM
64
64
  end
65
65
 
66
66
  def self.span_context(topic, region)
67
- cloud = ElasticAPM::Span::Context::Destination::Cloud.new(region: region)
68
-
69
67
  ElasticAPM::Span::Context.new(
70
- message: {
71
- queue_name: topic
72
- },
68
+ message: { queue_name: topic },
73
69
  destination: {
74
- resource: [SUBTYPE, topic].compact.join('/'),
75
- type: TYPE,
76
- name: SUBTYPE,
77
- cloud: cloud
70
+ service: { resource: "#{SUBTYPE}/#{topic}" },
71
+ cloud: { region: region }
78
72
  }
79
73
  )
80
74
  end
81
75
 
82
- def install
83
- ::Aws::SNS::Client.class_eval do
84
- alias :publish_without_apm :publish
76
+ # @api private
77
+ module Ext
78
+ def publish(params = {}, options = {})
79
+ unless (transaction = ElasticAPM.current_transaction)
80
+ return super(params, options)
81
+ end
85
82
 
86
- def publish(params = {}, options = {})
87
- unless (transaction = ElasticAPM.current_transaction)
88
- return publish_without_apm(params, options)
83
+ topic = ElasticAPM::Spies::SNSSpy.get_topic(params)
84
+ span_name = topic ? "SNS PUBLISH to #{topic}" : 'SNS PUBLISH'
85
+ region = ElasticAPM::Spies::SNSSpy.arn_region(
86
+ params[:topic_arn] || params[:target_arn]
87
+ )
88
+ context = ElasticAPM::Spies::SNSSpy.span_context(
89
+ topic,
90
+ region || config.region
91
+ )
92
+
93
+ ElasticAPM.with_span(
94
+ span_name,
95
+ TYPE,
96
+ subtype: SUBTYPE,
97
+ action: 'publish',
98
+ context: context
99
+ ) do |span|
100
+ trace_context = span&.trace_context || transaction.trace_context
101
+ trace_context.apply_headers do |key, value|
102
+ params[:message_attributes] ||= {}
103
+ params[:message_attributes][key] ||= {}
104
+ params[:message_attributes][key][:string_value] = value
105
+ params[:message_attributes][key][:data_type] = 'String'
89
106
  end
90
107
 
91
- topic = ElasticAPM::Spies::SNSSpy.get_topic(params)
92
- span_name = topic ? "SNS PUBLISH to #{topic}" : 'SNS PUBLISH'
93
- region = ElasticAPM::Spies::SNSSpy.arn_region(
94
- params[:topic_arn] || params[:target_arn]
95
- )
96
- context = ElasticAPM::Spies::SNSSpy.span_context(
97
- topic,
98
- region || config.region
99
- )
100
-
101
- ElasticAPM.with_span(
102
- span_name,
103
- TYPE,
104
- subtype: SUBTYPE,
105
- action: 'publish',
106
- context: context
107
- ) do |span|
108
- trace_context = span&.trace_context || transaction.trace_context
109
- trace_context.apply_headers do |key, value|
110
- params[:message_attributes] ||= {}
111
- params[:message_attributes][key] ||= {}
112
- params[:message_attributes][key][:string_value] = value
113
- params[:message_attributes][key][:data_type] = 'String'
114
- end
115
-
116
- ElasticAPM::Spies::SNSSpy.without_net_http do
117
- publish_without_apm(params, options)
118
- end
108
+ ElasticAPM::Spies::SNSSpy.without_net_http do
109
+ super(params, options)
119
110
  end
120
111
  end
121
112
  end
122
113
  end
114
+
115
+ def install
116
+ ::Aws::SNS::Client.prepend(Ext)
117
+ end
123
118
  end
124
119
 
125
120
  register(