elastic-apm 3.11.0 → 3.14.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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/.ci/.jenkins_codecov.yml +1 -1
  3. data/.ci/.jenkins_exclude.yml +46 -61
  4. data/.ci/.jenkins_framework.yml +3 -4
  5. data/.ci/.jenkins_master_framework.yml +1 -1
  6. data/.ci/.jenkins_ruby.yml +1 -3
  7. data/.ci/Jenkinsfile +22 -2
  8. data/.ci/docker/jruby/11-jdk/Dockerfile +2 -1
  9. data/.ci/docker/jruby/12-jdk/Dockerfile +2 -1
  10. data/.ci/docker/jruby/13-jdk/Dockerfile +2 -1
  11. data/.ci/docker/jruby/7-jdk/Dockerfile +2 -1
  12. data/.ci/docker/jruby/8-jdk/Dockerfile +2 -1
  13. data/.github/labeler-config.yml +3 -0
  14. data/.github/workflows/addToProject.yml +29 -0
  15. data/.github/workflows/labeler.yml +16 -0
  16. data/.rubocop.yml +33 -4
  17. data/CHANGELOG.asciidoc +58 -0
  18. data/Gemfile +23 -9
  19. data/Rakefile +10 -10
  20. data/docs/api.asciidoc +2 -1
  21. data/docs/configuration.asciidoc +20 -3
  22. data/docs/supported-technologies.asciidoc +2 -0
  23. data/lib/elastic_apm.rb +14 -2
  24. data/lib/elastic_apm/central_config.rb +7 -2
  25. data/lib/elastic_apm/config.rb +35 -18
  26. data/lib/elastic_apm/config/log_level_map.rb +47 -0
  27. data/lib/elastic_apm/context.rb +2 -8
  28. data/lib/elastic_apm/graphql.rb +2 -0
  29. data/lib/elastic_apm/grpc.rb +3 -3
  30. data/lib/elastic_apm/instrumenter.rb +1 -1
  31. data/lib/elastic_apm/metadata/cloud_info.rb +6 -4
  32. data/lib/elastic_apm/metadata/service_info.rb +2 -2
  33. data/lib/elastic_apm/metadata/system_info/container_info.rb +16 -5
  34. data/lib/elastic_apm/metrics.rb +1 -0
  35. data/lib/elastic_apm/metrics/cpu_mem_set.rb +1 -0
  36. data/lib/elastic_apm/middleware.rb +8 -3
  37. data/lib/elastic_apm/normalizers/rails/active_record.rb +16 -4
  38. data/lib/elastic_apm/opentracing.rb +2 -1
  39. data/lib/elastic_apm/span.rb +13 -2
  40. data/lib/elastic_apm/span/context/db.rb +1 -1
  41. data/lib/elastic_apm/span/context/http.rb +2 -0
  42. data/lib/elastic_apm/spies/delayed_job.rb +11 -3
  43. data/lib/elastic_apm/spies/dynamo_db.rb +8 -1
  44. data/lib/elastic_apm/spies/elasticsearch.rb +4 -2
  45. data/lib/elastic_apm/spies/faraday.rb +19 -11
  46. data/lib/elastic_apm/spies/http.rb +1 -0
  47. data/lib/elastic_apm/spies/mongo.rb +10 -2
  48. data/lib/elastic_apm/spies/net_http.rb +4 -1
  49. data/lib/elastic_apm/spies/rake.rb +4 -2
  50. data/lib/elastic_apm/spies/resque.rb +4 -2
  51. data/lib/elastic_apm/spies/sequel.rb +12 -1
  52. data/lib/elastic_apm/spies/shoryuken.rb +2 -0
  53. data/lib/elastic_apm/spies/sidekiq.rb +2 -0
  54. data/lib/elastic_apm/spies/sneakers.rb +2 -0
  55. data/lib/elastic_apm/spies/sucker_punch.rb +2 -0
  56. data/lib/elastic_apm/sql/signature.rb +4 -2
  57. data/lib/elastic_apm/sql/tokenizer.rb +2 -2
  58. data/lib/elastic_apm/stacktrace/frame.rb +1 -0
  59. data/lib/elastic_apm/stacktrace_builder.rb +2 -4
  60. data/lib/elastic_apm/trace_context.rb +0 -2
  61. data/lib/elastic_apm/trace_context/traceparent.rb +0 -2
  62. data/lib/elastic_apm/trace_context/tracestate.rb +7 -5
  63. data/lib/elastic_apm/transaction.rb +17 -4
  64. data/lib/elastic_apm/transport/connection.rb +1 -1
  65. data/lib/elastic_apm/transport/filters/hash_sanitizer.rb +8 -1
  66. data/lib/elastic_apm/transport/filters/secrets_filter.rb +32 -10
  67. data/lib/elastic_apm/transport/serializers.rb +1 -0
  68. data/lib/elastic_apm/transport/serializers/metadata_serializer.rb +23 -8
  69. data/lib/elastic_apm/transport/serializers/span_serializer.rb +2 -3
  70. data/lib/elastic_apm/transport/serializers/transaction_serializer.rb +1 -0
  71. data/lib/elastic_apm/util/deep_dup.rb +65 -0
  72. data/lib/elastic_apm/util/precision_validator.rb +1 -1
  73. data/lib/elastic_apm/version.rb +1 -1
  74. metadata +7 -2
