Skip to content

Commit 98446e7

Browse files
feat: rest-client HTTP semantic convention stability migration (#1568)
* feat: rest-client HTTP semantic convention stability migration * fix: rubocop whitespace fix * feat: update span name * Update instrumentation/restclient/README.md Co-authored-by: Kayla Reopelle <[email protected]> --------- Co-authored-by: Kayla Reopelle <[email protected]>
1 parent 5a43d86 commit 98446e7

File tree

10 files changed

+542
-86
lines changed

10 files changed

+542
-86
lines changed

instrumentation/restclient/Appraisals

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
# frozen_string_literal: true
22

3-
appraise 'rest-client-2.1' do
4-
gem 'rest-client', '~> 2.1.0'
5-
end
3+
# To faclitate HTTP semantic convention stability migration, we are using
4+
# appraisal to test the different semantic convention modes along with different
5+
# gem versions. For more information on the semantic convention modes, see:
6+
# https://opentelemetry.io/docs/specs/semconv/non-normative/http-migration/
7+
8+
semconv_stability = %w[dup stable old]
9+
10+
semconv_stability.each do |mode|
11+
appraise "rest-client-2.1_#{mode}" do
12+
gem 'rest-client', '~> 2.1.0'
13+
end
614

7-
appraise 'rest-client-2.0' do
8-
gem 'rest-client', '~> 2.0.0'
15+
appraise "rest-client-2.0_#{mode}" do
16+
gem 'rest-client', '~> 2.0.0'
17+
end
918
end

instrumentation/restclient/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,19 @@ Apache 2.0 license. See [LICENSE][license-github] for more information.
4848
[community-meetings]: https://github.com/open-telemetry/community#community-meetings
4949
[slack-channel]: https://cloud-native.slack.com/archives/C01NWKKMKMY
5050
[discussions-url]: https://github.com/open-telemetry/opentelemetry-ruby/discussions
51+
52+
## HTTP semantic convention stability
53+
54+
In the OpenTelemetry ecosystem, HTTP semantic conventions have now reached a stable state. However, the initial Faraday 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, RestClient instrumentation code comes in three patch versions: `dup`, `old`, and `stable`. These versions are identical except for the attributes they send. Any changes to RestClient 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/).

instrumentation/restclient/lib/opentelemetry/instrumentation/restclient/instrumentation.rb

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ module RestClient
1111
# instrumentation
1212
class Instrumentation < OpenTelemetry::Instrumentation::Base
1313
install do |_config|
14-
require_dependencies
15-
patch_request
14+
patch_type = determine_semconv
15+
send(:"require_dependencies_#{patch_type}")
16+
send(:"patch_request_#{patch_type}")
1617
end
1718

1819
present do
@@ -23,12 +24,41 @@ class Instrumentation < OpenTelemetry::Instrumentation::Base
2324

2425
private
2526

26-
def require_dependencies
27-
require_relative 'patches/request'
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/request'
42+
end
43+
44+
def require_dependencies_stable
45+
require_relative 'patches/stable/request'
46+
end
47+
48+
def require_dependencies_old
49+
require_relative 'patches/old/request'
50+
end
51+
52+
def patch_request_dup
53+
::RestClient::Request.prepend(Patches::Dup::Request)
54+
end
55+
56+
def patch_request_stable
57+
::RestClient::Request.prepend(Patches::Stable::Request)
2858
end
2959

