atatus 1.7.0 → 2.0.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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/Gemfile +49 -13
  4. data/LICENSE +1 -1
  5. data/atatus.gemspec +3 -3
  6. data/lib/atatus/agent.rb +10 -7
  7. data/lib/atatus/central_config.rb +19 -8
  8. data/lib/atatus/collector/layer.rb +1 -1
  9. data/lib/atatus/{sql_summarizer.rb → config/log_level_map.rb} +22 -28
  10. data/lib/atatus/config/options.rb +2 -1
  11. data/lib/atatus/config/regexp_list.rb +1 -1
  12. data/lib/atatus/config/round_float.rb +31 -0
  13. data/lib/atatus/config/server_info.rb +50 -0
  14. data/lib/atatus/config/wildcard_pattern_list.rb +3 -1
  15. data/lib/atatus/config.rb +91 -70
  16. data/lib/atatus/context/request/socket.rb +1 -2
  17. data/lib/atatus/context/response.rb +1 -3
  18. data/lib/atatus/context.rb +3 -10
  19. data/lib/atatus/context_builder.rb +3 -3
  20. data/lib/atatus/error.rb +2 -1
  21. data/lib/atatus/error_builder.rb +1 -1
  22. data/lib/atatus/fields.rb +98 -0
  23. data/lib/atatus/graphql.rb +2 -0
  24. data/lib/atatus/grpc.rb +5 -7
  25. data/lib/atatus/instrumenter.rb +29 -25
  26. data/lib/atatus/metadata/cloud_info.rb +156 -0
  27. data/lib/atatus/metadata/service_info.rb +3 -3
  28. data/lib/atatus/metadata/system_info/container_info.rb +20 -8
  29. data/lib/atatus/metadata/system_info.rb +20 -5
  30. data/lib/atatus/metadata.rb +3 -1
  31. data/lib/atatus/metrics/cpu_mem_set.rb +10 -38
  32. data/lib/atatus/metrics/jvm_set.rb +88 -0
  33. data/lib/atatus/metrics/metric.rb +2 -0
  34. data/lib/atatus/metrics.rb +33 -16
  35. data/lib/atatus/middleware.rb +8 -3
  36. data/lib/atatus/naively_hashable.rb +1 -0
  37. data/lib/atatus/normalizers/rails/active_record.rb +25 -7
  38. data/lib/atatus/normalizers.rb +2 -2
  39. data/lib/atatus/opentracing.rb +5 -3
  40. data/lib/atatus/rails.rb +1 -1
  41. data/lib/atatus/span/context/db.rb +1 -1
  42. data/lib/atatus/span/context/destination.rb +58 -32
  43. data/lib/atatus/span/context/http.rb +2 -0
  44. data/lib/atatus/span/context/links.rb +32 -0
  45. data/lib/atatus/{sql.rb → span/context/message.rb} +16 -12
  46. data/lib/atatus/span/context/service.rb +55 -0
  47. data/lib/atatus/span/context.rb +28 -3
  48. data/lib/atatus/span.rb +35 -5
  49. data/lib/atatus/span_helpers.rb +12 -23
  50. data/lib/atatus/spies/action_dispatch.rb +10 -13
  51. data/lib/atatus/spies/azure_storage_table.rb +148 -0
  52. data/lib/atatus/spies/delayed_job.rb +19 -13
  53. data/lib/atatus/spies/dynamo_db.rb +56 -15
  54. data/lib/atatus/spies/elasticsearch.rb +54 -39
  55. data/lib/atatus/spies/faraday.rb +92 -58
  56. data/lib/atatus/spies/http.rb +33 -37
  57. data/lib/atatus/spies/json.rb +5 -9
  58. data/lib/atatus/spies/mongo.rb +26 -19
  59. data/lib/atatus/spies/net_http.rb +53 -51
  60. data/lib/atatus/spies/racecar.rb +77 -0
  61. data/lib/atatus/spies/rake.rb +27 -27
  62. data/lib/atatus/spies/redis.rb +11 -12
  63. data/lib/atatus/spies/resque.rb +18 -23
  64. data/lib/atatus/spies/s3.rb +132 -0
  65. data/lib/atatus/spies/sequel.rb +11 -2
  66. data/lib/atatus/spies/shoryuken.rb +4 -6
  67. data/lib/atatus/spies/sidekiq.rb +23 -31
  68. data/lib/atatus/spies/sinatra.rb +20 -28
  69. data/lib/atatus/spies/sneakers.rb +2 -0
  70. data/lib/atatus/spies/sns.rb +126 -0
  71. data/lib/atatus/spies/sqs.rb +231 -0
  72. data/lib/atatus/spies/sucker_punch.rb +20 -22
  73. data/lib/atatus/spies/tilt.rb +10 -13
  74. data/lib/atatus/spies.rb +20 -0
  75. data/lib/atatus/sql/signature.rb +4 -2
  76. data/lib/atatus/sql/tokenizer.rb +23 -7
  77. data/lib/atatus/stacktrace/frame.rb +1 -0
  78. data/lib/atatus/stacktrace_builder.rb +12 -16
  79. data/lib/atatus/subscriber.rb +1 -0
  80. data/lib/atatus/trace_context/traceparent.rb +5 -8
  81. data/lib/atatus/trace_context/tracestate.rb +16 -14
  82. data/lib/atatus/trace_context.rb +6 -16
  83. data/lib/atatus/transaction.rb +17 -4
  84. data/lib/atatus/transport/base.rb +1 -3
  85. data/lib/atatus/transport/connection/http.rb +11 -3
  86. data/lib/atatus/transport/connection/proxy_pipe.rb +1 -2
  87. data/lib/atatus/transport/connection.rb +3 -2
  88. data/lib/atatus/transport/filters/hash_sanitizer.rb +16 -34
  89. data/lib/atatus/transport/filters/secrets_filter.rb +35 -12
  90. data/lib/atatus/transport/serializers/context_serializer.rb +1 -2
  91. data/lib/atatus/transport/serializers/metadata_serializer.rb +54 -8
  92. data/lib/atatus/transport/serializers/metricset_serializer.rb +2 -2
  93. data/lib/atatus/transport/serializers/span_serializer.rb +55 -9
  94. data/lib/atatus/transport/serializers/transaction_serializer.rb +1 -0
  95. data/lib/atatus/transport/serializers.rb +9 -6
  96. data/lib/atatus/transport/user_agent.rb +16 -9
  97. data/lib/atatus/transport/worker.rb +2 -1
  98. data/lib/atatus/util/deep_dup.rb +65 -0
  99. data/lib/atatus/util/precision_validator.rb +46 -0
  100. data/lib/atatus/util.rb +2 -0
  101. data/lib/atatus/version.rb +1 -1
  102. data/lib/atatus.rb +32 -5
  103. metadata +40 -11
