opentelemetry-instrumentation-ethon 0.22.0 → 0.23.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 +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +17 -1
- data/lib/opentelemetry/instrumentation/ethon/instrumentation.rb +40 -6
- data/lib/opentelemetry/instrumentation/ethon/patches/dup/easy.rb +138 -0
- data/lib/opentelemetry/instrumentation/ethon/patches/old/easy.rb +130 -0
- data/lib/opentelemetry/instrumentation/ethon/patches/stable/easy.rb +133 -0
- data/lib/opentelemetry/instrumentation/ethon/version.rb +1 -1
- metadata +7 -131
- data/lib/opentelemetry/instrumentation/ethon/patches/easy.rb +0 -127
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8bbbe66773c8fce6953073cbd3abc9ed960cedc72dab5d23b58681f2970b6d1f
|
4
|
+
data.tar.gz: 1decc400420cc2a26de71704e789a91dbd1db8a264fb105f1f6cb62bb3f26be6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4977551e5520240c119eca62427f21e493b35f48e3033e370517679ad21050187a4352468944f590ee62cc25aaa7e5d55ee51eb239fd457ce5408dcdd0de7493
|
7
|
+
data.tar.gz: 6c35ebff6e824bbc848db16c7094906fe2b36d3756583d899164c809b91fe6951c1b74b1095169b96a347b245933e4b6bf8a9fae3507d29d7093111c45d64116
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Release History: opentelemetry-instrumentation-ethon
|
2
2
|
|
3
|
+
### v0.23.0 / 2025-07-29
|
4
|
+
|
5
|
+
* ADDED: Add Ethon `OTEL_SEMCONV_STABILITY_OPT_IN` environment variable [#1561](https://github.com/open-telemetry/opentelemetry-ruby-contrib/pull/1561)
|
6
|
+
|
3
7
|
### v0.22.0 / 2025-01-16
|
4
8
|
|
5
9
|
* BREAKING CHANGE: Set minimum supported version to Ruby 3.1
|
data/README.md
CHANGED
@@ -32,7 +32,7 @@ end
|
|
32
32
|
|
33
33
|
## Examples
|
34
34
|
|
35
|
-
Example usage of faraday can be seen in the `./example/ethon.rb` file
|
35
|
+
Example usage of faraday can be seen in the [`./example/ethon.rb` file](https://github.com/open-telemetry/opentelemetry-ruby-contrib/blob/main/instrumentation/ethon/example/ethon.rb)
|
36
36
|
|
37
37
|
## How can I get involved?
|
38
38
|
|
@@ -52,3 +52,19 @@ The `opentelemetry-instrumentation-all` gem is distributed under the Apache 2.0
|
|
52
52
|
[community-meetings]: https://github.com/open-telemetry/community#community-meetings
|
53
53
|
[slack-channel]: https://cloud-native.slack.com/archives/C01NWKKMKMY
|
54
54
|
[discussions-url]: https://github.com/open-telemetry/opentelemetry-ruby/discussions
|
55
|
+
|
56
|
+
## HTTP semantic convention stability
|
57
|
+
|
58
|
+
In the OpenTelemetry ecosystem, HTTP semantic conventions have now reached a stable state. However, the initial Ethon instrumentation was introduced before this stability was achieved, which resulted in HTTP attributes being based on an older version of the semantic conventions.
|
59
|
+
|
60
|
+
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.
|
61
|
+
|
62
|
+
When setting the value for `OTEL_SEMCONV_STABILITY_OPT_IN`, you can specify which conventions you wish to adopt:
|
63
|
+
|
64
|
+
- `http` - Emits the stable HTTP and networking conventions and ceases emitting the old conventions previously emitted by the instrumentation.
|
65
|
+
- `http/dup` - Emits both the old and stable HTTP and networking conventions, enabling a phased rollout of the stable semantic conventions.
|
66
|
+
- Default behavior (in the absence of either value) is to continue emitting the old HTTP and networking conventions the instrumentation previously emitted.
|
67
|
+
|
68
|
+
During the transition from old to stable conventions, Ethon instrumentation code comes in three patch versions: `dup`, `old`, and `stable`. These versions are identical except for the attributes they send. Any changes to Ethon instrumentation should consider all three patches.
|
69
|
+
|
70
|
+
For additional information on migration, please refer to our [documentation](https://opentelemetry.io/docs/specs/semconv/non-normative/http-migration/).
|
@@ -11,8 +11,9 @@ module OpenTelemetry
|
|
11
11
|
# instrumentation
|
12
12
|
class Instrumentation < OpenTelemetry::Instrumentation::Base
|
13
13
|
install do |_config|
|
14
|
-
|
15
|
-
|
14
|
+
patch_type = determine_semconv
|
15
|
+
send(:"require_dependencies_#{patch_type}")
|
16
|
+
send(:"add_patches_#{patch_type}")
|
16
17
|
end
|
17
18
|
|
18
19
|
present do
|
@@ -23,13 +24,46 @@ module OpenTelemetry
|
|
23
24
|
|
24
25
|
private
|
25
26
|
|
26
|
-
def
|
27
|
-
|
27
|
+
def determine_semconv
|
28
|
+
stability_opt_in = ENV.fetch('OTEL_SEMCONV_STABILITY_OPT_IN', '')
|
29
|
+
values = stability_opt_in.split(',').map(&:strip)
|
30
|
+
|
31
|
+
if values.include?('http/dup')
|
32
|
+
'dup'
|
33
|
+
elsif values.include?('http')
|
34
|
+
'stable'
|
35
|
+
else
|
36
|
+
'old'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def require_dependencies_dup
|
41
|
+
require_relative 'patches/dup/easy'
|
42
|
+
require_relative 'patches/multi'
|
43
|
+
end
|
44
|
+
|
45
|
+
def require_dependencies_stable
|
46
|
+
require_relative 'patches/stable/easy'
|
28
47
|
require_relative 'patches/multi'
|
29
48
|
end
|
30
49
|
|
31
|
-
def
|
32
|
-
|
50
|
+
def require_dependencies_old
|
51
|
+
require_relative 'patches/old/easy'
|
52
|
+
require_relative 'patches/multi'
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_patches_dup
|
56
|
+
::Ethon::Easy.prepend(Patches::Dup::Easy)
|
57
|
+
::Ethon::Multi.prepend(Patches::Multi)
|
58
|
+
end
|
59
|
+
|
60
|
+
def add_patches_stable
|
61
|
+
::Ethon::Easy.prepend(Patches::Stable::Easy)
|
62
|
+
::Ethon::Multi.prepend(Patches::Multi)
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_patches_old
|
66
|
+
::Ethon::Easy.prepend(Patches::Old::Easy)
|
33
67
|
::Ethon::Multi.prepend(Patches::Multi)
|
34
68
|
end
|
35
69
|
end
|
@@ -0,0 +1,138 @@
|
|
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 Ethon
|
10
|
+
module Patches
|
11
|
+
# Module using old and stable HTTP semantic conventions
|
12
|
+
module Dup
|
13
|
+
# Ethon::Easy patch for instrumentation
|
14
|
+
module Easy
|
15
|
+
ACTION_NAMES_TO_HTTP_METHODS = Hash.new do |h, k|
|
16
|
+
# #to_s is required because user input could be symbol or string
|
17
|
+
h[k] = k.to_s.upcase
|
18
|
+
end
|
19
|
+
HTTP_METHODS_TO_SPAN_NAMES = Hash.new do |h, k|
|
20
|
+
h[k] = k.to_s
|
21
|
+
h[k] = 'HTTP' if k == '_OTHER'
|
22
|
+
end
|
23
|
+
|
24
|
+
# Constant for the HTTP status range
|
25
|
+
HTTP_STATUS_SUCCESS_RANGE = (100..399)
|
26
|
+
|
27
|
+
def http_request(url, action_name, options = {})
|
28
|
+
@otel_method = ACTION_NAMES_TO_HTTP_METHODS[action_name]
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
def headers=(headers)
|
33
|
+
# Store headers to call this method again when span is ready
|
34
|
+
@otel_original_headers = headers
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
def perform
|
39
|
+
otel_before_request
|
40
|
+
super
|
41
|
+
end
|
42
|
+
|
43
|
+
def complete
|
44
|
+
begin
|
45
|
+
response_options = mirror.options
|
46
|
+
response_code = (response_options[:response_code] || response_options[:code]).to_i
|
47
|
+
if response_code.zero?
|
48
|
+
return_code = response_options[:return_code]
|
49
|
+
message = return_code ? ::Ethon::Curl.easy_strerror(return_code) : 'unknown reason'
|
50
|
+
@otel_span.status = OpenTelemetry::Trace::Status.error("Request has failed: #{message}")
|
51
|
+
else
|
52
|
+
@otel_span.set_attribute('http.status_code', response_code)
|
53
|
+
@otel_span.set_attribute('http.response.status_code', response_code)
|
54
|
+
@otel_span.status = OpenTelemetry::Trace::Status.error unless HTTP_STATUS_SUCCESS_RANGE.cover?(response_code.to_i)
|
55
|
+
end
|
56
|
+
ensure
|
57
|
+
@otel_span&.finish
|
58
|
+
@otel_span = nil
|
59
|
+
end
|
60
|
+
super
|
61
|
+
end
|
62
|
+
|
63
|
+
def reset
|
64
|
+
super
|
65
|
+
ensure
|
66
|
+
@otel_span = nil
|
67
|
+
@otel_method = nil
|
68
|
+
@otel_original_headers = nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def otel_before_request
|
72
|
+
method = '_OTHER' # Could be GET or not HTTP at all
|
73
|
+
method = @otel_method if instance_variable_defined?(:@otel_method) && !@otel_method.nil?
|
74
|
+
|
75
|
+
@otel_span = tracer.start_span(
|
76
|
+
HTTP_METHODS_TO_SPAN_NAMES[method],
|
77
|
+
attributes: span_creation_attributes(method),
|
78
|
+
kind: :client
|
79
|
+
)
|
80
|
+
|
81
|
+
@otel_original_headers ||= {}
|
82
|
+
OpenTelemetry::Trace.with_span(@otel_span) do
|
83
|
+
OpenTelemetry.propagation.inject(@otel_original_headers)
|
84
|
+
end
|
85
|
+
self.headers = @otel_original_headers
|
86
|
+
end
|
87
|
+
|
88
|
+
def otel_span_started?
|
89
|
+
instance_variable_defined?(:@otel_span) && !@otel_span.nil?
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def span_creation_attributes(method)
|
95
|
+
http_method = (method == '_OTHER' ? 'N/A' : method)
|
96
|
+
instrumentation_attrs = {
|
97
|
+
'http.method' => http_method,
|
98
|
+
'http.request.method' => method
|
99
|
+
}
|
100
|
+
|
101
|
+
uri = _otel_cleanse_uri(url)
|
102
|
+
if uri
|
103
|
+
instrumentation_attrs['http.url'] = uri.to_s
|
104
|
+
instrumentation_attrs['url.full'] = uri.to_s
|
105
|
+
instrumentation_attrs['net.peer.name'] = uri.host if uri.host
|
106
|
+
instrumentation_attrs['server.address'] = uri.host if uri.host
|
107
|
+
end
|
108
|
+
|
109
|
+
config = Ethon::Instrumentation.instance.config
|
110
|
+
instrumentation_attrs['peer.service'] = config[:peer_service] if config[:peer_service]
|
111
|
+
instrumentation_attrs.merge!(
|
112
|
+
OpenTelemetry::Common::HTTP::ClientContext.attributes
|
113
|
+
)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns a URL string with userinfo removed.
|
117
|
+
#
|
118
|
+
# @param [String] url The URL string to cleanse.
|
119
|
+
#
|
120
|
+
# @return [String] the cleansed URL.
|
121
|
+
def _otel_cleanse_uri(url)
|
122
|
+
cleansed_url = URI.parse(url)
|
123
|
+
cleansed_url.password = nil
|
124
|
+
cleansed_url.user = nil
|
125
|
+
cleansed_url
|
126
|
+
rescue URI::Error
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
|
130
|
+
def tracer
|
131
|
+
Ethon::Instrumentation.instance.tracer
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,130 @@
|
|
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 Ethon
|
10
|
+
module Patches
|
11
|
+
# Module using old HTTP semantic conventions
|
12
|
+
module Old
|
13
|
+
# Ethon::Easy patch for instrumentation
|
14
|
+
module Easy
|
15
|
+
ACTION_NAMES_TO_HTTP_METHODS = Hash.new do |h, k|
|
16
|
+
# #to_s is required because user input could be symbol or string
|
17
|
+
h[k] = k.to_s.upcase
|
18
|
+
end
|
19
|
+
HTTP_METHODS_TO_SPAN_NAMES = Hash.new { |h, k| h[k] = "HTTP #{k}" }
|
20
|
+
|
21
|
+
# Constant for the HTTP status range
|
22
|
+
HTTP_STATUS_SUCCESS_RANGE = (100..399)
|
23
|
+
|
24
|
+
def http_request(url, action_name, options = {})
|
25
|
+
@otel_method = ACTION_NAMES_TO_HTTP_METHODS[action_name]
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
29
|
+
def headers=(headers)
|
30
|
+
# Store headers to call this method again when span is ready
|
31
|
+
@otel_original_headers = headers
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
def perform
|
36
|
+
otel_before_request
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
def complete
|
41
|
+
begin
|
42
|
+
response_options = mirror.options
|
43
|
+
response_code = (response_options[:response_code] || response_options[:code]).to_i
|
44
|
+
if response_code.zero?
|
45
|
+
return_code = response_options[:return_code]
|
46
|
+
message = return_code ? ::Ethon::Curl.easy_strerror(return_code) : 'unknown reason'
|
47
|
+
@otel_span.status = OpenTelemetry::Trace::Status.error("Request has failed: #{message}")
|
48
|
+
else
|
49
|
+
@otel_span.set_attribute('http.status_code', response_code)
|
50
|
+
@otel_span.status = OpenTelemetry::Trace::Status.error unless HTTP_STATUS_SUCCESS_RANGE.cover?(response_code.to_i)
|
51
|
+
end
|
52
|
+
ensure
|
53
|
+
@otel_span&.finish
|
54
|
+
@otel_span = nil
|
55
|
+
end
|
56
|
+
super
|
57
|
+
end
|
58
|
+
|
59
|
+
def reset
|
60
|
+
super
|
61
|
+
ensure
|
62
|
+
@otel_span = nil
|
63
|
+
@otel_method = nil
|
64
|
+
@otel_original_headers = nil
|
65
|
+
end
|
66
|
+
|
67
|
+
def otel_before_request
|
68
|
+
method = 'N/A' # Could be GET or not HTTP at all
|
69
|
+
method = @otel_method if instance_variable_defined?(:@otel_method) && !@otel_method.nil?
|
70
|
+
|
71
|
+
@otel_span = tracer.start_span(
|
72
|
+
HTTP_METHODS_TO_SPAN_NAMES[method],
|
73
|
+
attributes: span_creation_attributes(method),
|
74
|
+
kind: :client
|
75
|
+
)
|
76
|
+
|
77
|
+
@otel_original_headers ||= {}
|
78
|
+
OpenTelemetry::Trace.with_span(@otel_span) do
|
79
|
+
OpenTelemetry.propagation.inject(@otel_original_headers)
|
80
|
+
end
|
81
|
+
self.headers = @otel_original_headers
|
82
|
+
end
|
83
|
+
|
84
|
+
def otel_span_started?
|
85
|
+
instance_variable_defined?(:@otel_span) && !@otel_span.nil?
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def span_creation_attributes(method)
|
91
|
+
instrumentation_attrs = {
|
92
|
+
'http.method' => method
|
93
|
+
}
|
94
|
+
|
95
|
+
uri = _otel_cleanse_uri(url)
|
96
|
+
if uri
|
97
|
+
instrumentation_attrs['http.url'] = uri.to_s
|
98
|
+
instrumentation_attrs['net.peer.name'] = uri.host if uri.host
|
99
|
+
end
|
100
|
+
|
101
|
+
config = Ethon::Instrumentation.instance.config
|
102
|
+
instrumentation_attrs['peer.service'] = config[:peer_service] if config[:peer_service]
|
103
|
+
instrumentation_attrs.merge!(
|
104
|
+
OpenTelemetry::Common::HTTP::ClientContext.attributes
|
105
|
+
)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Returns a URL string with userinfo removed.
|
109
|
+
#
|
110
|
+
# @param [String] url The URL string to cleanse.
|
111
|
+
#
|
112
|
+
# @return [String] the cleansed URL.
|
113
|
+
def _otel_cleanse_uri(url)
|
114
|
+
cleansed_url = URI.parse(url)
|
115
|
+
cleansed_url.password = nil
|
116
|
+
cleansed_url.user = nil
|
117
|
+
cleansed_url
|
118
|
+
rescue URI::Error
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
|
122
|
+
def tracer
|
123
|
+
Ethon::Instrumentation.instance.tracer
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,133 @@
|
|
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 Ethon
|
10
|
+
module Patches
|
11
|
+
# Module using stable HTTP semantic conventions
|
12
|
+
module Stable
|
13
|
+
# Ethon::Easy patch for instrumentation
|
14
|
+
module Easy
|
15
|
+
ACTION_NAMES_TO_HTTP_METHODS = Hash.new do |h, k|
|
16
|
+
# #to_s is required because user input could be symbol or string
|
17
|
+
h[k] = k.to_s.upcase
|
18
|
+
end
|
19
|
+
HTTP_METHODS_TO_SPAN_NAMES = Hash.new do |h, k|
|
20
|
+
h[k] = k.to_s
|
21
|
+
h[k] = 'HTTP' if k == '_OTHER'
|
22
|
+
end
|
23
|
+
|
24
|
+
# Constant for the HTTP status range
|
25
|
+
HTTP_STATUS_SUCCESS_RANGE = (100..399)
|
26
|
+
|
27
|
+
def http_request(url, action_name, options = {})
|
28
|
+
@otel_method = ACTION_NAMES_TO_HTTP_METHODS[action_name]
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
def headers=(headers)
|
33
|
+
# Store headers to call this method again when span is ready
|
34
|
+
@otel_original_headers = headers
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
def perform
|
39
|
+
otel_before_request
|
40
|
+
super
|
41
|
+
end
|
42
|
+
|
43
|
+
def complete
|
44
|
+
begin
|
45
|
+
response_options = mirror.options
|
46
|
+
response_code = (response_options[:response_code] || response_options[:code]).to_i
|
47
|
+
if response_code.zero?
|
48
|
+
return_code = response_options[:return_code]
|
49
|
+
message = return_code ? ::Ethon::Curl.easy_strerror(return_code) : 'unknown reason'
|
50
|
+
@otel_span.status = OpenTelemetry::Trace::Status.error("Request has failed: #{message}")
|
51
|
+
else
|
52
|
+
@otel_span.set_attribute('http.response.status_code', response_code)
|
53
|
+
@otel_span.status = OpenTelemetry::Trace::Status.error unless HTTP_STATUS_SUCCESS_RANGE.cover?(response_code.to_i)
|
54
|
+
end
|
55
|
+
ensure
|
56
|
+
@otel_span&.finish
|
57
|
+
@otel_span = nil
|
58
|
+
end
|
59
|
+
super
|
60
|
+
end
|
61
|
+
|
62
|
+
def reset
|
63
|
+
super
|
64
|
+
ensure
|
65
|
+
@otel_span = nil
|
66
|
+
@otel_method = nil
|
67
|
+
@otel_original_headers = nil
|
68
|
+
end
|
69
|
+
|
70
|
+
def otel_before_request
|
71
|
+
method = '_OTHER' # Could be GET or not HTTP at all
|
72
|
+
method = @otel_method if instance_variable_defined?(:@otel_method) && !@otel_method.nil?
|
73
|
+
|
74
|
+
@otel_span = tracer.start_span(
|
75
|
+
HTTP_METHODS_TO_SPAN_NAMES[method],
|
76
|
+
attributes: span_creation_attributes(method),
|
77
|
+
kind: :client
|
78
|
+
)
|
79
|
+
|
80
|
+
@otel_original_headers ||= {}
|
81
|
+
OpenTelemetry::Trace.with_span(@otel_span) do
|
82
|
+
OpenTelemetry.propagation.inject(@otel_original_headers)
|
83
|
+
end
|
84
|
+
self.headers = @otel_original_headers
|
85
|
+
end
|
86
|
+
|
87
|
+
def otel_span_started?
|
88
|
+
instance_variable_defined?(:@otel_span) && !@otel_span.nil?
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def span_creation_attributes(method)
|
94
|
+
instrumentation_attrs = {
|
95
|
+
'http.request.method' => method
|
96
|
+
}
|
97
|
+
|
98
|
+
uri = _otel_cleanse_uri(url)
|
99
|
+
if uri
|
100
|
+
instrumentation_attrs['url.full'] = uri.to_s
|
101
|
+
instrumentation_attrs['server.address'] = uri.host if uri.host
|
102
|
+
end
|
103
|
+
|
104
|
+
config = Ethon::Instrumentation.instance.config
|
105
|
+
instrumentation_attrs['peer.service'] = config[:peer_service] if config[:peer_service]
|
106
|
+
instrumentation_attrs.merge!(
|
107
|
+
OpenTelemetry::Common::HTTP::ClientContext.attributes
|
108
|
+
)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns a URL string with userinfo removed.
|
112
|
+
#
|
113
|
+
# @param [String] url The URL string to cleanse.
|
114
|
+
#
|
115
|
+
# @return [String] the cleansed URL.
|
116
|
+
def _otel_cleanse_uri(url)
|
117
|
+
cleansed_url = URI.parse(url)
|
118
|
+
cleansed_url.password = nil
|
119
|
+
cleansed_url.user = nil
|
120
|
+
cleansed_url
|
121
|
+
rescue URI::Error
|
122
|
+
nil
|
123
|
+
end
|
124
|
+
|
125
|
+
def tracer
|
126
|
+
Ethon::Instrumentation.instance.tracer
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: opentelemetry-instrumentation-ethon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.23.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-
|
11
|
+
date: 2025-08-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: opentelemetry-api
|
@@ -38,132 +38,6 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 0.23.0
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: appraisal
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '2.5'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '2.5'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: bundler
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '2.4'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '2.4'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: minitest
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - "~>"
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '5.0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - "~>"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '5.0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: opentelemetry-sdk
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - "~>"
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '1.1'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - "~>"
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '1.1'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: opentelemetry-test-helpers
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - "~>"
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '0.3'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - "~>"
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0.3'
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: rubocop
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - "~>"
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: 1.69.1
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - "~>"
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: 1.69.1
|
125
|
-
- !ruby/object:Gem::Dependency
|
126
|
-
name: rubocop-performance
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
128
|
-
requirements:
|
129
|
-
- - "~>"
|
130
|
-
- !ruby/object:Gem::Version
|
131
|
-
version: 1.23.0
|
132
|
-
type: :development
|
133
|
-
prerelease: false
|
134
|
-
version_requirements: !ruby/object:Gem::Requirement
|
135
|
-
requirements:
|
136
|
-
- - "~>"
|
137
|
-
- !ruby/object:Gem::Version
|
138
|
-
version: 1.23.0
|
139
|
-
- !ruby/object:Gem::Dependency
|
140
|
-
name: simplecov
|
141
|
-
requirement: !ruby/object:Gem::Requirement
|
142
|
-
requirements:
|
143
|
-
- - "~>"
|
144
|
-
- !ruby/object:Gem::Version
|
145
|
-
version: 0.17.1
|
146
|
-
type: :development
|
147
|
-
prerelease: false
|
148
|
-
version_requirements: !ruby/object:Gem::Requirement
|
149
|
-
requirements:
|
150
|
-
- - "~>"
|
151
|
-
- !ruby/object:Gem::Version
|
152
|
-
version: 0.17.1
|
153
|
-
- !ruby/object:Gem::Dependency
|
154
|
-
name: yard
|
155
|
-
requirement: !ruby/object:Gem::Requirement
|
156
|
-
requirements:
|
157
|
-
- - "~>"
|
158
|
-
- !ruby/object:Gem::Version
|
159
|
-
version: '0.9'
|
160
|
-
type: :development
|
161
|
-
prerelease: false
|
162
|
-
version_requirements: !ruby/object:Gem::Requirement
|
163
|
-
requirements:
|
164
|
-
- - "~>"
|
165
|
-
- !ruby/object:Gem::Version
|
166
|
-
version: '0.9'
|
167
41
|
description: Ethon instrumentation for the OpenTelemetry framework
|
168
42
|
email:
|
169
43
|
- cncf-opentelemetry-contributors@lists.cncf.io
|
@@ -179,17 +53,19 @@ files:
|
|
179
53
|
- lib/opentelemetry/instrumentation.rb
|
180
54
|
- lib/opentelemetry/instrumentation/ethon.rb
|
181
55
|
- lib/opentelemetry/instrumentation/ethon/instrumentation.rb
|
182
|
-
- lib/opentelemetry/instrumentation/ethon/patches/easy.rb
|
56
|
+
- lib/opentelemetry/instrumentation/ethon/patches/dup/easy.rb
|
183
57
|
- lib/opentelemetry/instrumentation/ethon/patches/multi.rb
|
58
|
+
- lib/opentelemetry/instrumentation/ethon/patches/old/easy.rb
|
59
|
+
- lib/opentelemetry/instrumentation/ethon/patches/stable/easy.rb
|
184
60
|
- lib/opentelemetry/instrumentation/ethon/version.rb
|
185
61
|
homepage: https://github.com/open-telemetry/opentelemetry-ruby-contrib
|
186
62
|
licenses:
|
187
63
|
- Apache-2.0
|
188
64
|
metadata:
|
189
|
-
changelog_uri: https://rubydoc.info/gems/opentelemetry-instrumentation-ethon/0.
|
65
|
+
changelog_uri: https://rubydoc.info/gems/opentelemetry-instrumentation-ethon/0.23.0/file/CHANGELOG.md
|
190
66
|
source_code_uri: https://github.com/open-telemetry/opentelemetry-ruby-contrib/tree/main/instrumentation/ethon
|
191
67
|
bug_tracker_uri: https://github.com/open-telemetry/opentelemetry-ruby-contrib/issues
|
192
|
-
documentation_uri: https://rubydoc.info/gems/opentelemetry-instrumentation-ethon/0.
|
68
|
+
documentation_uri: https://rubydoc.info/gems/opentelemetry-instrumentation-ethon/0.23.0
|
193
69
|
post_install_message:
|
194
70
|
rdoc_options: []
|
195
71
|
require_paths:
|
@@ -1,127 +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 Ethon
|
10
|
-
module Patches
|
11
|
-
# Ethon::Easy patch for instrumentation
|
12
|
-
module Easy
|
13
|
-
ACTION_NAMES_TO_HTTP_METHODS = Hash.new do |h, k|
|
14
|
-
# #to_s is required because user input could be symbol or string
|
15
|
-
h[k] = k.to_s.upcase
|
16
|
-
end
|
17
|
-
HTTP_METHODS_TO_SPAN_NAMES = Hash.new { |h, k| h[k] = "HTTP #{k}" }
|
18
|
-
|
19
|
-
# Constant for the HTTP status range
|
20
|
-
HTTP_STATUS_SUCCESS_RANGE = (100..399)
|
21
|
-
|
22
|
-
def http_request(url, action_name, options = {})
|
23
|
-
@otel_method = ACTION_NAMES_TO_HTTP_METHODS[action_name]
|
24
|
-
super
|
25
|
-
end
|
26
|
-
|
27
|
-
def headers=(headers)
|
28
|
-
# Store headers to call this method again when span is ready
|
29
|
-
@otel_original_headers = headers
|
30
|
-
super
|
31
|
-
end
|
32
|
-
|
33
|
-
def perform
|
34
|
-
otel_before_request
|
35
|
-
super
|
36
|
-
end
|
37
|
-
|
38
|
-
def complete
|
39
|
-
begin
|
40
|
-
response_options = mirror.options
|
41
|
-
response_code = (response_options[:response_code] || response_options[:code]).to_i
|
42
|
-
if response_code.zero?
|
43
|
-
return_code = response_options[:return_code]
|
44
|
-
message = return_code ? ::Ethon::Curl.easy_strerror(return_code) : 'unknown reason'
|
45
|
-
@otel_span.status = OpenTelemetry::Trace::Status.error("Request has failed: #{message}")
|
46
|
-
else
|
47
|
-
@otel_span.set_attribute('http.status_code', response_code)
|
48
|
-
@otel_span.status = OpenTelemetry::Trace::Status.error unless HTTP_STATUS_SUCCESS_RANGE.cover?(response_code.to_i)
|
49
|
-
end
|
50
|
-
ensure
|
51
|
-
@otel_span&.finish
|
52
|
-
@otel_span = nil
|
53
|
-
end
|
54
|
-
super
|
55
|
-
end
|
56
|
-
|
57
|
-
def reset
|
58
|
-
super
|
59
|
-
ensure
|
60
|
-
@otel_span = nil
|
61
|
-
@otel_method = nil
|
62
|
-
@otel_original_headers = nil
|
63
|
-
end
|
64
|
-
|
65
|
-
def otel_before_request
|
66
|
-
method = 'N/A' # Could be GET or not HTTP at all
|
67
|
-
method = @otel_method if instance_variable_defined?(:@otel_method) && !@otel_method.nil?
|
68
|
-
|
69
|
-
@otel_span = tracer.start_span(
|
70
|
-
HTTP_METHODS_TO_SPAN_NAMES[method],
|
71
|
-
attributes: span_creation_attributes(method),
|
72
|
-
kind: :client
|
73
|
-
)
|
74
|
-
|
75
|
-
@otel_original_headers ||= {}
|
76
|
-
OpenTelemetry::Trace.with_span(@otel_span) do
|
77
|
-
OpenTelemetry.propagation.inject(@otel_original_headers)
|
78
|
-
end
|
79
|
-
self.headers = @otel_original_headers
|
80
|
-
end
|
81
|
-
|
82
|
-
def otel_span_started?
|
83
|
-
instance_variable_defined?(:@otel_span) && !@otel_span.nil?
|
84
|
-
end
|
85
|
-
|
86
|
-
private
|
87
|
-
|
88
|
-
def span_creation_attributes(method)
|
89
|
-
instrumentation_attrs = {
|
90
|
-
'http.method' => method
|
91
|
-
}
|
92
|
-
|
93
|
-
uri = _otel_cleanse_uri(url)
|
94
|
-
if uri
|
95
|
-
instrumentation_attrs['http.url'] = uri.to_s
|
96
|
-
instrumentation_attrs['net.peer.name'] = uri.host if uri.host
|
97
|
-
end
|
98
|
-
|
99
|
-
config = Ethon::Instrumentation.instance.config
|
100
|
-
instrumentation_attrs['peer.service'] = config[:peer_service] if config[:peer_service]
|
101
|
-
instrumentation_attrs.merge!(
|
102
|
-
OpenTelemetry::Common::HTTP::ClientContext.attributes
|
103
|
-
)
|
104
|
-
end
|
105
|
-
|
106
|
-
# Returns a URL string with userinfo removed.
|
107
|
-
#
|
108
|
-
# @param [String] url The URL string to cleanse.
|
109
|
-
#
|
110
|
-
# @return [String] the cleansed URL.
|
111
|
-
def _otel_cleanse_uri(url)
|
112
|
-
cleansed_url = URI.parse(url)
|
113
|
-
cleansed_url.password = nil
|
114
|
-
cleansed_url.user = nil
|
115
|
-
cleansed_url
|
116
|
-
rescue URI::Error
|
117
|
-
nil
|
118
|
-
end
|
119
|
-
|
120
|
-
def tracer
|
121
|
-
Ethon::Instrumentation.instance.tracer
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|