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
@@ -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),