@@ -31,7 +31,8 @@ module ElasticAPM
31
31
  begin
32
32
  config = ElasticAPM.agent.config
33
33
  ElasticAPM::Transport::Filters::HashSanitizer.new(
34
- key_patterns: config.custom_key_filters + config.sanitize_field_names
34
+ key_patterns: config.custom_key_filters +
35
+ config.sanitize_field_names
35
36
  )
36
37
  end
37
38
  end
@@ -53,7 +54,8 @@ module ElasticAPM
53
54
  if ElasticAPM.agent.config.capture_elasticsearch_queries
54
55
  unless args[1].nil? || args[1].empty?
55
56
  statement << {
56
- body: ElasticAPM::Spies::ElasticsearchSpy.sanitizer.strip_from!(args[1])
57
+ body: ElasticAPM::Spies::ElasticsearchSpy
58
+ .sanitizer.strip_from(args[1])
57
59
  }
58
60
  end
59
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
@@ -53,20 +55,25 @@ module ElasticAPM
53
55
  tmp_request = build_request(method) do |req|
54
56
  yield(req) if block_given?
55
57
  end
56
- uri = URI(tmp_request.path)
58
+ uri = tmp_request.path && URI(tmp_request.path)
57
59
  end
58
60
 
59
- host = uri.host
61
+ host = uri&.host || 'localhost'
60
62
 
61
63
  upcased_method = method.to_s.upcase
62
64
 
63
- destination = ElasticAPM::Span::Context::Destination.from_uri(uri)
64
-
65
- context =
66
- ElasticAPM::Span::Context.new(
67
- http: { url: uri, method: upcased_method },
68
- destination: destination
69
- )
65
+ if uri
66
+ destination = ElasticAPM::Span::Context::Destination.from_uri(uri)
67
+
68
+ context =
69
+ ElasticAPM::Span::Context.new(
70
+ http: { url: uri, method: upcased_method },
71
+ destination: destination
72
+ )
73
+ else
74
+ context =
75
+ ElasticAPM::Span::Context.new(http: { url: uri, method: upcased_method })
76
+ end
70
77
 
