elastic-apm 3.10.1 → 3.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.github/labeler-config.yml +3 -0
  3. data/.github/workflows/labeler.yml +16 -0
  4. data/.rubocop.yml +33 -4
  5. data/CHANGELOG.asciidoc +67 -0
  6. data/Gemfile +5 -6
  7. data/Rakefile +10 -10
  8. data/docs/api.asciidoc +2 -1
  9. data/docs/configuration.asciidoc +4 -3
  10. data/lib/elastic_apm.rb +14 -2
  11. data/lib/elastic_apm/central_config.rb +7 -2
  12. data/lib/elastic_apm/config.rb +51 -19
  13. data/lib/elastic_apm/config/log_level_map.rb +47 -0
  14. data/lib/elastic_apm/config/options.rb +2 -1
  15. data/lib/elastic_apm/config/round_float.rb +31 -0
  16. data/lib/elastic_apm/config/wildcard_pattern_list.rb +2 -0
  17. data/lib/elastic_apm/context.rb +2 -8
  18. data/lib/elastic_apm/graphql.rb +2 -0
  19. data/lib/elastic_apm/grpc.rb +3 -3
  20. data/lib/elastic_apm/instrumenter.rb +11 -4
  21. data/lib/elastic_apm/metadata.rb +3 -1
  22. data/lib/elastic_apm/metadata/cloud_info.rb +130 -0
  23. data/lib/elastic_apm/metadata/service_info.rb +2 -2
  24. data/lib/elastic_apm/metadata/system_info/container_info.rb +16 -5
  25. data/lib/elastic_apm/metrics.rb +1 -0
  26. data/lib/elastic_apm/metrics/cpu_mem_set.rb +1 -0
  27. data/lib/elastic_apm/middleware.rb +8 -3
  28. data/lib/elastic_apm/opentracing.rb +2 -1
  29. data/lib/elastic_apm/span.rb +14 -0
  30. data/lib/elastic_apm/span/context/db.rb +1 -1
  31. data/lib/elastic_apm/spies/delayed_job.rb +8 -2
  32. data/lib/elastic_apm/spies/dynamo_db.rb +8 -1
  33. data/lib/elastic_apm/spies/elasticsearch.rb +10 -2
  34. data/lib/elastic_apm/spies/faraday.rb +5 -2
  35. data/lib/elastic_apm/spies/http.rb +1 -0
  36. data/lib/elastic_apm/spies/mongo.rb +6 -2
  37. data/lib/elastic_apm/spies/net_http.rb +3 -0
  38. data/lib/elastic_apm/spies/rake.rb +4 -2
  39. data/lib/elastic_apm/spies/resque.rb +4 -2
  40. data/lib/elastic_apm/spies/sequel.rb +12 -1
  41. data/lib/elastic_apm/spies/shoryuken.rb +2 -0
  42. data/lib/elastic_apm/spies/sidekiq.rb +2 -0
  43. data/lib/elastic_apm/spies/sneakers.rb +2 -0
  44. data/lib/elastic_apm/spies/sucker_punch.rb +2 -0
  45. data/lib/elastic_apm/sql/signature.rb +4 -2
  46. data/lib/elastic_apm/sql/tokenizer.rb +2 -2
  47. data/lib/elastic_apm/stacktrace/frame.rb +1 -0
  48. data/lib/elastic_apm/stacktrace_builder.rb +2 -4
  49. data/lib/elastic_apm/trace_context.rb +1 -3
  50. data/lib/elastic_apm/trace_context/traceparent.rb +2 -6
  51. data/lib/elastic_apm/trace_context/tracestate.rb +114 -9
  52. data/lib/elastic_apm/transaction.rb +41 -7
  53. data/lib/elastic_apm/transport/connection.rb +2 -1
  54. data/lib/elastic_apm/transport/filters/hash_sanitizer.rb +16 -16
  55. data/lib/elastic_apm/transport/filters/secrets_filter.rb +35 -12
  56. data/lib/elastic_apm/transport/serializers.rb +9 -6
  57. data/lib/elastic_apm/transport/serializers/metadata_serializer.rb +44 -4
  58. data/lib/elastic_apm/transport/serializers/span_serializer.rb +3 -3
  59. data/lib/elastic_apm/transport/serializers/transaction_serializer.rb +2 -0
  60. data/lib/elastic_apm/transport/user_agent.rb +3 -3
  61. data/lib/elastic_apm/transport/worker.rb +1 -0
  62. data/lib/elastic_apm/util.rb +2 -0
  63. data/lib/elastic_apm/util/deep_dup.rb +65 -0
  64. data/lib/elastic_apm/util/precision_validator.rb +46 -0
  65. data/lib/elastic_apm/version.rb +1 -1
  66. metadata +10 -3