@@ -0,0 +1,148 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+ #
18
+ # frozen_string_literal: true
19
+
20
+ module Atatus
21
+ # @api private
22
+ module Spies
23
+ # @api private
24
+ class AzureStorageTableSpy
25
+ TYPE = "storage"
26
+ SUBTYPE = "azuretable"
27
+
28
+ module Helpers
29
+ class << self
30
+ def instrument(operation_name, table_name = nil, service:)
31
+ span_name = span_name(operation_name, table_name)
32
+ action = formatted_op_name(operation_name)
33
+ account_name = account_name_from_storage_table_host(service.storage_service_host[:primary])
34
+
35
+ destination = Atatus::Span::Context::Destination.from_uri(service.storage_service_host[:primary])
36
+ destination.service.resource = "#{SUBTYPE}/#{account_name}"
37
+
38
+ context = Atatus::Span::Context.new(destination: destination)
39
+
40
+ Atatus.with_span(span_name, TYPE, subtype: SUBTYPE, action: action, context: context) do
41
+ Atatus::Spies.without_faraday do
42
+ Atatus::Spies.without_net_http do
43
+ yield
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ DEFAULT_OP_NAMES = {
52
+ "create_table" => "Create",
53
+ "delete_table" => "Delete",
54
+ "get_table_acl" => "GetAcl",
55
+ "set_table_acl" => "SetAcl",
56
+ "insert_entity" => "Insert",
57
+ "query_entities" => "Query",
58
+ "update_entity" => "Update",
59
+ "merge_entity" => "Merge",
60
+ "delete_entity" => "Delete"
61
+ }.freeze
62
+
63
+ def formatted_op_names
64
+ @formatted_op_names ||= Concurrent::Map.new
65
+ end
66
+
67
+ def account_names
68
+ @account_names ||= Concurrent::Map.new
69
+ end
70
+
71
+ def span_name(operation_name, table_name = nil)
72
+ base = "AzureTable #{formatted_op_name(operation_name)}"
73
+
74
+ return base unless table_name
75
+
76
+ "#{base} #{table_name}"
77
+ end
78
+
79
+ def formatted_op_name(operation_name)
80
+ formatted_op_names.compute_if_absent(operation_name) do
81
+ DEFAULT_OP_NAMES.fetch(operation_name) do
82
+ operation_name.to_s.split("_").collect(&:capitalize).join
83
+ end
84
+ end
85
+ end
86
+
87
+ def account_name_from_storage_table_host(host)
88
+ account_names.compute_if_absent(host) do
89
+ URI(host).host.split(".").first || "unknown"
90
+ end
91
+ rescue Exception
92
+ "unknown"
93
+ end
94
+ end
95
+ end
96
+
97
+ # @api private
98
+ module Ext
99
+ # Methods with table_name as first parameter
100
+ %i[
101
+ create_table
102
+ delete_table
103
+ get_table
104
+ get_table_acl
105
+ set_table_acl
106
+ insert_entity
107
+ query_entities
108
+ update_entity
109
+ merge_entity
110
+ delete_entity
111
+ ].each do |method_name|
112
+ define_method(method_name) do |table_name, *args|
113
+ unless (transaction = Atatus.current_transaction)
114
+ return super(table_name, *args)
115
+ end
116
+
117
+ Atatus::Spies::AzureStorageTableSpy::Helpers.instrument(
118
+ method_name.to_s, table_name, service: self
119
+ ) do
120
+ super(table_name, *args)
121
+ end
122
+ end
123
+ end
124
+
125
+ # Methods WITHOUT table_name as first parameter
126
+ def query_tables(*args)
127
+ unless (transaction = Atatus.current_transaction)
128
+ return super(*args)
129
+ end
130
+
131
+ Atatus::Spies::AzureStorageTableSpy::Helpers.instrument("query_tables", service: self) do
132
+ super(*args)
133
+ end
134
+ end
135
+ end
136
+
137
+ def install
138
+ ::Azure::Storage::Table::TableService.prepend(Ext)
139
+ end
140
+ end
141
+
142
+ register(
143
+ "Azure::Storage::Table::TableService",
144
+ "azure/storage/table",
145
+ AzureStorageTableSpy.new
146
+ )
147
+ end
148
+ end
@@ -27,39 +27,45 @@ module Atatus
27
27
  TYPE = 'Delayed::Job'
