opentelemetry-instrumentation-httpx 0.2.1 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 30bf96ab2ff772a00b7bf0d1a0be504223461df614db85924f27ceb05174cef1
4
- data.tar.gz: b291ae28f4cc9d2d62f7ba98648fa70d1d122c204646bdb084f0be864940d552
3
+ metadata.gz: 6c8a1ef0156cbdb24361ce0b786c842a284a5ba882cfa3c9acdd5d1b74774dd2
4
+ data.tar.gz: 06cecad615cba89782a5eda29c1e7d5de1a7af8db5b172b3e9756349d199290e
5
5
  SHA512:
6
- metadata.gz: 16ca1e7c26803cdca094cebb24ee417e55c2c07d3cffafd0ae438c6b5260a67662a6c77d6a88732a7a26990ea7fcae2d534441b00bfa972cd6e74bfb39b04fd4
7
- data.tar.gz: e1d69f6302e2773db472ca4c4053038866af2d8cb4838481c8fbe806af97e9276fc2ab90e9b91505e2d37c2f2d7afb6082557975a033c4d237be03e0655e0fd6
6
+ metadata.gz: 23b8e7faa82ddfe1f9d7a86a7dd8ec14a7ecb0f651c9a2c6d6ec7c08ea97cdbc1ab74d108ccbc54711ae2bad9c539e8c4e38be05bd1089817860aa7715419475
7
+ data.tar.gz: 0c903889f51a88a7f876712700f7f089e16280d9a459f46c2e86c5f3cd9db856cef079473fef5760e92d805746c288f2f633510380b477cd187988925b3c0078
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Release History: opentelemetry-instrumentation-httpx
2
2
 
