elastic-apm 3.15.1 → 4.2.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.
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(