30-
def patch_request
31-
::RestClient::Request.prepend(Patches::Request)
60+
def patch_request_old
61+
::RestClient::Request.prepend(Patches::Old::Request)
3262
end
3363
end
3464
end
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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 RestClient
10+
module Patches
11+
module Dup
12+
# Module to prepend to RestClient::Request for instrumentation
13+
module Request
14+
# Constant for the HTTP status range
15+
HTTP_STATUS_SUCCESS_RANGE = (100..399)
16+
17+
def execute(&)
18+
trace_request do |_span|
19+
super
20+
end
21+
end
22+
23+
private
24+
25+
def create_request_span
26+
http_method = method.upcase
27+
instrumentation_attrs = {
28+
'http.method' => http_method.to_s,
29+
'http.request.method' => http_method.to_s,
30+
'http.url' => OpenTelemetry::Common::Utilities.cleanse_url(url),
31+
'url.full' => OpenTelemetry::Common::Utilities.cleanse_url(url)
32+
}
33+
instrumentation_config = RestClient::Instrumentation.instance.config
34+
instrumentation_attrs['peer.service'] = instrumentation_config[:peer_service] if instrumentation_config[:peer_service]
35+
span = tracer.start_span(
36+
http_method.to_s,
37+
attributes: instrumentation_attrs.merge(
38+
OpenTelemetry::Common::HTTP::ClientContext.attributes
39+
),
40+
kind: :client
41+
)
42+
43+
OpenTelemetry::Trace.with_span(span) do
44+
OpenTelemetry.propagation.inject(processed_headers)
45+
end
46+
47+
span
48+
end
49+
50+
def trace_request
51+
span = create_request_span
52+
53+
yield(span).tap do |response|
54+
# Verify return value is a response.
55+
# If so, add additional attributes.
56+
if response.is_a?(::RestClient::Response)
57+
span.set_attribute('http.status_code', response.code)
58+
span.set_attribute('http.response.status_code', response.code)
59+
span.status = OpenTelemetry::Trace::Status.error unless HTTP_STATUS_SUCCESS_RANGE.cover?(response.code.to_i)
60+
end
61+
end
62+
rescue ::RestClient::ExceptionWithResponse => e
63+
span.set_attribute('http.status_code', e.http_code)
64+
span.set_attribute('http.response.status_code', e.http_code)
65+
span.status = OpenTelemetry::Trace::Status.error unless HTTP_STATUS_SUCCESS_RANGE.cover?(e.http_code.to_i)
66+
raise e
67+
ensure
68+
span.finish
69+
end
70+
71+
def tracer
72+
RestClient::Instrumentation.instance.tracer
73+
end
74+
end
75+
end
76+
end
77+
end
78+
end
79+
end
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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 RestClient
10+
module Patches
11+
module Old
12+
# Module to prepend to RestClient::Request for instrumentation
13+
module Request
14+
# Constant for the HTTP status range
15+
HTTP_STATUS_SUCCESS_RANGE = (100..399)
16+
17+
def execute(&)
18+
trace_request do |_span|
19+
super
20+
end
21+
end
22+
23+
private
24+
25+
def create_request_span
26+
http_method = method.upcase
27+
instrumentation_attrs = {
28+
'http.method' => http_method.to_s,
29+
'http.url' => OpenTelemetry::Common::Utilities.cleanse_url(url)
30+
}
31+
instrumentation_config = RestClient::Instrumentation.instance.config
32+
instrumentation_attrs['peer.service'] = instrumentation_config[:peer_service] if instrumentation_config[:peer_service]
33+
span = tracer.start_span(
34+
"HTTP #{http_method}",
35+
attributes: instrumentation_attrs.merge(
36+
OpenTelemetry::Common::HTTP::ClientContext.attributes
37+
),
38+
kind: :client
39+
)
40+
41+
OpenTelemetry::Trace.with_span(span) do
42+
OpenTelemetry.propagation.inject(processed_headers)
43+
end
44+
45+
span
46+
end
47+
48+
def trace_request
49+
span = create_request_span
50+
51+
yield(span).tap do |response|
52+
# Verify return value is a response.
53+
# If so, add additional attributes.
54+
if response.is_a?(::RestClient::Response)
55+
span.set_attribute('http.status_code', response.code)
56+
span.status = OpenTelemetry::Trace::Status.error unless HTTP_STATUS_SUCCESS_RANGE.cover?(response.code.to_i)
57+
end
58+
end
59+
rescue ::RestClient::ExceptionWithResponse => e
60+
span.set_attribute('http.status_code', e.http_code)
61+
span.status = OpenTelemetry::Trace::Status.error unless HTTP_STATUS_SUCCESS_RANGE.cover?(e.http_code.to_i)
62+
raise e
63+
ensure
64+
span.finish
65+
end
66+
67+
def tracer
68+
RestClient::Instrumentation.instance.tracer
69+
end
70+
end
71+
end
72+
end
73+
end
74+
end
75+
end

instrumentation/restclient/lib/opentelemetry/instrumentation/restclient/patches/request.rb

Lines changed: 0 additions & 73 deletions
This file was deleted.

0 commit comments

Comments
 (0)