@@ -123,6 +123,7 @@ module ElasticAPM
123
123
  end
124
124
 
125
125
  attr_accessor :trace_context
126
+
126
127
  def_delegators :trace_context, :trace_id, :id, :parent_id
127
128
 
128
129
  def self.from_header(header)
@@ -345,7 +346,7 @@ module ElasticAPM
345
346
  context = context_from_child_of(child_of) ||
346
347
  context_from_references(references) ||
347
348
  context_from_active_scope(ignore_active_scope)
348
- return context.child if context&.respond_to?(:child)
349
+ return context.child if context.respond_to?(:child)
349
350
 
350
351
  context
351
352
  end
@@ -25,6 +25,17 @@ module ElasticAPM
25
25
  extend Forwardable
26
26
  include ChildDurations::Methods
27
27
 
28
+ # @api private
29
+ class Outcome
30
+ FAILURE = "failure"
31
+ SUCCESS = "success"
32
+ UNKNOWN = "unknown"
33
+
34
+ def self.from_http_status(code)
35
+ code.to_i >= 400 ? FAILURE : SUCCESS
36
+ end
37
+ end
38
+
28
39
  DEFAULT_TYPE = 'custom'
29
40
 
30
41
  # rubocop:disable Metrics/ParameterLists
@@ -53,6 +64,7 @@ module ElasticAPM
53
64
  @transaction = transaction
54
65
  @parent = parent
55
66
  @trace_context = trace_context || parent.trace_context.child
67
+ @sample_rate = transaction.sample_rate
56
68
 
57
69
  @context = context || Span::Context.new(sync: sync)
58
70
  @stacktrace_builder = stacktrace_builder
@@ -65,6 +77,7 @@ module ElasticAPM
65
77
  :action,
66
78
  :name,
67
79
  :original_backtrace,
80
+ :outcome,
68
81
  :subtype,
69
82
  :trace_context,
70
83
  :type
@@ -73,6 +86,7 @@ module ElasticAPM
73
86
  :context,
74
87
  :duration,
75
88
  :parent,
89
+ :sample_rate,
76
90
  :self_time,
77
91
  :stacktrace,
78
92
  :timestamp,
@@ -30,7 +30,7 @@ module ElasticAPM
30
30
  rows_affected: nil
31
31
  )
32
32
  @instance = instance
33
- @statement = statement
33
+ @statement = statement&.encode('utf-8', invalid: :replace, undef: :replace)
34
34
  @type = type
35
35
  @user = user
36
36
  @rows_affected = rows_affected
@@ -41,10 +41,12 @@ module ElasticAPM
41
41
  job_name = name_from_payload(job.payload_object)
42
42
  transaction = ElasticAPM.start_transaction(job_name, TYPE)
43
43
  job.invoke_job_without_apm(*args, &block)
44
- transaction.done 'success'
44
+ transaction&.done 'success'
45
+ transaction&.outcome = Transaction::Outcome::SUCCESS
45
46
  rescue ::Exception => e
46
47
  ElasticAPM.report(e, handled: false)
47
- transaction.done 'error'
48
+ transaction&.done 'error'
49
+ transaction&.outcome = Transaction::Outcome::FAILURE
48
50
  raise
49
51
  ensure
50
52
  ElasticAPM.end_transaction
@@ -53,6 +55,10 @@ module ElasticAPM
53
55
  def self.name_from_payload(payload_object)
54
56
  if payload_object.is_a?(::Delayed::PerformableMethod)
55
57
  performable_method_name(payload_object)
58
+ elsif payload_object.instance_of?(
59
+ ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper
60
+ )
61
+ payload_object.job_data['job_class']
56
62
  else
57
63
  payload_object.class.name
58
64
  end
@@ -25,9 +25,11 @@ module ElasticAPM
25
25
  def self.without_net_http
26
26
  return yield unless defined?(NetHTTPSpy)
27
27
 
28
+ # rubocop:disable Style/ExplicitBlockArgument
28
29
  ElasticAPM::Spies::NetHTTPSpy.disable_in do
29
30
  yield
30
31
  end
32
+ # rubocop:enable Style/ExplicitBlockArgument
31
33
  end
32
34
 
33
35
  def install
@@ -37,7 +39,12 @@ module ElasticAPM
37
39
  alias :"#{operation_name}_without_apm" :"#{operation_name}"
38
40
 
39
41
  define_method(operation_name) do |params = {}, options = {}|
