atatus 1.7.0 → 2.0.0

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