28
28
 
29
29
  def install
30
- if defined?(::Delayed) && defined?(::Delayed::Backend) && defined?(::Delayed::Backend::Base)
30
+ ::Delayed::Backend::Base.class_eval do
31
+ alias invoke_job_without_apm invoke_job
31
32
 
32
- ::Delayed::Backend::Base.class_eval do
33
- alias invoke_job_without_apm invoke_job
34
-
35
- def invoke_job(*args, &block)
36
- ::Atatus::Spies::DelayedJobSpy
37
- .invoke_job(self, *args, &block)
38
- end
33
+ def invoke_job(*args, &block)
34
+ ::Atatus::Spies::DelayedJobSpy
35
+ .invoke_job(self, *args, &block)
39
36
  end
40
-
41
37
  end
42
38
  end
43
39
 
44
40
  def self.invoke_job(job, *args, &block)
45
- job_name = name_from_payload(job.payload_object)
41
+ job_name = job_name(job)
46
42
  transaction = Atatus.start_transaction(job_name, TYPE)
47
43
  job.invoke_job_without_apm(*args, &block)
48
- transaction.done 'success'
44
+ transaction&.done 'success'
45
+ transaction&.outcome = Transaction::Outcome::SUCCESS
49
46
  rescue ::Exception => e
50
47
  Atatus.report(e, handled: false)
