Skip to content

Commit 7837db8

Browse files
feat: http semconv opt in files (open-telemetry#1547)
* feat: WIP semcon file re-structure * feat: semconv testing * Clean up appraisal file * Appease Rubocop * Add README and consider database migration env vars * safe nav stability_opt_in * Add tests for environment variable * Appease rubocop * Cleanup * Test refactor * update determine_semconv test --------- Co-authored-by: Kayla Reopelle <[email protected]>
1 parent a83b342 commit 7837db8

File tree

18 files changed

+1009
-122
lines changed

18 files changed

+1009
-122
lines changed

instrumentation/http/Appraisals

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,19 @@
44
#
55
# SPDX-License-Identifier: Apache-2.0
66

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

11-
appraise 'http-3.3.0' do
12-
gem 'http', '~> 3.3.0'
19+
appraise "http-3.3.0-#{mode}" do
20+
gem 'http', '~> 3.3.0'
21+
end
1322
end

instrumentation/http/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,19 @@ The `opentelemetry-instrumentation-http` gem is distributed under the Apache 2.0
6464
[community-meetings]: https://github.com/open-telemetry/community#community-meetings
6565
[slack-channel]: https://cloud-native.slack.com/archives/C01NWKKMKMY
6666
[discussions-url]: https://github.com/open-telemetry/opentelemetry-ruby/discussions
67+
68+
## HTTP semantic convention stability
69+
70+
In the OpenTelemetry ecosystem, HTTP semantic conventions have now reached a stable state. However, the initial HTTP instrumentation was introduced before this stability was achieved, which resulted in HTTP attributes being based on an older version of the semantic conventions.
71+
72+
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.
73+
74+
When setting the value for `OTEL_SEMCONV_STABILITY_OPT_IN`, you can specify which conventions you wish to adopt:
75+
76+
- `http` - Emits the stable HTTP and networking conventions and ceases emitting the old conventions previously emitted by the instrumentation.
77+
- `http/dup` - Emits both the old and stable HTTP and networking conventions, enabling a phased rollout of the stable semantic conventions.
78+
- Default behavior (in the absence of either value) is to continue emitting the old HTTP and networking conventions the instrumentation previously emitted.
79+
80+
During the transition from old to stable conventions, HTTP instrumentation code comes in three patch versions: `dup`, `old`, and `stable`. These versions are identical except for the attributes they send. Any changes to HTTP instrumentation should consider all three patches.
81+
82+
For additional information on migration, please refer to our [documentation](https://opentelemetry.io/docs/specs/semconv/non-normative/http-migration/).

instrumentation/http/lib/opentelemetry/instrumentation/http/instrumentation.rb

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ module HTTP
1010
# The Instrumentation class contains logic to detect and install the Http instrumentation
1111
class Instrumentation < OpenTelemetry::Instrumentation::Base
1212
install do |_config|
13-
require_dependencies
14-
patch
13+
patch_type = determine_semconv
14+
send(:"require_dependencies_#{patch_type}")
15+
send(:"patch_#{patch_type}")
1516
end
1617

1718
present do
@@ -20,14 +21,47 @@ class Instrumentation < OpenTelemetry::Instrumentation::Base
2021

2122
option :span_name_formatter, default: nil, validate: :callable
2223

23-
def patch
24-
::HTTP::Client.prepend(Patches::Client)
25-
::HTTP::Connection.prepend(Patches::Connection)
24+
def determine_semconv
25+
stability_opt_in = ENV.fetch('OTEL_SEMCONV_STABILITY_OPT_IN', '')
26+
values = stability_opt_in.split(',').map(&:strip)
27+
28+
if values.include?('http/dup')
29+
'dup'
30+
elsif values.include?('http')
31+
'stable'
32+
else
33+
'old'
34+
end
35+
end
36+
37+
def patch_old
38+
::HTTP::Client.prepend(Patches::Old::Client)
39+
::HTTP::Connection.prepend(Patches::Old::Connection)
40+
end
41+
42+
def patch_dup
43+
::HTTP::Client.prepend(Patches::Dup::Client)
44+
::HTTP::Connection.prepend(Patches::Dup::Connection)
45+
end
46+
47+
def patch_stable
48+
::HTTP::Client.prepend(Patches::Stable::Client)
49+
::HTTP::Connection.prepend(Patches::Stable::Connection)
50+
end
51+
52+
def require_dependencies_dup
53+
require_relative 'patches/dup/client'
54+
require_relative 'patches/dup/connection'
55+
end
56+
57+
def require_dependencies_old
58+
require_relative 'patches/old/client'
59+
require_relative 'patches/old/connection'
2660
end
2761

28-
def require_dependencies
29-
require_relative 'patches/client'
30-
require_relative 'patches/connection'
62+
def require_dependencies_stable
63+
require_relative 'patches/stable/client'
64+
require_relative 'patches/stable/connection'
3165
end
3266
end
3367
end

instrumentation/http/lib/opentelemetry/instrumentation/http/patches/client.rb

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

instrumentation/http/lib/opentelemetry/instrumentation/http/patches/connection.rb

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
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 HTTP
10+
module Patches
11+
# Module using old and stable HTTP semantic conventions
12+
module Dup
13+
# Module to prepend to HTTP::Client for instrumentation
14+
module Client
15+
# Constant for the HTTP status range
16+
HTTP_STATUS_SUCCESS_RANGE = (100..399)
17+
18+
def perform(req, options)
19+
uri = req.uri
20+
request_method = req.verb.to_s.upcase
21+
span_name = create_request_span_name(request_method, uri.path)
22+
23+
attributes = {
24+
# old semconv
25+
'http.method' => request_method,
26+
'http.scheme' => uri.scheme,
27+
'http.target' => uri.path,
28+
'http.url' => "#{uri.scheme}://#{uri.host}",
29+
'net.peer.name' => uri.host,
30+
'net.peer.port' => uri.port,
31+
# stable semconv
32+
'http.request.method' => request_method,
33+
'url.scheme' => uri.scheme,
34+
'url.path' => uri.path,
35+
'url.full' => "#{uri.scheme}://#{uri.host}",
36+
'server.address' => uri.host,
37+
'server.port' => uri.port
38+
}
39+
attributes['url.query'] = uri.query unless uri.query.nil?
40+
attributes.merge!(OpenTelemetry::Common::HTTP::ClientContext.attributes)
41+
42+
tracer.in_span(span_name, attributes: attributes, kind: :client) do |span|
43+
OpenTelemetry.propagation.inject(req.headers)
44+
super.tap do |response|
45+
annotate_span_with_response!(span, response)
46+
end
47+
end
48+
end
49+
50+
private
51+
52+
def config
53+
OpenTelemetry::Instrumentation::HTTP::Instrumentation.instance.config
54+
end
55+
56+
def annotate_span_with_response!(span, response)
57+
return unless response&.status
58+
59+
status_code = response.status.to_i
60+
span.set_attribute('http.status_code', status_code) # old semconv
61+
span.set_attribute('http.response.status_code', status_code) # stable semconv
62+
span.status = OpenTelemetry::Trace::Status.error unless HTTP_STATUS_SUCCESS_RANGE.cover?(status_code)
63+
end
64+
65+
def create_request_span_name(request_method, request_path)
66+
if (implementation = config[:span_name_formatter])
67+
updated_span_name = implementation.call(request_method, request_path)
68+
updated_span_name.is_a?(String) ? updated_span_name : "HTTP #{request_method}"
69+
else
70+
"HTTP #{request_method}"
71+
end
72+
rescue StandardError
73+
"HTTP #{request_method}"
74+
end
75+
76+
def tracer
77+
HTTP::Instrumentation.instance.tracer
78+
end
79+
end
80+
end
81+
end
82+
end
83+
end
84+
end
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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 HTTP
10+
module Patches
11+
# Module using old and stable HTTP semantic conventions
12+
module Dup
13+
# Module to prepend to HTTP::Connection for instrumentation
14+
module Connection
15+
def initialize(req, options)
16+
attributes = {
17+
# old semconv
18+
'net.peer.name' => req.uri.host,
19+
'net.peer.port' => req.uri.port,
20+
# stable semconv
21+
'server.address' => req.uri.host,
22+
'server.port' => req.uri.port
23+
}.merge!(OpenTelemetry::Common::HTTP::ClientContext.attributes)
24+
25+
tracer.in_span('HTTP CONNECT', attributes: attributes) do
26+
super
27+
end
28+
end
29+
30+
private
31+
32+
def tracer
33+
HTTP::Instrumentation.instance.tracer
34+
end
35+
end
36+
end
37+
end
38+
end
39+
end
40+
end

0 commit comments

Comments
 (0)