40
- ElasticAPM.with_span(operation_name, 'db', subtype: 'dynamodb', action: operation_name) do
42
+ ElasticAPM.with_span(
43
+ operation_name,
44
+ 'db',
45
+ subtype: 'dynamodb',
46
+ action: operation_name
47
+ ) do
41
48
  ElasticAPM::Spies::DynamoDBSpy.without_net_http do
42
49
  original_method = method("#{operation_name}_without_apm")
43
50
  original_method.call(params, options)
@@ -27,7 +27,14 @@ module ElasticAPM
27
27
  SUBTYPE = 'elasticsearch'
28
28
 
29
29
  def self.sanitizer
30
- @sanitizer ||= ElasticAPM::Transport::Filters::HashSanitizer.new
30
+ @sanitizer ||=
31
+ begin
32
+ config = ElasticAPM.agent.config
33
+ ElasticAPM::Transport::Filters::HashSanitizer.new(
34
+ key_patterns: config.custom_key_filters +
35
+ config.sanitize_field_names
36
+ )
37
+ end
31
38
  end
32
39
 
33
40
  def install
@@ -47,7 +54,8 @@ module ElasticAPM
47
54
  if ElasticAPM.agent.config.capture_elasticsearch_queries
48
55
  unless args[1].nil? || args[1].empty?
49
56
  statement << {
50
- body: ElasticAPM::Spies::ElasticsearchSpy.sanitizer.strip_from!(args[1])
57
+ body: ElasticAPM::Spies::ElasticsearchSpy
58
+ .sanitizer.strip_from(args[1])
51
59
  }
52
60
  end
53
61
  end
@@ -28,12 +28,14 @@ module ElasticAPM
28
28
  def self.without_net_http
29
29
  return yield unless defined?(NetHTTPSpy)
30
30
 
31
+ # rubocop:disable Style/ExplicitBlockArgument
31
32
  ElasticAPM::Spies::NetHTTPSpy.disable_in do
32
33
  yield
33
34
  end
35
+ # rubocop:enable Style/ExplicitBlockArgument
34
36
  end
35
37
 
36
- # rubocop:disable Metrics/CyclomaticComplexity
38
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
37
39
  def install
38
40
  ::Faraday::Connection.class_eval do
39
41
  alias run_request_without_apm run_request
@@ -89,13 +91,14 @@ module ElasticAPM
89
91
  http.status_code = result.status.to_s
90
92
  end
91
93
 
94
+ span&.outcome = Span::Outcome.from_http_status(result.status)
92
95
  result
93
96
  end
94
97
  end
95
98
  end
96
99
  end
97
100
  end
98
- # rubocop:enable Metrics/CyclomaticComplexity
101
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
99
102
  end
100
103
 
101
104
  register 'Faraday', 'faraday', FaradaySpy.new
@@ -61,6 +61,7 @@ module ElasticAPM
61
61
  http.status_code = result.status.to_s
62
62
  end
63
63
 
64
+ span&.outcome = Span::Outcome.from_http_status(result.status)
64
65
  result
65
66
  end
66
67
  end
@@ -44,11 +44,15 @@ module ElasticAPM
44
44
  end
45
45
 
46
46
  def failed(event)
47
- pop_event(event)
47
+ span = pop_event(event)
48
+ span&.outcome = Span::Outcome::FAILURE
49
+ span
48
50
  end
49
51
 
50
52
  def succeeded(event)
51
- pop_event(event)
53
+ span = pop_event(event)
54
+ span&.outcome = Span::Outcome::SUCCESS
55
+ span
52
56
  end
53
57
 
54
58
  private
@@ -47,6 +47,7 @@ module ElasticAPM
47
47
  end
48
48
 
49
49
  # rubocop:disable Metrics/CyclomaticComplexity
50
+ # rubocop:disable Metrics/PerceivedComplexity
50
51
  def install
51
52
  Net::HTTP.class_eval do
52
53
  alias request_without_apm request
@@ -96,12 +97,14 @@ module ElasticAPM
96
97
  http.status_code = result.code
97
98
  end
98
99
 
100
+ span&.outcome = Span::Outcome.from_http_status(result.code)
99
101
  result
100
102
  end
101
103
  end
102
104
  end
103
105
  end
104
106
  # rubocop:enable Metrics/CyclomaticComplexity
107
+ # rubocop:enable Metrics/PerceivedComplexity
105
108
  end
106
109
 
107
110
  register 'Net::HTTP', 'net/http', NetHTTPSpy.new
@@ -39,9 +39,11 @@ module ElasticAPM
39
39
  begin
40
40
  result = execute_without_apm(*args)
41
41
 