51
- transaction.done 'error'
48
+ transaction&.done 'error'
49
+ transaction&.outcome = Transaction::Outcome::FAILURE
52
50
  raise
53
51
  ensure
54
52
  Atatus.end_transaction
55
53
  end
56
54
 
57
- def self.name_from_payload(payload_object)
55
+ def self.job_name(job)
56
+ payload_object = job.payload_object
57
+
58
58
  if payload_object.is_a?(::Delayed::PerformableMethod)
59
59
  performable_method_name(payload_object)
60
+ elsif payload_object.instance_of?(
61
+ ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper
62
+ )
63
+ payload_object.job_data['job_class']
60
64
  else
61
65
  payload_object.class.name
62
66
  end
67
+ rescue
68
+ job.name
63
69
  end
64
70
 
65
71
  def self.performable_method_name(payload_object)
@@ -22,35 +22,76 @@ module Atatus
22
22
  module Spies
23
23
  # @api private
24
24
  class DynamoDBSpy
25
+ TYPE = 'db'
26
+ SUBTYPE = 'dynamodb'
27
+ ACTION = 'query'
28
+
29
+ @@formatted_op_names = Concurrent::Map.new
30
+
25
31
  def self.without_net_http
26
32
  return yield unless defined?(NetHTTPSpy)
27
33
 
34
+ # rubocop:disable Style/ExplicitBlockArgument
28
35
  Atatus::Spies::NetHTTPSpy.disable_in do
29
36
  yield
30
37
  end
38
+ # rubocop:enable Style/ExplicitBlockArgument
31
39
  end
32
40
 
33
- def install
34
- if defined?(::Aws) && defined?(::Aws::DynamoDB) && defined?(::Aws::DynamoDB::Client)
35
-
36
- ::Aws::DynamoDB::Client.class_eval do
37
- # Alias all available operations
38
- api.operation_names.each do |operation_name|
39
- alias :"#{operation_name}_without_apm" :"#{operation_name}"
40
-
41
- define_method(operation_name) do |params = {}, options = {}|
42
- Atatus.with_span(operation_name, 'db', subtype: 'dynamodb', action: operation_name) do
43
- Atatus::Spies::DynamoDBSpy.without_net_http do
44
- original_method = method("#{operation_name}_without_apm")
45
- original_method.call(params, options)
46
- end
41
+ def self.span_name(operation_name, params)
42
+ params[:table_name] ?
43
+ "DynamoDB #{formatted_op_name(operation_name)} #{params[:table_name]}" :
44
+ "DynamoDB #{formatted_op_name(operation_name)}"
45
+ end
46
+
47
+ def self.formatted_op_name(operation_name)
48
+ @@formatted_op_names.compute_if_absent(operation_name) do
49
+ operation_name.to_s.split('_').collect(&:capitalize).join
50
+ end
51
+ end
52
+
53
+ # @api private
54
+ module Ext
55
+ def self.prepended(mod)
56
+ # Alias all available operations
57
+ mod.api.operation_names.each do |operation_name|
58
+ define_method(operation_name) do |params = {}, options = {}|
59
+ context = Atatus::Span::Context.new(
60
+ db: {
61
+ instance: config.region,
62
+ type: SUBTYPE,
63
+ statement: params[:key_condition_expression]
64
+ },
65
+ destination: {
66
+ address: config.endpoint.host,
67
+ port: config.endpoint.port,
68
+ service: {
69
+ name: SUBTYPE,
70
+ type: TYPE,
71
+ resource: SUBTYPE },
72
+ cloud: { region: config.region }
73
+ }
74
+ )
75
+
76
+ Atatus.with_span(
77
+ Atatus::Spies::DynamoDBSpy.span_name(operation_name, params),
78
+ TYPE,
79
+ subtype: SUBTYPE,
80
+ action: ACTION,
81
+ context: context
82
+ ) do
83
+ Atatus::Spies::DynamoDBSpy.without_net_http do
84
+ super(params, options)
47
85
  end
48
86
  end
49
87
  end
50
88
  end
51
-
52
89
  end
53
90
  end
91
+
92
+ def install
93
+ ::Aws::DynamoDB::Client.prepend(Ext)
94
+ end
54
95
  end
55
96
 
