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
@@ -50,28 +50,21 @@ module ElasticAPM
50
50
  end
51
51
 
52
52
  def self.span_context(queue_name, region)
53
- cloud = ElasticAPM::Span::Context::Destination::Cloud.new(region: region)
54
-
55
53
  ElasticAPM::Span::Context.new(
56
- message: {
57
- queue_name: queue_name
58
- },
54
+ message: { queue_name: queue_name },
59
55
  destination: {
60
- resource: [SUBTYPE, queue_name].compact.join('/'),
61
- type: TYPE,
62
- name: SUBTYPE,
63
- cloud: cloud
56
+ service: { resource: "#{SUBTYPE}/#{queue_name}" },
57
+ cloud: { region: region }
64
58
  }
65
59
  )
66
60
  end
67
61
 
68
- def install
69
- ::Aws::SQS::Client.class_eval do
70
- alias :send_message_without_apm :send_message
71
-
62
+ # @api private
63
+ module Ext
64
+ def self.prepended(mod)
72
65
  def send_message(params = {}, options = {})
73
66
  unless (transaction = ElasticAPM.current_transaction)
74
- return send_message_without_apm(params, options)
67
+ return super(params, options)
75
68
  end
76
69
 
77
70
  queue_name = ElasticAPM::Spies::SQSSpy.queue_name(params)
@@ -98,16 +91,14 @@ module ElasticAPM
98
91
  end
99
92
 
100
93
  ElasticAPM::Spies::SQSSpy.without_net_http do
101
- send_message_without_apm(params, options)
94
+ super(params, options)
102
95
  end
103
96
  end
104
97
  end
105
98
 
106
- alias :send_message_batch_without_apm :send_message_batch
107
-
108
99
  def send_message_batch(params = {}, options = {})
109
100
  unless (transaction = ElasticAPM.current_transaction)
110
- return send_message_batch_without_apm(params, options)
101
+ return super(params, options)
111
102
  end
112
103
 
113
104
  queue_name = ElasticAPM::Spies::SQSSpy.queue_name(params)
@@ -127,6 +118,7 @@ module ElasticAPM
127
118
  context: context
128
119
  ) do |span|
129
120
  trace_context = span&.trace_context || transaction.trace_context
121
+
130
122
  trace_context.apply_headers do |key, value|
131
123
  params[:entries].each do |message|
132
124
  message[:message_attributes] ||= {}
@@ -137,16 +129,14 @@ module ElasticAPM
137
129
  end
138
130
 
139
131
  ElasticAPM::Spies::SQSSpy.without_net_http do
140
- send_message_batch_without_apm(params, options)
132
+ super(params, options)
141
133
  end
142
134
  end
143
135
  end
144
136
 
145
- alias :receive_message_without_apm :receive_message
146
-
147
137
  def receive_message(params = {}, options = {})
148
138
  unless ElasticAPM.current_transaction
149
- return receive_message_without_apm(params, options)
139
+ return super(params, options)
150
140
  end
151
141
 
152
142
  queue_name = ElasticAPM::Spies::SQSSpy.queue_name(params)
@@ -166,16 +156,14 @@ module ElasticAPM
166
156
  context: context
167
157
  ) do
168
158
  ElasticAPM::Spies::SQSSpy.without_net_http do
169
- receive_message_without_apm(params, options)
159
+ super(params, options)
170
160
  end
171
161
  end
172
162
  end
173
163
 
174
- alias :delete_message_without_apm :delete_message
175
-
176
164
  def delete_message(params = {}, options = {})
177
165
  unless ElasticAPM.current_transaction
178
- return delete_message_without_apm(params, options)
166
+ return super(params, options)
179
167
  end
180
168
 
181
169
  queue_name = ElasticAPM::Spies::SQSSpy.queue_name(params)
@@ -194,16 +182,14 @@ module ElasticAPM
194
182
  context: context
195
183
  ) do
196
184
  ElasticAPM::Spies::SQSSpy.without_net_http do
197
- delete_message_without_apm(params, options)
185
+ super(params, options)
198
186
  end
199
187
  end
200
188
  end
201
189
 