42
- transaction.result = 'success' if transaction
42
+ transaction&.result = 'success'
43
+ transaction&.outcome = Transaction::Outcome::SUCCESS
43
44
  rescue StandardError => e
44
- transaction.result = 'error' if transaction
45
+ transaction&.result = 'error'
46
+ transaction&.outcome = Transaction::Outcome::FAILURE
45
47
  ElasticAPM.report(e)
46
48
 
47
49
  raise
@@ -36,10 +36,12 @@ module ElasticAPM
36
36
  name = @payload && @payload['class']&.to_s
37
37
  transaction = ElasticAPM.start_transaction(name, TYPE)
38
38
  perform_without_elastic_apm
39
- transaction.done 'success'
39
+ transaction&.done 'success'
40
+ transaction&.outcome = Transaction::Outcome::SUCCESS
40
41
  rescue ::Exception => e
41
42
  ElasticAPM.report(e, handled: false)
42
- transaction.done 'error' if transaction
43
+ transaction&.done 'error'
44
+ transaction&.outcome = Transaction::Outcome::FAILURE
43
45
  raise
44
46
  ensure
45
47
  ElasticAPM.end_transaction
@@ -31,6 +31,7 @@ module ElasticAPM
31
31
  @summarizer ||= Sql.summarizer
32
32
  end
33
33
 
34
+ # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
34
35
  def install
35
36
  require 'sequel/database/logging'
36
37
 
@@ -61,7 +62,12 @@ module ElasticAPM
61
62
  action: ACTION,
62
63
  context: context
63
64
  )
64
- yield.tap do |result|
65
+ log_connection_yield_without_apm(
66
+ sql,
67
+ connection,
68
+ args,
69
+ &block
70
+ ).tap do |result|
65
71
  if name =~ /^(UPDATE|DELETE)/
66
72
  if connection.respond_to?(:changes)
67
73
  span.context.db.rows_affected = connection.changes
@@ -70,11 +76,16 @@ module ElasticAPM
70
76
  end
71
77
  end
72
78
  end
79
+ rescue
80
+ span&.outcome = Span::Outcome::FAILURE
81
+ raise
73
82
  ensure
83
+ span&.outcome ||= Span::Outcome::SUCCESS
74
84
  ElasticAPM.end_span
75
85
  end
76
86
  end
77
87
  end
88
+ # rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
78
89
  end
79
90
 
80
91
  register 'Sequel', 'sequel', SequelSpy.new
@@ -37,9 +37,11 @@ module ElasticAPM
37
37
  yield
38
38
 
39
39
  transaction&.done :success
40
+ transaction&.outcome = Transaction::Outcome::SUCCESS
40
41
  rescue ::Exception => e
41
42
  ElasticAPM.report(e, handled: false)
42
43
  transaction&.done :error
44
+ transaction&.outcome = Transaction::Outcome::FAILURE
43
45
  raise
44
46
  ensure
45
47
  ElasticAPM.end_transaction
@@ -35,9 +35,11 @@ module ElasticAPM
35
35
  yield
36
36
 
37
37
  transaction&.done :success
38
+ transaction&.outcome = Transaction::Outcome::SUCCESS
38
39
  rescue ::Exception => e
39
40
  ElasticAPM.report(e, handled: false)
40
41
  transaction&.done :error
42
+ transaction&.outcome = Transaction::Outcome::FAILURE
41
43
  raise
42
44
  ensure
43
45
  ElasticAPM.end_transaction
@@ -57,11 +57,13 @@ module ElasticAPM
57
57
 
58
58
  res = @app.call(deserialized_msg, delivery_info, metadata, handler)
59
59
  transaction&.done(:success)
60
+ transaction&.outcome = Transaction::Outcome::SUCCESS
60
61
 
61
62
  res
62
63
  rescue ::Exception => e
63
64
  ElasticAPM.report(e, handled: false)
64
65
  transaction&.done(:error)
66
+ transaction&.outcome = Transaction::Outcome::FAILURE
65
67
  raise
66
68
  ensure
67
69
  ElasticAPM.end_transaction
@@ -35,12 +35,14 @@ module ElasticAPM
35
35
  transaction = ElasticAPM.start_transaction(name, TYPE)
36
36
  __run_perform_without_elastic_apm(*args)
37
37
  transaction.done 'success'
38
+ transaction&.outcome = Transaction::Outcome::SUCCESS
38
39
  rescue ::Exception => e
39
40
  # Note that SuckerPunch by default doesn't raise the errors from
40
41
  # the user-defined JobClass#perform method as it uses an error
41
42
  # handler, accessed via `SuckerPunch.exception_handler`.
42
43
  ElasticAPM.report(e, handled: false)
43
44
  transaction.done 'error'