56
97
  register(
@@ -27,53 +27,62 @@ module Atatus
27
27
  SUBTYPE = 'elasticsearch'
28
28
 
29
29
  def self.sanitizer
30
- @sanitizer ||= Atatus::Transport::Filters::HashSanitizer.new
30
+ @sanitizer ||=
31
+ begin
32
+ config = Atatus.agent.config
33
+ Atatus::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
- def install
34
- if defined?(::Elasticsearch) &&
35
- defined?(::Elasticsearch::Transport) &&
36
- defined?(::Elasticsearch::Transport::Client)
37
-
38
- ::Elasticsearch::Transport::Client.class_eval do
39
- alias perform_request_without_apm perform_request
40
-
41
- def perform_request(method, path, *args, &block)
42
- unless Atatus.current_transaction
43
- return perform_request_without_apm(method, path, *args, &block)
44
- end
45
-
46
- name = format(NAME_FORMAT, method, path)
47
- statement = []
48
-
49
- statement << { params: args&.[](0) }
40
+ # @api private
41
+ module Ext
42
+ def perform_request(method, path, *args, &block)
43
+ unless Atatus.current_transaction
44
+ return super(method, path, *args, &block)
45
+ end
50
46
 
51
- if Atatus.agent.config.capture_elasticsearch_queries
52
- unless args[1].nil? || args[1].empty?
53
- statement << {
54
- body: Atatus::Spies::ElasticsearchSpy.sanitizer.strip_from!(args[1])
55
- }
56
- end
57
- end
47
+ name = format(NAME_FORMAT, method, path)
48
+ statement = []
58
49
 
59
- context = Span::Context.new(
60
- db: { statement: statement.reduce({}, :merge).to_json },
61
- destination: {
62
- name: SUBTYPE,
63
- resource: SUBTYPE,
64
- type: TYPE
65
- }
66
- )
50
+ statement << { params: args&.[](0) }
67
51
 
68
- Atatus.with_span(
69
- name,
70
- TYPE,
71
- subtype: SUBTYPE,
72
- context: context
73
- ) { perform_request_without_apm(method, path, *args, &block) }
52
+ if Atatus.agent.config.capture_elasticsearch_queries
53
+ unless args[1].nil? || args[1].empty?
54
+ body =
55
+ Atatus::Spies::ElasticsearchSpy
56
+ .sanitizer.strip_from(args[1])
57
+ statement << { body: body }
74
58
  end
75
59
  end
76
60
 
61
+ context = Span::Context.new(
62
+ db: { statement: statement.reduce({}, :merge).to_json },
63
+ destination: {
64
+ service: {
65
+ name: SUBTYPE,
66
+ resource: SUBTYPE,
67
+ type: TYPE
68
+ }
69
+ }
70
+ )
71
+
72
+ Atatus.with_span(
73
+ name,
74
+ TYPE,
75
+ subtype: SUBTYPE,
76
+ context: context
77
+ ) { super(method, path, *args, &block) }
78
+ end
79
+ end
80
+
81
+ def install
82
+ if defined?(::Elastic::Transport::Client)
83
+ ::Elastic::Transport::Client.prepend(Ext)
84
+ elsif defined?(::Elasticsearch::Transport::Client)
85
+ ::Elasticsearch::Transport::Client.prepend(Ext)
77
86
  end
78
87
  end
79
88
  end
@@ -83,5 +92,11 @@ module Atatus
83
92
  'elasticsearch-transport',
84
93
  ElasticsearchSpy.new
85
94
  )
95
+
96
+ register(
97
+ 'Elastic::Transport::Client',
98
+ 'elastic-transport',
99
+ ElasticsearchSpy.new
100
+ )
86
101
  end
87
102
  end
@@ -22,84 +22,118 @@ module Atatus
22
22
  module Spies
23
23
  # @api private
24
24
  class FaradaySpy
25
- TYPE = 'ext'
26
- SUBTYPE = 'faraday'
25
+ DISABLE_KEY = :__atatus_faraday_disabled
26
+ TYPE = 'external'
27
+ SUBTYPE = 'http'
27
28
 
