elastic-apm 3.11.0 → 3.14.0

Sign up to get free protection for your applications and to get access to all the features.
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