45
+ transaction&.outcome = Transaction::Outcome::FAILURE
44
46
  raise
45
47
  ensure
46
48
  ElasticAPM.end_transaction
@@ -36,8 +36,8 @@ module ElasticAPM
36
36
  end
37
37
 
38
38
  def initialize(sql)
39
- @sql = sql
40
- @tokenizer = Tokenizer.new(sql)
39
+ @sql = sql.encode('utf-8', invalid: :replace, undef: :replace)
40
+ @tokenizer = Tokenizer.new(@sql)
41
41
  end
42
42
 
43
43
  def parse
@@ -158,9 +158,11 @@ module ElasticAPM
158
158
  def scan_dotted_identifier
159
159
  table = @tokenizer.text
160
160
 
161
+ # rubocop:disable Style/WhileUntilModifier
161
162
  while scan_token(PERIOD) && scan_token(IDENT)
162
163
  table += ".#{@tokenizer.text}"
163
164
  end
165
+ # rubocop:enable Style/WhileUntilModifier
164
166
 
165
167
  table
166
168
  end
@@ -130,7 +130,7 @@ module ElasticAPM
130
130
  end
131
131
  # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
132
132
 
133
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
133
+ # rubocop:disable Metrics/CyclomaticComplexity
134
134
  def scan_dollar_sign
135
135
  while (peek = peek_char)
136
136
  case peek
@@ -165,7 +165,7 @@ module ElasticAPM
165
165
 
166
166
  OTHER
167
167
  end
168
- # rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
168
+ # rubocop:enable Metrics/CyclomaticComplexity
169
169
 
170
170
  def scan_quoted_indentifier(delimiter)
171
171
  while (char = next_char)
@@ -38,6 +38,7 @@ module ElasticAPM
38
38
  :module,
39
39
  :colno
40
40
  )
41
+
41
42
  def build_context(context_line_count)
42
43
  return unless abs_path && context_line_count > 0
43
44
 
@@ -23,7 +23,7 @@ require 'elastic_apm/util/lru_cache'
23
23
  module ElasticAPM
24
24
  # @api private
25
25
  class StacktraceBuilder
26
- JAVA_FORMAT = /^(.+)\.([^\.]+)\(([^\:]+)\:(\d+)\)$/.freeze
26
+ JAVA_FORMAT = /^(.+)\.([^.]+)\(([^:]+):(\d+)\)$/.freeze
27
27
  RUBY_FORMAT = /^(.+?):(\d+)(?::in `(.+?)')?$/.freeze
28
28
 
29
29
  RUBY_VERS_REGEX = %r{ruby(/gems)?[-/](\d+\.)+\d}.freeze
@@ -86,14 +86,13 @@ module ElasticAPM
86
86
  [file, number, method, module_name]
87
87
  end
88
88
 
89
- # rubocop:disable Metrics/CyclomaticComplexity
90
89
  def library_frame?(config, abs_path)
91
90
  return false unless abs_path
92
91
 
93
92
  return true if abs_path.start_with?(GEMS_PATH)
94
93
 
95
94
  if abs_path.start_with?(config.__root_path)
96
- return true if abs_path.start_with?(config.__root_path + '/vendor')
95
+ return true if abs_path.start_with?("#{config.__root_path}/vendor")
97
96
  return false
98
97
  end
99
98
 
@@ -102,7 +101,6 @@ module ElasticAPM
102
101
 
103
102
  false
104
103
  end
105
- # rubocop:enable Metrics/CyclomaticComplexity
106
104
 
107
105
  def strip_load_path(path)
108
106
  return nil if path.nil?
@@ -33,7 +33,7 @@ module ElasticAPM
33
33
  **legacy_traceparent_attrs
34
34
  )
35
35
  @traceparent = traceparent || Traceparent.new(**legacy_traceparent_attrs)
36
- @tracestate = tracestate
36
+ @tracestate = tracestate || Tracestate.new
37
37
  end
38
38
 
39
39
  attr_accessor :traceparent, :tracestate
@@ -42,7 +42,6 @@ module ElasticAPM
42
42
  :version, :trace_id, :id, :parent_id, :ensure_parent_id, :recorded?
43
43
 
44
44
  class << self
45
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
46
45
  def parse(legacy_header = nil, env: nil, metadata: nil)
47
46
  unless legacy_header || env || metadata
48
47
  raise ArgumentError, 'TraceContext expects env:, metadata: ' \
@@ -57,7 +56,6 @@ module ElasticAPM
57
56
  trace_context_from_metadata(metadata)
58
57
  end
59
58
  end
60
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
61
59
 
62
60
  private
63
61