28
- def self.without_net_http
29
- return yield unless defined?(NetHTTPSpy)
30
-
31
- Atatus::Spies::NetHTTPSpy.disable_in do
32
- yield
29
+ class << self
30
+ def disabled=(disabled)
31
+ Thread.current[DISABLE_KEY] = disabled
33
32
  end
34
- end
35
-
36
- # rubocop:disable Metrics/CyclomaticComplexity
37
- def install
38
- if defined?(::Faraday) && defined?(::Faraday::Connection)
39
33
 
40
- ::Faraday::Connection.class_eval do
41
- alias run_request_without_apm run_request
42
-
43
- def run_request(method, url, body, headers, &block)
44
- unless (transaction = Atatus.current_transaction)
45
- return run_request_without_apm(method, url, body, headers, &block)
46
- end
34
+ def disabled?
35
+ Thread.current[DISABLE_KEY] ||= false
36
+ end
47
37
 
48
- uri = URI(build_url(url))
38
+ def disable_in
39
+ self.disabled = true
49
40
 
50
- # If url is set inside block it isn't available until yield,
51
- # so we temporarily build the request to yield. This could be a
52
- # problem if the block has side effects as it will be yielded twice
53
- # ~mikker
54
- unless uri.host
55
- tmp_request = build_request(method) do |req|
56
- yield(req) if block_given?
57
- end
58
- uri = URI(tmp_request.path)
59
- end
41
+ begin
42
+ yield
43
+ ensure
44
+ self.disabled = false
45
+ end
46
+ end
47
+ end
60
48
 
61
- host = uri.host
49
+ # @api private
50
+ module Ext
51
+ # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
52
+ def run_request(method, url, body, headers, &block)
53
+ unless (transaction = Atatus.current_transaction)
54
+ return super(method, url, body, headers, &block)
55
+ end
62
56
 
63
- upcased_method = method.to_s.upcase
57
+ if Atatus::Spies::FaradaySpy.disabled?
58
+ return super(method, url, body, headers, &block)
59
+ end
64
60
 
65
- destination = Atatus::Span::Context::Destination.from_uri(uri)
61
+ uri = URI(build_url(url))
66
62
 
67
- context =
68
- Atatus::Span::Context.new(
69
- http: { url: uri, method: upcased_method },
70
- destination: destination
71
- )
63
+ # If url is set inside block it isn't available until yield,
64
+ # so we temporarily build the request to yield. This could be a
65
+ # problem if the block has side effects as it will be yielded twice
66
+ # ~mikker
67
+ unless uri.host
68
+ tmp_request = build_request(method) do |req|
69
+ yield(req) if block_given?
70
+ end
71
+ uri = tmp_request.path && URI(tmp_request.path)
72
+ end
72
73
 
73
- Atatus.with_span(
74
- "#{upcased_method} #{host}",
75
- TYPE,
76
- subtype: SUBTYPE,
77
- action: upcased_method,
78
- context: context
79
- ) do |span|
80
- Atatus::Spies::FaradaySpy.without_net_http do
81
- trace_context = span&.trace_context || transaction.trace_context
74
+ host = uri&.host || 'localhost'
82
75
 
83
- result =
84
- run_request_without_apm(method, url, body, headers) do |req|
85
- trace_context.apply_headers { |k, v| req[k] = v }
76
+ upcased_method = method.to_s.upcase
86
77
 
87
- yield req if block_given?
88
- end
78
+ if uri
79
+ destination = Atatus::Span::Context::Destination.from_uri(uri, type: SUBTYPE)
89
80
 
90
- if (http = span&.context&.http)
91
- http.status_code = result.status.to_s
92
- end
81
+ context =
82
+ Atatus::Span::Context.new(
83
+ http: { url: uri, method: upcased_method },
84
+ destination: destination
85
+ )
86
+ else
87
+ context =
88
+ Atatus::Span::Context.new(http: { url: uri, method: upcased_method })
89
+ end
93
90
 