71
78
  ElasticAPM.with_span(
72
79
  "#{upcased_method} #{host}",
@@ -89,13 +96,14 @@ module ElasticAPM
89
96
  http.status_code = result.status.to_s
90
97
  end
91
98
 
99
+ span&.outcome = Span::Outcome.from_http_status(result.status)
92
100
  result
93
101
  end
94
102
  end
95
103
  end
96
104
  end
97
105
  end
98
- # rubocop:enable Metrics/CyclomaticComplexity
106
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
99
107
  end
100
108
 
101
109
  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,19 @@ module ElasticAPM
44
44
  end
45
45
 
46
46
  def failed(event)
47
- pop_event(event)
47
+ if (span = pop_event(event))
48
+ span.outcome = Span::Outcome::FAILURE
49
+ end
50
+
51
+ span
48
52
  end
49
53
 
50
54
  def succeeded(event)
51
- pop_event(event)
55
+ if span = pop_event(event)
56
+ span.outcome = Span::Outcome::SUCCESS
57
+ end
58
+
59
+ span
52
60
  end
53
61
 
54
62
  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
@@ -60,7 +61,7 @@ module ElasticAPM
60
61
  return request_without_apm(req, body, &block)
61
62
  end
62
63
 
63
- host = req['host']&.split(':')&.first || address
64
+ host = req['host']&.split(':')&.first || address || 'localhost'
64
65
  method = req.method.to_s.upcase
65
66
  path, query = req.path.split('?')
66
67
 
@@ -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?
@@ -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
 
@@ -27,7 +27,6 @@ module ElasticAPM
27
27
  TRACE_ID_LENGTH = 16
28
28
  ID_LENGTH = 8
29
29
 
30
- # rubocop:disable Metrics/ParameterLists
31
30
  def initialize(
32
31
  version: VERSION,
33
32
  trace_id: nil,
@@ -42,7 +41,6 @@ module ElasticAPM
42
41
  @id = id || hex(ID_LENGTH)
43
42
  @recorded = recorded
44
43
  end
45
- # rubocop:enable Metrics/ParameterLists
46
44
 
47
45
  attr_accessor :version, :id, :trace_id, :parent_id, :recorded
48
46
 
@@ -37,12 +37,13 @@ module ElasticAPM
37
37
  end
38
38
  end
39
39
 
40
+ # @api private
40
41
  class EsEntry
41
42
  ASSIGN = ':'
42
43
  SPLIT = ';'
43
44
 
44
- SHORT_TO_LONG = { 's' => 'sample_rate' }
45
- LONG_TO_SHORT = { 'sample_rate' => 's' }
45
+ SHORT_TO_LONG = { 's' => 'sample_rate' }.freeze
46
+ LONG_TO_SHORT = { 'sample_rate' => 's' }.freeze
46
47
 
47
48
  def initialize(values = nil)
48
49
  parse(values)
@@ -83,7 +84,7 @@ module ElasticAPM
83
84
 
84
85
  values.split(SPLIT).map do |kv|
85
86
  k, v = kv.split(ASSIGN)
86
- next unless SHORT_TO_LONG.keys.include?(k)
87
+ next unless SHORT_TO_LONG.key?(k)
87
88
  send("#{SHORT_TO_LONG[k]}=", v)
88
89
  end
89
90
  end
@@ -136,8 +137,9 @@ module ElasticAPM
136
137
  def split_by_nl_and_comma(str)
137
138
  # HTTP allows multiple headers with the same name, eg. multiple
138
139
  # Set-Cookie headers per response.
139
- # Rack handles this by joining the headers under the same key, separated
140
- # by newlines, see https://www.rubydoc.info/github/rack/rack/file/SPEC
140
+ # Rack handles this by joining the headers under the same key,
141
+ # separated by newlines.
142
+ # See https://www.rubydoc.info/github/rack/rack/file/SPEC
141
143
  String(str).split("\n").map { |s| s.split(',') }.flatten
142
144
  end
143
145
  end
@@ -20,6 +20,17 @@
20
20
  module ElasticAPM
21
21
  # @api private
22
22
  class Transaction
23
+ # @api private
24
+ class Outcome
25
+ FAILURE = "failure"
26
+ SUCCESS = "success"
27
+ UNKNOWN = "unknown"
28
+
29
+ def self.from_http_status(code)
30
+ code.to_i >= 500 ? FAILURE : SUCCESS
31
+ end
32
+ end
33
+
23
34
  extend Forwardable
24
35
  include ChildDurations::Methods
25
36
 
@@ -33,10 +44,10 @@ module ElasticAPM
33
44
  def initialize(
34
45
  name = nil,
35
46
  type = nil,
47
+ config:,
36
48
  sampled: true,
37
49
  sample_rate: 1,
38
50
  context: nil,
39
- config:,
40
51
  trace_context: nil
41
52
  )
42
53
  @name = name
@@ -63,7 +74,9 @@ module ElasticAPM
63
74
  unless (@trace_context = trace_context)
64
75
  @trace_context = TraceContext.new(
65
76
  traceparent: TraceContext::Traceparent.new(recorded: sampled),
66
- tracestate: TraceContext::Tracestate.new(sample_rate: sampled ? sample_rate : 0)
77
+ tracestate: TraceContext::Tracestate.new(
78
+ sample_rate: sampled ? sample_rate : 0
79
+ )
67
80
  )
68
81
  end
69
82
 
@@ -74,7 +87,7 @@ module ElasticAPM
74
87
  end
75
88
  # rubocop:enable Metrics/ParameterLists
76
89
 
77
- attr_accessor :name, :type, :result
90
+ attr_accessor :name, :type, :result, :outcome
78
91
 
79
92
  attr_reader(
80
93
  :breakdown_metrics,
@@ -90,7 +103,7 @@ module ElasticAPM
90
103
  :started_spans,
91
104
  :timestamp,
92
105
  :trace_context,
93
- :transaction_max_spans
106
+ :transaction_max_spans
94
107
  )
95
108
 
96
109
  alias :collect_metrics? :collect_metrics