202
- alias :delete_message_batch_without_apm :delete_message_batch
203
-
204
190
  def delete_message_batch(params = {}, options = {})
205
191
  unless ElasticAPM.current_transaction
206
- return delete_message_batch_without_apm(params, options)
192
+ return super(params, options)
207
193
  end
208
194
 
209
195
  queue_name = ElasticAPM::Spies::SQSSpy.queue_name(params)
@@ -223,12 +209,16 @@ module ElasticAPM
223
209
  context: context
224
210
  ) do
225
211
  ElasticAPM::Spies::SQSSpy.without_net_http do
226
- delete_message_batch_without_apm(params, options)
212
+ super(params, options)
227
213
  end
228
214
  end
229
215
  end
230
216
  end
231
217
  end
218
+
219
+ def install
220
+ ::Aws::SQS::Client.prepend(Ext)
221
+ end
232
222
  end
233
223
 
234
224
  register(
@@ -24,19 +24,20 @@ module ElasticAPM
24
24
  class TiltSpy
25
25
  TYPE = 'template.tilt'
26
26
 
27
- def install
28
- ::Tilt::Template.class_eval do
29
- alias render_without_apm render
30
-
31
- def render(*args, &block)
32
- name = options[:__elastic_apm_template_name] || 'Unknown template'
27
+ # @api private
28
+ module Ext
29
+ def render(*args, &block)
30
+ name = options[:__elastic_apm_template_name] || 'Unknown template'
33
31
 
34
- ElasticAPM.with_span name, TYPE do
35
- render_without_apm(*args, &block)
36
- end
32
+ ElasticAPM.with_span name, TYPE do
33
+ super(*args, &block)
37
34
  end
38
35
  end
39
36
  end
37
+
38
+ def install
39
+ ::Tilt::Template.prepend(Ext)
40
+ end
40
41
  end
41
42
 
42
43
  register 'Tilt::Template', 'tilt/template', TiltSpy.new
@@ -69,7 +69,7 @@ module ElasticAPM
69
69
  when '[' then scan_quoted_indentifier(']')
70
70
  when '(' then LPAREN
71
71
  when ')' then RPAREN
72
- when '/' then scan_bracketed_comment
72
+ when '/' then scan_bracketed_or_cql_comment
73
73
  when '-' then scan_simple_comment
74
74
  when "'" then scan_string_literal
75
75
  when ALPHA then scan_keyword_or_identifier(possible_keyword: true)
@@ -110,7 +110,7 @@ module ElasticAPM
110
110
  next next_char
111
111
  end
112
112
 
113
- next next_char if peek =~ ALPHA
113
+ next next_char if ALPHA.match?(peek)
114
114
 
115
115
  break
116
116
  end
@@ -185,10 +185,16 @@ module ElasticAPM
185
185
  IDENT
186
186
  end
187
187
 
188
+ def scan_bracketed_or_cql_comment
189
+ case peek_char
190
+ when '*' then scan_bracketed_comment
191
+ when '/' then scan_cql_comment
192
+ else OTHER
193
+ end
194
+ end
195
+
188
196
  # rubocop:disable Metrics/CyclomaticComplexity
189
197
  def scan_bracketed_comment
190
- return OTHER unless peek_char == '*'
191
-
192
198
  nesting = 1
193
199
 
194
200
  while (char = next_char)
@@ -207,6 +213,16 @@ module ElasticAPM
207
213
  end
208
214
  # rubocop:enable Metrics/CyclomaticComplexity
209
215
 
216
+ def scan_cql_comment
217
+ return OTHER unless peek_char == '/'
218
+
219
+ while (char = next_char)
220
+ break if char == "\n"
221
+ end
222
+
223
+ COMMENT
224
+ end
225
+
210
226
  def scan_simple_comment
211
227
  return OTHER unless peek_char == '-'
212
228
 
@@ -251,7 +267,7 @@ module ElasticAPM
251
267
  when 'e', 'E'
252
268
  return NUMBER if exponent
253
269
  next_char
254
- next_char if peek_char =~ /[+-]/
270
+ next_char if /[+-]/.match?(peek_char)
255
271
  else break
256
272
  end
257
273
  end
@@ -38,7 +38,9 @@ module ElasticAPM
38
38
 
39
39
  def initialize(config)
40
40
  @config = config
41
- @cache = Util::LruCache.new(2048, &method(:build_frame))
41
+ @cache = Util::LruCache.new(2048) do |cache, frame|
42
+ build_frame(cache, frame)
43
+ end
42
44
  end
43
45
 
44
46
  attr_reader :config
@@ -45,6 +45,7 @@ module ElasticAPM
45
45
  # AS::Notifications API
46
46
 
47
47
  Notification = Struct.new(:id, :span)
48
+
48
49
  def start(name, id, payload)
49
50
  return unless (transaction = @agent.current_transaction)
50
51
 
@@ -29,10 +29,9 @@ module ElasticAPM
29
29
 
30
30
  def initialize(
31
31
  traceparent: nil,
32
- tracestate: nil,
33
- **legacy_traceparent_attrs
32
+ tracestate: nil
34
33
  )
35
- @traceparent = traceparent || Traceparent.new(**legacy_traceparent_attrs)
34
+ @traceparent = traceparent || Traceparent.new
36
35
  @tracestate = tracestate || Tracestate.new
37
36
  end
38
37
 
@@ -42,15 +41,13 @@ module ElasticAPM
42
41
  :version, :trace_id, :id, :parent_id, :ensure_parent_id, :recorded?
43
42
 
44
43
  class << self
45
- def parse(legacy_header = nil, env: nil, metadata: nil)
46
- unless legacy_header || env || metadata
44
+ def parse(env: nil, metadata: nil)
45
+ unless env || metadata
47
46
  raise ArgumentError, 'TraceContext expects env:, metadata: ' \
48
47
  'or single argument header string'
49
48
  end
50
49
 
51
- if legacy_header
52
- legacy_parse_from_header(legacy_header)
53
- elsif env
50
+ if env
54
51
  trace_context_from_env(env)
55
52
  elsif metadata
56
53
  trace_context_from_metadata(metadata)
@@ -88,11 +85,6 @@ module ElasticAPM
88
85
 
89
86
  new(traceparent: parent, tracestate: state)
90
87
  end
91
-
92
- def legacy_parse_from_header(header)
93
- parent = Traceparent.parse(header)
94
- new(traceparent: parent)
95
- end
96
88
  end
97
89
 
98
90
  def child
@@ -22,7 +22,7 @@ module ElasticAPM
22
22
  # @api private
23
23
  class Traceparent
24
24
  VERSION = '00'
25
- HEX_REGEX = /[^[:xdigit:]]/.freeze
25
+ NON_HEX_REGEX = /[^[:xdigit:]]/.freeze
26
26
 
27
27
  TRACE_ID_LENGTH = 16
28
28
  ID_LENGTH = 8
@@ -30,14 +30,13 @@ module ElasticAPM
30
30
  def initialize(
31
31
  version: VERSION,
32
32
  trace_id: nil,
33
- span_id: nil,
33
+ parent_id: nil,
34
34
  id: nil,
35
35
  recorded: true
36
36
  )
37
37
  @version = version
38
38
  @trace_id = trace_id || hex(TRACE_ID_LENGTH)
39
- # TODO: rename span_id kw arg to parent_id with next major version bump
40
- @parent_id = span_id
39
+ @parent_id = parent_id
41
40
  @id = id || hex(ID_LENGTH)
42
41
  @recorded = recorded
43
42
  end
@@ -56,8 +55,8 @@ module ElasticAPM
56
55
  values[-1] = Util.hex_to_bits(values[-1])
57
56
  end
58
57
 
59
- raise_invalid(header) if HEX_REGEX =~ t.trace_id
60
- raise_invalid(header) if HEX_REGEX =~ t.parent_id
58
+ raise_invalid(header) if NON_HEX_REGEX.match?(t.trace_id)
59
+ raise_invalid(header) if NON_HEX_REGEX.match?(t.parent_id)
61
60
  end
62
61
  end
63
62
 
@@ -105,7 +105,7 @@ module ElasticAPM
105
105
  @close_task&.cancel
106
106
  @close_task =
107
107
  Concurrent::ScheduledTask.execute(@config.api_request_time) do
108
- flush(:timeout)
108
+ flush(:scheduled_flush)
109
109
  end
110
110
  end
111
111
  end
@@ -74,7 +74,7 @@ module ElasticAPM
74
74
  debug '%s: Closing request with reason %s', thread_str, reason
75
75
  @closed.make_true
76
76
 
77
- @wr&.close(reason)
77
+ @wr&.close
78
78
  return if @request.nil? || @request&.join(5)
79
79
 
80
80
  error(
@@ -85,7 +85,7 @@ module ElasticAPM
85
85
  end
86
86
 
87
87
  def closed?
88
- @closed.true?
88
+ @rd.closed? && @closed.true?
89
89
  end
90
90
 
91
91
  def inspect
@@ -117,6 +117,8 @@ module ElasticAPM
117
117
  error(
118
118
  "Couldn't establish connection to APM Server:\n%p", e.inspect
119
119
  )
120
+ ensure
121
+ @rd&.close
120
122
  end
121
123
  end
122
124
  end
@@ -62,8 +62,7 @@ module ElasticAPM
62
62
  @io = Zlib::GzipWriter.new(io)
63
63
  end
64
64
 
65
- def close(reason = nil)
66
- debug("Closing writer with reason #{reason}")
65
+ def close
67
66
  io.close
68
67
  end
69
68
 
@@ -26,17 +26,8 @@ module ElasticAPM
26
26
  class HashSanitizer
27
27
  FILTERED = '[FILTERED]'
28
28
 
29
- # DEPRECATED: Remove these additions in next major version
30
- LEGACY_KEY_FILTERS = [/cookie/i, /auth/i].freeze
31
-
32
- # DEPRECATED: Remove this check in next major version
33
- VALUE_FILTERS = [
34
- # (probably) credit card number
35
- /^\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}$/
36
- ].freeze
37
-
38
29
  def initialize(key_patterns:)
39
- @key_patterns = key_patterns + LEGACY_KEY_FILTERS
30
+ @key_patterns = key_patterns
40
31
  end
41
32
 
42
33
  attr_accessor :key_patterns
@@ -48,18 +39,13 @@ module ElasticAPM
48
39
  def strip_from!(obj)
49
40
  return unless obj.is_a?(Hash)
50
41
 
51
- obj.each do |k, v|
52
- if filter_key?(k)
53
- next obj[k] = FILTERED
54
- end
55
-
42
+ obj.each_pair do |k, v|
56
43
  case v
57
44
  when Hash
58
45
  strip_from!(v)
59
- when String
60
- if filter_value?(v)
61
- obj[k] = FILTERED
62
- end
46
+ else
47
+ next unless filter_key?(k)
48
+ obj[k] = FILTERED
63
49
  end
64
50
  end
65
51
  end
@@ -67,10 +53,6 @@ module ElasticAPM
67
53
  def filter_key?(key)
68
54
  @key_patterns.any? { |regex| regex.match(key) }
69
55
  end
70
-
71
- def filter_value?(value)
72
- VALUE_FILTERS.any? { |regex| regex.match(value) }
73
- end
74
56
  end
75
57
  end
76
58
  end
@@ -65,7 +65,7 @@ module ElasticAPM
65
65
  }
66
66
 
67
67
  if (node_name = service.node_name)
68
- base[:node] = { name: keyword_field(node_name) }
68
+ base[:node] = { configured_name: keyword_field(node_name) }
69
69
  end
70
70
 
71
71
  base
@@ -81,7 +81,8 @@ module ElasticAPM
81
81
 
82
82
  def build_system(system)
83
83
  {
84
- hostname: keyword_field(system.hostname),
84
+ detected_hostname: keyword_field(system.detected_hostname),
85
+ configured_hostname: keyword_field(system.configured_hostname),
85
86
  architecture: keyword_field(system.architecture),
86
87
  platform: keyword_field(system.platform),
87
88
  kubernetes: keyword_object(system.kubernetes),