94
- result
91
+ context =
92
+ Atatus::Span::Context.new(
93
+ http: { url: uri, method: upcased_method },
94
+ destination: destination
95
+ )
96
+
97
+ Atatus.with_span(
98
+ "#{upcased_method} #{host}",
99
+ TYPE,
100
+ subtype: SUBTYPE,
101
+ context: context
102
+ ) do |span|
103
+ Atatus::Spies.without_net_http do
104
+ trace_context = span&.trace_context || transaction.trace_context
105
+
106
+ begin
107
+ result = super(method, url, body, headers) do |req|
108
+ trace_context.apply_headers { |k, v| req[k] = v }
109
+ yield req if block
110
+ end
111
+ rescue Faraday::ClientError, Faraday::ServerError => e # Faraday::Response::RaiseError
112
+ status = e.response_status if e.respond_to?(:response_status)
113
+ status ||= e.response&.fetch(:status)
114
+ http = span&.context&.http
115
+ if http && status
116
+ http.status_code = status.to_s
117
+ span.outcome = Span::Outcome.from_http_status(status)
95
118
  end
119
+ raise e
120
+ end
121
+
122
+ if (http = span&.context&.http)
123
+ http.status_code = result.status.to_s
96
124
  end
125
+
126
+ span&.outcome = Span::Outcome.from_http_status(result.status)
127
+ result
97
128
  end
98
129
  end
99
-
100
130
  end
101
131
  end
102
- # rubocop:enable Metrics/CyclomaticComplexity
132
+ # rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
133
+
134
+ def install
135
+ ::Faraday::Connection.prepend(Ext)
136
+ end
103
137
  end
104
138
 
105
139
  register 'Faraday', 'faraday', FaradaySpy.new
@@ -22,54 +22,50 @@ module Atatus
22
22
  module Spies
23
23
  # @api private
24
24
  class HTTPSpy
25
- TYPE = 'ext'
26
- SUBTYPE = 'http_rb'
27
- def install
28
- if defined?(::HTTP) && defined?(::HTTP::Client)
29
-
30
- ::HTTP::Client.class_eval do
31
- alias perform_without_apm perform
32
-
33
- def perform(req, options)
34
- unless (transaction = Atatus.current_transaction)
35
- return perform_without_apm(req, options)
36
- end
25
+ TYPE = 'external'
26
+ SUBTYPE = 'http'
37
27
 
38
- method = req.verb.to_s.upcase
39
- host = req.uri.host
28
+ # @api private
29
+ module Ext
30
+ def perform(req, options)
31
+ unless (transaction = Atatus.current_transaction)
32
+ return super(req, options)
33
+ end
40
34
 
41
- destination =
42
- Atatus::Span::Context::Destination.from_uri(req.uri)
43
- context = Atatus::Span::Context.new(
44
- http: { url: req.uri, method: method },
45
- destination: destination
46
- )
35
+ method = req.verb.to_s.upcase
36
+ host = req.uri.host
47
37
 
48
- name = "#{method} #{host}"
38
+ context = Atatus::Span::Context.new(
39
+ http: { url: req.uri, method: method },
40
+ destination: Atatus::Span::Context::Destination.from_uri(req.uri, type: SUBTYPE)
41
+ )
49
42
 
50
- Atatus.with_span(
51
- name,
52
- TYPE,
53
- subtype: SUBTYPE,
54
- action: method,
55
- context: context
56
- ) do |span|
57
- trace_context = span&.trace_context || transaction.trace_context
58
- trace_context.apply_headers { |key, value| req[key] = value }
43
+ name = "#{method} #{host}"
59
44
 
60
- result = perform_without_apm(req, options)
45
+ Atatus.with_span(
46
+ name,
47
+ TYPE,
48
+ subtype: SUBTYPE,
49
+ context: context
50
+ ) do |span|
51
+ trace_context = span&.trace_context || transaction.trace_context
52
+ trace_context.apply_headers { |key, value| req[key] = value }
61
53
 
62
- if (http = span&.context&.http)
63
- http.status_code = result.status.to_s
64
- end
54
+ result = super(req, options)
65
55
 
66
- result
67
- end
56
+ if (http = span&.context&.http)
57
+ http.status_code = result.status.to_s
68
58
  end
69
- end
70
59
 
60
+ span&.outcome = Span::Outcome.from_http_status(result.status)
61
+ result
62
+ end
71
63
  end
72
64
  end
65
+
66
+ def install
67
+ ::HTTP::Client.prepend(Ext)
68
+ end
73
69
  end
74
70
 
75
71
  register 'HTTP', 'http', HTTPSpy.new