3
+ ### v0.3.0 / 2025-08-12
4
+
5
+ * ADDED: HTTPX `OTEL_SEMCONV_STABILITY_OPT_IN` environment variable [#1589](https://github.com/open-telemetry/opentelemetry-ruby-contrib/pull/1589)
6
+
3
7
  ### v0.2.1 / 2025-04-29
4
8
 
5
9
  * FIXED: Httpx instrumentation trace context propagation
data/README.md CHANGED
@@ -48,3 +48,19 @@ The `opentelemetry-instrumentation-httpx` gem is distributed under the Apache 2.
48
48
  [slack-channel]: https://cloud-native.slack.com/archives/C01NWKKMKMY
49
49
  [discussions-url]: https://github.com/open-telemetry/opentelemetry-ruby/discussions
50
50
  [httpx-home]: https://github.com/HoneyryderChuck/httpx
51
+
52
+ ## HTTP semantic convention stability
53
+
54
+ In the OpenTelemetry ecosystem, HTTP semantic conventions have now reached a stable state. However, the initial HTTPX instrumentation was introduced before this stability was achieved, which resulted in HTTP attributes being based on an older version of the semantic conventions.
55
+
56
+ To facilitate the migration to stable semantic conventions, you can use the `OTEL_SEMCONV_STABILITY_OPT_IN` environment variable. This variable allows you to opt-in to the new stable conventions, ensuring compatibility and future-proofing your instrumentation.
57
+
58
+ When setting the value for `OTEL_SEMCONV_STABILITY_OPT_IN`, you can specify which conventions you wish to adopt:
59
+
60
+ - `http` - Emits the stable HTTP and networking conventions and ceases emitting the old conventions previously emitted by the instrumentation.
61
+ - `http/dup` - Emits both the old and stable HTTP and networking conventions, enabling a phased rollout of the stable semantic conventions.
62
+ - Default behavior (in the absence of either value) is to continue emitting the old HTTP and networking conventions the instrumentation previously emitted.
63
+
64
+ During the transition from old to stable conventions, HTTPX instrumentation code comes in three patch versions: `dup`, `old`, and `stable`. These versions are identical except for the attributes they send. Any changes to HTTPX instrumentation should consider all three patches.
65
+
66
+ For additional information on migration, please refer to our [documentation](https://opentelemetry.io/docs/specs/semconv/non-normative/http-migration/).
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright The OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ module OpenTelemetry
8
+ module Instrumentation
9
+ module HTTPX
10
+ module Dup
11
+ module Plugin
12
+ # Instruments around HTTPX's request/response lifecycle in order to generate
13
+ # an OTEL trace.
14
+ module RequestTracer
15
+ module_function
16
+
17
+ # initializes tracing on the +request+.
18
+ def call(request)
19
+ span = nil
20
+
21
+ # request objects are reused, when already buffered requests get rerouted to a different
22
+ # connection due to connection issues, or when they already got a response, but need to
23
+ # be retried. In such situations, the original span needs to be extended for the former,
24
+ # while a new is required for the latter.
25
+ request.on(:idle) do
26
+ span = nil
27
+ end
28
+ # the span is initialized when the request is buffered in the parser, which is the closest
29
+ # one gets to actually sending the request.
30
+ request.on(:headers) do
31
+ next if span
32
+
33
+ span = initialize_span(request)
34
+ end
35
+
36
+ request.on(:response) do |response|
37
+ unless span
38
+ next unless response.is_a?(::HTTPX::ErrorResponse) && response.error.respond_to?(:connection)
39
+
40
+ # handles the case when the +error+ happened during name resolution, which means
41
+ # that the tracing start point hasn't been triggered yet; in such cases, the approximate
42
+ # initial resolving time is collected from the connection, and used as span start time,
43
+ # and the tracing object in inserted before the on response callback is called.
44
+ span = initialize_span(request, response.error.connection.init_time)
45
+
46
+ end
47
+
48
+ finish(response, span)
49
+ end
50
+ end
51
+
52
+ def finish(response, span)
53
+ if response.is_a?(::HTTPX::ErrorResponse)
54
+ span.record_exception(response.error)
55
+ span.status = Trace::Status.error(response.error.to_s)
56
+ else
57
+ span.set_attribute(OpenTelemetry::SemanticConventions::Trace::HTTP_STATUS_CODE, response.status)
58
+ span.set_attribute('http.response.status_code', response.status)
59
+
60
+ if response.status.between?(400, 599)
61
+ err = ::HTTPX::HTTPError.new(response)
62
+ span.record_exception(err)
63
+ span.status = Trace::Status.error(err.to_s)
64
+ end
65
+ end
66
+
67
+ span.finish
68
+ end
69
+
70
+ # return a span initialized with the +@request+ state.
71
+ def initialize_span(request, start_time = ::Time.now)
72
+ verb = request.verb
73
+ uri = request.uri
74
+
75
+ config = HTTPX::Instrumentation.instance.config
76
+
77
+ attributes = {
78
+ OpenTelemetry::SemanticConventions::Trace::HTTP_HOST => uri.host,
79
+ OpenTelemetry::SemanticConventions::Trace::HTTP_METHOD => verb,
80
+ OpenTelemetry::SemanticConventions::Trace::HTTP_SCHEME => uri.scheme,
81
+ OpenTelemetry::SemanticConventions::Trace::HTTP_TARGET => uri.path,
82
+ OpenTelemetry::SemanticConventions::Trace::HTTP_URL => "#{uri.scheme}://#{uri.host}",
83
+ OpenTelemetry::SemanticConventions::Trace::NET_PEER_NAME => uri.host,
84
+ OpenTelemetry::SemanticConventions::Trace::NET_PEER_PORT => uri.port,
85
+ 'http.request.method' => verb,
86
+ 'url.scheme' => uri.scheme,
87
+ 'url.path' => uri.path,
88
+ 'url.full' => "#{uri.scheme}://#{uri.host}",
89
+ 'server.address' => uri.host,
90
+ 'server.port' => uri.port
91
+ }
92
+
93
+ attributes['url.query'] = uri.query unless uri.query.nil?
94
+ attributes[OpenTelemetry::SemanticConventions::Trace::PEER_SERVICE] = config[:peer_service] if config[:peer_service]
95
+ attributes.merge!(OpenTelemetry::Common::HTTP::ClientContext.attributes)
96
+
97
+ span = tracer.start_span(verb, attributes: attributes, kind: :client, start_timestamp: start_time)
98
+
99
+ OpenTelemetry::Trace.with_span(span) do
100
+ OpenTelemetry.propagation.inject(request.headers)
101
+ end
102
+
103
+ span
104
+ rescue StandardError => e
105
+ OpenTelemetry.handle_error(exception: e)
106
+ end
107
+
108
+ def tracer
109
+ HTTPX::Instrumentation.instance.tracer
110
+ end
111
+ end
112
+
113
+ # Request patch to initiate the trace on initialization.
114
+ module RequestMethods
115
+ def initialize(*)
116
+ super
117
+
118
+ RequestTracer.call(self)
119
+ end
120
+ end
121
+
122
+ # Connection patch to start monitoring on initialization.
123
+ module ConnectionMethods
124
+ attr_reader :init_time
125
+
126
+ def initialize(*)
127
+ super
128
+
129
+ @init_time = ::Time.now
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -10,8 +10,9 @@ module OpenTelemetry
10
10
  # The Instrumentation class contains logic to detect and install the Http instrumentation
11
11
  class Instrumentation < OpenTelemetry::Instrumentation::Base
12
12
  install do |_config|
13
- require_dependencies
14
- patch
13
+ patch_type = determine_semconv
14
+ send(:"require_dependencies_#{patch_type}")
15
+ send(:"patch_#{patch_type}")
15
16
  end
16
17
 
17
18
  compatible do
@@ -24,15 +25,50 @@ module OpenTelemetry
24
25
 
25
26
  option :peer_service, default: nil, validate: :string
26
27
 
27
- def patch
28
- otel_session = ::HTTPX.plugin(Plugin)
28
+ def determine_semconv
29
+ stability_opt_in = ENV.fetch('OTEL_SEMCONV_STABILITY_OPT_IN', '')
30
+ values = stability_opt_in.split(',').map(&:strip)
31
+
32
+ if values.include?('http/dup')
33
+ 'dup'
34
+ elsif values.include?('http')
35
+ 'stable'
36
+ else
37
+ 'old'
38
+ end
39
+ end
40
+
41
+ def patch_old
42
+ otel_session = ::HTTPX.plugin(Old::Plugin)
29
43
 
30
44
  ::HTTPX.send(:remove_const, :Session)
31
45
  ::HTTPX.send(:const_set, :Session, otel_session.class)
32
46
  end
33
47
 
34
- def require_dependencies
35
- require_relative 'plugin'
48
+ def patch_stable
49
+ otel_session = ::HTTPX.plugin(Stable::Plugin)
50
+
51
+ ::HTTPX.send(:remove_const, :Session)
52
+ ::HTTPX.send(:const_set, :Session, otel_session.class)
53
+ end
54
+
55
+ def patch_dup
56
+ otel_session = ::HTTPX.plugin(Dup::Plugin)
57
+
58
+ ::HTTPX.send(:remove_const, :Session)
59
+ ::HTTPX.send(:const_set, :Session, otel_session.class)
60
+ end
61
+
62
+ def require_dependencies_old
63
+ require_relative 'old/plugin'
64
+ end
65
+
66
+ def require_dependencies_stable
67
+ require_relative 'stable/plugin'
68
+ end
69
+
70
+ def require_dependencies_dup
71
+ require_relative 'dup/plugin'
36
72
  end
37
73
  end
38
74
  end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright The OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ module OpenTelemetry
8
+ module Instrumentation
9
+ module HTTPX
10
+ module Old
11
+ module Plugin
12
+ # Instruments around HTTPX's request/response lifecycle in order to generate
13
+ # an OTEL trace.
14
+ module RequestTracer
15
+ module_function
16
+
17
+ # initializes tracing on the +request+.
18
+ def call(request)
19
+ span = nil
20
+
21
+ # request objects are reused, when already buffered requests get rerouted to a different
22
+ # connection due to connection issues, or when they already got a response, but need to
23
+ # be retried. In such situations, the original span needs to be extended for the former,
24
+ # while a new is required for the latter.
25
+ request.on(:idle) do
26
+ span = nil
27
+ end
28
+ # the span is initialized when the request is buffered in the parser, which is the closest
29
+ # one gets to actually sending the request.
30
+ request.on(:headers) do
31
+ next if span
32
+
33
+ span = initialize_span(request)
34
+ end
35
+
36
+ request.on(:response) do |response|
37
+ unless span
38
+ next unless response.is_a?(::HTTPX::ErrorResponse) && response.error.respond_to?(:connection)
39
+
40
+ # handles the case when the +error+ happened during name resolution, which means
41
+ # that the tracing start point hasn't been triggered yet; in such cases, the approximate
42
+ # initial resolving time is collected from the connection, and used as span start time,
43
+ # and the tracing object in inserted before the on response callback is called.
44
+ span = initialize_span(request, response.error.connection.init_time)
45
+
46
+ end
47
+
48
+ finish(response, span)
49
+ end
50
+ end
51
+
52
+ def finish(response, span)
53
+ if response.is_a?(::HTTPX::ErrorResponse)
54
+ span.record_exception(response.error)
55
+ span.status = Trace::Status.error(response.error.to_s)
56
+ else
57
+ span.set_attribute(OpenTelemetry::SemanticConventions::Trace::HTTP_STATUS_CODE, response.status)
58
+
59
+ if response.status.between?(400, 599)
60
+ err = ::HTTPX::HTTPError.new(response)
61
+ span.record_exception(err)
62
+ span.status = Trace::Status.error(err.to_s)
63
+ end
64
+ end
65
+
66
+ span.finish
67
+ end
68
+
69
+ # return a span initialized with the +@request+ state.
70
+ def initialize_span(request, start_time = ::Time.now)
71
+ verb = request.verb
72
+ uri = request.uri
73
+
74
+ config = HTTPX::Instrumentation.instance.config
75
+
76
+ attributes = {
77
+ OpenTelemetry::SemanticConventions::Trace::HTTP_HOST => uri.host,
78
+ OpenTelemetry::SemanticConventions::Trace::HTTP_METHOD => verb,
79
+ OpenTelemetry::SemanticConventions::Trace::HTTP_SCHEME => uri.scheme,
80
+ OpenTelemetry::SemanticConventions::Trace::HTTP_TARGET => uri.path,
81
+ OpenTelemetry::SemanticConventions::Trace::HTTP_URL => "#{uri.scheme}://#{uri.host}",
82
+ OpenTelemetry::SemanticConventions::Trace::NET_PEER_NAME => uri.host,
83
+ OpenTelemetry::SemanticConventions::Trace::NET_PEER_PORT => uri.port
84
+ }
85
+
86
+ attributes[OpenTelemetry::SemanticConventions::Trace::PEER_SERVICE] = config[:peer_service] if config[:peer_service]
87
+ attributes.merge!(OpenTelemetry::Common::HTTP::ClientContext.attributes)
88
+
89
+ span = tracer.start_span("HTTP #{verb}", attributes: attributes, kind: :client, start_timestamp: start_time)
90
+
91
+ OpenTelemetry::Trace.with_span(span) do
92
+ OpenTelemetry.propagation.inject(request.headers)
93
+ end
94
+
95
+ span
96
+ rescue StandardError => e
97
+ OpenTelemetry.handle_error(exception: e)
98
+ end
99
+
100
+ def tracer
101
+ HTTPX::Instrumentation.instance.tracer
102
+ end
103
+ end
104
+
105
+ # Request patch to initiate the trace on initialization.
106
+ module RequestMethods
107
+ def initialize(*)
108
+ super
109
+
110
+ RequestTracer.call(self)
111
+ end
112
+ end
113
+
114
+ # Connection patch to start monitoring on initialization.
115
+ module ConnectionMethods
116
+ attr_reader :init_time
117
+
118
+ def initialize(*)
119
+ super
120
+
121
+ @init_time = ::Time.now
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright The OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ module OpenTelemetry
8
+ module Instrumentation
9
+ module HTTPX
10
+ module Stable
11
+ module Plugin
12
+ # Instruments around HTTPX's request/response lifecycle in order to generate
13
+ # an OTEL trace.
14
+ module RequestTracer
15
+ module_function
16
+
17
+ # initializes tracing on the +request+.
18
+ def call(request)
19
+ span = nil
20
+
21
+ # request objects are reused, when already buffered requests get rerouted to a different
22
+ # connection due to connection issues, or when they already got a response, but need to
23
+ # be retried. In such situations, the original span needs to be extended for the former,
24
+ # while a new is required for the latter.
25
+ request.on(:idle) do
26
+ span = nil
27
+ end
28
+ # the span is initialized when the request is buffered in the parser, which is the closest
29
+ # one gets to actually sending the request.
30
+ request.on(:headers) do
31
+ next if span
32
+
33
+ span = initialize_span(request)
34
+ end
35
+
36
+ request.on(:response) do |response|
37
+ unless span
38
+ next unless response.is_a?(::HTTPX::ErrorResponse) && response.error.respond_to?(:connection)
39
+
40
+ # handles the case when the +error+ happened during name resolution, which means
41
+ # that the tracing start point hasn't been triggered yet; in such cases, the approximate
42
+ # initial resolving time is collected from the connection, and used as span start time,
43
+ # and the tracing object in inserted before the on response callback is called.
44
+ span = initialize_span(request, response.error.connection.init_time)
45
+
46
+ end
47
+
48
+ finish(response, span)
49
+ end
50
+ end
51
+
52
+ def finish(response, span)
53
+ if response.is_a?(::HTTPX::ErrorResponse)
54
+ span.record_exception(response.error)
55
+ span.status = Trace::Status.error(response.error.to_s)
56
+ else
57
+ span.set_attribute('http.response.status_code', response.status)
58
+
59
+ if response.status.between?(400, 599)
60
+ err = ::HTTPX::HTTPError.new(response)
61
+ span.record_exception(err)
62
+ span.status = Trace::Status.error(err.to_s)
63
+ end
64
+ end
65
+
66
+ span.finish
67
+ end
68
+
69
+ # return a span initialized with the +@request+ state.
70
+ def initialize_span(request, start_time = ::Time.now)
71
+ verb = request.verb
72
+ uri = request.uri
73
+
74
+ config = HTTPX::Instrumentation.instance.config
75
+
76
+ attributes = {
77
+ 'http.request.method' => verb,
78
+ 'url.scheme' => uri.scheme,
79
+ 'url.path' => uri.path,
80
+ 'url.full' => "#{uri.scheme}://#{uri.host}",
81
+ 'server.address' => uri.host,
82
+ 'server.port' => uri.port
83
+ }
84
+ attributes['url.query'] = uri.query unless uri.query.nil?
85
+ attributes[OpenTelemetry::SemanticConventions::Trace::PEER_SERVICE] = config[:peer_service] if config[:peer_service]
86
+ attributes.merge!(OpenTelemetry::Common::HTTP::ClientContext.attributes)
87
+
88
+ span = tracer.start_span(verb, attributes: attributes, kind: :client, start_timestamp: start_time)
89
+
90
+ OpenTelemetry::Trace.with_span(span) do
91
+ OpenTelemetry.propagation.inject(request.headers)
92
+ end
93
+
94
+ span
95
+ rescue StandardError => e
96
+ OpenTelemetry.handle_error(exception: e)
97
+ end
98
+
99
+ def tracer
100
+ HTTPX::Instrumentation.instance.tracer
101
+ end
102
+ end
103
+
104
+ # Request patch to initiate the trace on initialization.
105
+ module RequestMethods
106
+ def initialize(*)
107
+ super
108
+
109
+ RequestTracer.call(self)
110
+ end
111
+ end
112
+
113
+ # Connection patch to start monitoring on initialization.
114
+ module ConnectionMethods
115
+ attr_reader :init_time
116
+
117
+ def initialize(*)
118
+ super
119
+
120
+ @init_time = ::Time.now
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -7,7 +7,7 @@
7
7
  module OpenTelemetry
8
8
  module Instrumentation
9
9
  module HTTPX
10
- VERSION = '0.2.1'
10
+ VERSION = '0.3.0'
11
11
  end
12
12
  end
13
13
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opentelemetry-instrumentation-httpx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OpenTelemetry Authors
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-04-29 00:00:00.000000000 Z
11
+ date: 2025-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: opentelemetry-api
@@ -52,17 +52,19 @@ files:
52
52
  - lib/opentelemetry-instrumentation-httpx.rb
53
53
  - lib/opentelemetry/instrumentation.rb
54
54
  - lib/opentelemetry/instrumentation/httpx.rb
55
+ - lib/opentelemetry/instrumentation/httpx/dup/plugin.rb
55
56
  - lib/opentelemetry/instrumentation/httpx/instrumentation.rb
56
- - lib/opentelemetry/instrumentation/httpx/plugin.rb
57
+ - lib/opentelemetry/instrumentation/httpx/old/plugin.rb
58
+ - lib/opentelemetry/instrumentation/httpx/stable/plugin.rb
57
59
  - lib/opentelemetry/instrumentation/httpx/version.rb
58
60
  homepage: https://github.com/open-telemetry/opentelemetry-ruby-contrib
59
61
  licenses:
60
62
  - Apache-2.0
61
63
  metadata:
62
- changelog_uri: https://rubydoc.info/gems/opentelemetry-instrumentation-httpx/0.2.1/file/CHANGELOG.md
64
+ changelog_uri: https://rubydoc.info/gems/opentelemetry-instrumentation-httpx/0.3.0/file/CHANGELOG.md
63
65
  source_code_uri: https://github.com/open-telemetry/opentelemetry-ruby-contrib/tree/main/instrumentation/http
64
66
  bug_tracker_uri: https://github.com/open-telemetry/opentelemetry-ruby-contrib/issues
65
- documentation_uri: https://rubydoc.info/gems/opentelemetry-instrumentation-httpx/0.2.1
67
+ documentation_uri: https://rubydoc.info/gems/opentelemetry-instrumentation-httpx/0.3.0
66
68
  post_install_message:
67
69
  rdoc_options: []
68
70
  require_paths:
@@ -1,126 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright The OpenTelemetry Authors
4
- #
5
- # SPDX-License-Identifier: Apache-2.0
6
-
7
- module OpenTelemetry
8
- module Instrumentation
9
- module HTTPX
10
- module Plugin
11
- # Instruments around HTTPX's request/response lifecycle in order to generate
12
- # an OTEL trace.
13
- module RequestTracer
14
- module_function
15
-
16
- # initializes tracing on the +request+.
17
- def call(request)
18
- span = nil
19
-
20
- # request objects are reused, when already buffered requests get rerouted to a different
21
- # connection due to connection issues, or when they already got a response, but need to
22
- # be retried. In such situations, the original span needs to be extended for the former,
23
- # while a new is required for the latter.
24
- request.on(:idle) do
25
- span = nil
26
- end
27
- # the span is initialized when the request is buffered in the parser, which is the closest
28
- # one gets to actually sending the request.
29
- request.on(:headers) do
30
- next if span
31
-
32
- span = initialize_span(request)
33
- end
34
-
35
- request.on(:response) do |response|
36
- unless span
37
- next unless response.is_a?(::HTTPX::ErrorResponse) && response.error.respond_to?(:connection)
38
-
39
- # handles the case when the +error+ happened during name resolution, which means
40
- # that the tracing start point hasn't been triggered yet; in such cases, the approximate
41
- # initial resolving time is collected from the connection, and used as span start time,
42
- # and the tracing object in inserted before the on response callback is called.
43
- span = initialize_span(request, response.error.connection.init_time)
44
-
45
- end
46
-
47
- finish(response, span)
48
- end
49
- end
50
-
51
- def finish(response, span)
52
- if response.is_a?(::HTTPX::ErrorResponse)
53
- span.record_exception(response.error)
54
- span.status = Trace::Status.error(response.error.to_s)
55
- else
56
- span.set_attribute(OpenTelemetry::SemanticConventions::Trace::HTTP_STATUS_CODE, response.status)
57
-
58
- if response.status.between?(400, 599)
59
- err = ::HTTPX::HTTPError.new(response)
60
- span.record_exception(err)
61
- span.status = Trace::Status.error(err.to_s)
62
- end
63
- end
64
-
65
- span.finish
66
- end
67
-
68
- # return a span initialized with the +@request+ state.
69
- def initialize_span(request, start_time = ::Time.now)
70
- verb = request.verb
71
- uri = request.uri
72
-
73
- config = HTTPX::Instrumentation.instance.config
74
-
75
- attributes = {
76
- OpenTelemetry::SemanticConventions::Trace::HTTP_HOST => uri.host,
77
- OpenTelemetry::SemanticConventions::Trace::HTTP_METHOD => verb,
78
- OpenTelemetry::SemanticConventions::Trace::HTTP_SCHEME => uri.scheme,
79
- OpenTelemetry::SemanticConventions::Trace::HTTP_TARGET => uri.path,
80
- OpenTelemetry::SemanticConventions::Trace::HTTP_URL => "#{uri.scheme}://#{uri.host}",
81
- OpenTelemetry::SemanticConventions::Trace::NET_PEER_NAME => uri.host,
82
- OpenTelemetry::SemanticConventions::Trace::NET_PEER_PORT => uri.port
83
- }
84
-
85
- attributes[OpenTelemetry::SemanticConventions::Trace::PEER_SERVICE] = config[:peer_service] if config[:peer_service]
86
- attributes.merge!(OpenTelemetry::Common::HTTP::ClientContext.attributes)
87
-
88
- span = tracer.start_span("HTTP #{verb}", attributes: attributes, kind: :client, start_timestamp: start_time)
89
-
90
- OpenTelemetry::Trace.with_span(span) do
91
- OpenTelemetry.propagation.inject(request.headers)
92
- end
93
-
94
- span
95
- rescue StandardError => e
96
- OpenTelemetry.handle_error(exception: e)
97
- end
98
-
99
- def tracer
100
- HTTPX::Instrumentation.instance.tracer
101
- end
102
- end
103
-
104
- # Request patch to initiate the trace on initialization.
105
- module RequestMethods
106
- def initialize(*)
107
- super
108
-
109
- RequestTracer.call(self)
110
- end
111
- end
112
-
113
- # Connection patch to start monitoring on initialization.
114
- module ConnectionMethods
115
- attr_reader :init_time
116
-
117
- def initialize(*)
118
- super
119
-
120
- @init_time = ::Time.now
121
- end
122
- end
123
- end
124
- end
125
- end
126
- end