Skip to content

Commit 49c6dc0

Browse files
committed
feat: HTTP Semconv naming
1 parent f59066e commit 49c6dc0

File tree

39 files changed

+630
-403
lines changed

39 files changed

+630
-403
lines changed

Rakefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ task default: [:each]
3636

3737
def foreach_gem(cmd)
3838
Dir.glob("**/opentelemetry-*.gemspec") do |gemspec|
39-
name = File.basename(gemspec, ".gemspec")
39+
next if gemspec.start_with?('.')
40+
4041
dir = File.dirname(gemspec)
4142
puts "**** Entering #{dir}"
4243
Dir.chdir(dir) do

instrumentation/all/Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ group :test do
3333
Dir.entries('../')
3434
.select { |entry| File.directory?(File.join('../', entry)) }
3535
.reject { |entry| excluded_instrumentations.include?(entry) }
36+
.reject { |entry| entry.start_with?('.') }
3637
.sort
3738
.each { |dir| gem "opentelemetry-instrumentation-#{dir}", path: "../#{dir}" }
3839
end

instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/http_helper.rb

Lines changed: 71 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ module Ethon
1111
# @api private
1212
module HttpHelper
1313
# Lightweight struct to hold span creation attributes
14-
SpanCreationAttributes = Struct.new(:span_name, :normalized_method, :original_method, keyword_init: true)
14+
SpanCreationAttributes = Struct.new(:span_name, :attributes, keyword_init: true)
1515

1616
# Pre-computed mapping to avoid string allocations during normalization
1717
METHOD_CACHE = {
@@ -44,41 +44,83 @@ module HttpHelper
4444
:trace => 'TRACE'
4545
}.freeze
4646

47-
# Pre-computed span names for old semantic conventions to avoid allocations
48-
OLD_SPAN_NAMES = {
49-
'CONNECT' => 'HTTP CONNECT',
50-
'DELETE' => 'HTTP DELETE',
51-
'GET' => 'HTTP GET',
52-
'HEAD' => 'HTTP HEAD',
53-
'OPTIONS' => 'HTTP OPTIONS',
54-
'PATCH' => 'HTTP PATCH',
55-
'POST' => 'HTTP POST',
56-
'PUT' => 'HTTP PUT',
57-
'TRACE' => 'HTTP TRACE'
58-
}.freeze
47+
private_constant :METHOD_CACHE
48+
49+
# Prepares span data using old semantic conventions
50+
# @param method [String, Symbol] The HTTP method
51+
# @return [SpanCreationAttributes] struct containing span_name and attributes hash
52+
def self.span_attrs_for_old(method)
53+
client_context_attrs = OpenTelemetry::Common::HTTP::ClientContext.attributes
54+
normalized = METHOD_CACHE[method]
55+
attributes = client_context_attrs.dup
56+
57+
# Determine base span name and method value
58+
if normalized
59+
span_name = normalized
60+
method_value = normalized
61+
else
62+
span_name = 'HTTP'
63+
method_value = '_OTHER'
64+
end
65+
66+
attributes['http.method'] ||= method_value
5967

60-
private_constant :METHOD_CACHE, :OLD_SPAN_NAMES
68+
SpanCreationAttributes.new(span_name: span_name, attributes: attributes)
69+
end
6170

62-
# Prepares all span data for the specified semantic convention in a single call
71+
# Prepares span data using stable semantic conventions
6372
# @param method [String, Symbol] The HTTP method
64-
# @param semconv [Symbol] The semantic convention to use (:stable or :old)
65-
# @return [SpanCreationAttributes] struct containing span_name, normalized_method, and original_method
66-
def self.span_attrs_for(method, semconv: :stable)
73+
# @return [SpanCreationAttributes] struct containing span_name and attributes hash
74+
def self.span_attrs_for_stable(method)
75+
client_context_attrs = OpenTelemetry::Common::HTTP::ClientContext.attributes
76+
url_template = client_context_attrs['url.template']
6777
normalized = METHOD_CACHE[method]
78+
attributes = client_context_attrs.dup
79+
80+
# Determine base span name and method value
6881
if normalized
69-
span_name = semconv == :old ? OLD_SPAN_NAMES[normalized] : normalized
70-
SpanCreationAttributes.new(
71-
span_name: span_name,
72-
normalized_method: normalized,
73-
original_method: nil
74-
)
82+
base_name = normalized
83+
method_value = normalized
84+
original = nil
7585
else
76-
SpanCreationAttributes.new(
77-
span_name: 'HTTP',
78-
normalized_method: '_OTHER',
79-
original_method: method.to_s
80-
)
86+
base_name = 'HTTP'
87+
method_value = '_OTHER'
88+
original = method.to_s
8189
end
90+
91+
span_name = url_template ? "#{base_name} #{url_template}" : base_name
92+
attributes['http.request.method'] ||= method_value
93+
attributes['http.request.method_original'] ||= original if original
94+
95+
SpanCreationAttributes.new(span_name: span_name, attributes: attributes)
96+
end
97+
98+
# Prepares span data using both old and stable semantic conventions
99+
# @param method [String, Symbol] The HTTP method
100+
# @return [SpanCreationAttributes] struct containing span_name and attributes hash
101+
def self.span_attrs_for_dup(method)
102+
client_context_attrs = OpenTelemetry::Common::HTTP::ClientContext.attributes
103+
url_template = client_context_attrs['url.template']
104+
normalized = METHOD_CACHE[method]
105+
attributes = client_context_attrs.dup
106+
107+
# Determine base span name and method value
108+
if normalized
109+
base_name = normalized
110+
method_value = normalized
111+
original = nil
112+
else
113+
base_name = 'HTTP'
114+
method_value = '_OTHER'
115+
original = method.to_s
116+
end
117+
118+
span_name = url_template ? "#{base_name} #{url_template}" : base_name
119+
attributes['http.method'] ||= method_value
120+
attributes['http.request.method'] ||= method_value
121+
attributes['http.request.method_original'] ||= original if original
122+
123+
SpanCreationAttributes.new(span_name: span_name, attributes: attributes)
82124
end
83125
end
84126
end

instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/dup/easy.rb

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def reset
6868
end
6969

7070
def otel_before_request
71-
span_data = HttpHelper.span_attrs_for(@otel_method)
71+
span_data = HttpHelper.span_attrs_for_dup(@otel_method)
7272

7373
@otel_span = tracer.start_span(
7474
span_data.span_name,
@@ -90,11 +90,7 @@ def otel_span_started?
9090
private
9191

9292
def span_creation_attributes(span_data)
93-
instrumentation_attrs = {
94-
'http.method' => span_data.normalized_method,
95-
'http.request.method' => span_data.normalized_method
96-
}
97-
instrumentation_attrs['http.request.method_original'] = span_data.original_method if span_data.original_method
93+
instrumentation_attrs = {}
9894

9995
uri = _otel_cleanse_uri(url)
10096
if uri
@@ -106,9 +102,7 @@ def span_creation_attributes(span_data)
106102

107103
config = Ethon::Instrumentation.instance.config
108104
instrumentation_attrs['peer.service'] = config[:peer_service] if config[:peer_service]
109-
instrumentation_attrs.merge!(
110-
OpenTelemetry::Common::HTTP::ClientContext.attributes
111-
)
105+
instrumentation_attrs.merge!(span_data.attributes)
112106
end
113107

114108
# Returns a URL string with userinfo removed.

instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/old/easy.rb

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def reset
6767
end
6868

6969
def otel_before_request
70-
span_data = HttpHelper.span_attrs_for(@otel_method, semconv: :old)
70+
span_data = HttpHelper.span_attrs_for_old(@otel_method)
7171

7272
@otel_span = tracer.start_span(
7373
span_data.span_name,
@@ -89,9 +89,7 @@ def otel_span_started?
8989
private
9090

9191
def span_creation_attributes(span_data)
92-
instrumentation_attrs = {
93-
'http.method' => span_data.normalized_method
94-
}
92+
instrumentation_attrs = {}
9593

9694
uri = _otel_cleanse_uri(url)
9795
if uri
@@ -101,9 +99,7 @@ def span_creation_attributes(span_data)
10199

102100
config = Ethon::Instrumentation.instance.config
103101
instrumentation_attrs['peer.service'] = config[:peer_service] if config[:peer_service]
104-
instrumentation_attrs.merge!(
105-
OpenTelemetry::Common::HTTP::ClientContext.attributes
106-
)
102+
instrumentation_attrs.merge!(span_data.attributes)
107103
end
108104

109105
# Returns a URL string with userinfo removed.

instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/stable/easy.rb

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def reset
6767
end
6868

6969
def otel_before_request
70-
span_data = HttpHelper.span_attrs_for(@otel_method)
70+
span_data = HttpHelper.span_attrs_for_stable(@otel_method)
7171

7272
@otel_span = tracer.start_span(
7373
span_data.span_name,
@@ -89,10 +89,7 @@ def otel_span_started?
8989
private
9090

9191
def span_creation_attributes(span_data)
92-
instrumentation_attrs = {
93-
'http.request.method' => span_data.normalized_method
94-
}
95-
instrumentation_attrs['http.request.method_original'] = span_data.original_method if span_data.original_method
92+
instrumentation_attrs = {}
9693

9794
uri = _otel_cleanse_uri(url)
9895
if uri
@@ -102,9 +99,7 @@ def span_creation_attributes(span_data)
10299

103100
config = Ethon::Instrumentation.instance.config
104101
instrumentation_attrs['peer.service'] = config[:peer_service] if config[:peer_service]
105-
instrumentation_attrs.merge!(
106-
OpenTelemetry::Common::HTTP::ClientContext.attributes
107-
)
102+
instrumentation_attrs.merge!(span_data.attributes)
108103
end
109104

110105
# Returns a URL string with userinfo removed.

instrumentation/excon/lib/opentelemetry/instrumentation/excon/http_helper.rb

Lines changed: 72 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ module Excon
1010
# Utility module for HTTP-related helper methods
1111
# @api private
1212
module HttpHelper
13-
# Lightweight struct to hold span creation attributes
14-
SpanCreationAttributes = Struct.new(:span_name, :normalized_method, :original_method, keyword_init: true)
13+
# Lightweight struct to hold span creation data
14+
SpanCreationAttributes = Struct.new(:span_name, :attributes, keyword_init: true)
1515

1616
# Pre-computed mapping to avoid string allocations during normalization
1717
METHOD_CACHE = {
@@ -44,41 +44,83 @@ module HttpHelper
4444
:trace => 'TRACE'
4545
}.freeze
4646

47-
# Pre-computed span names for old semantic conventions to avoid allocations
48-
OLD_SPAN_NAMES = {
49-
'CONNECT' => 'HTTP CONNECT',
50-
'DELETE' => 'HTTP DELETE',
51-
'GET' => 'HTTP GET',
52-
'HEAD' => 'HTTP HEAD',
53-
'OPTIONS' => 'HTTP OPTIONS',
54-
'PATCH' => 'HTTP PATCH',
55-
'POST' => 'HTTP POST',
56-
'PUT' => 'HTTP PUT',
57-
'TRACE' => 'HTTP TRACE'
58-
}.freeze
47+
private_constant :METHOD_CACHE
48+
49+
# Prepares span data using old semantic conventions
50+
# @param method [String, Symbol] The HTTP method
51+
# @return [SpanCreationAttributes] struct containing span_name and attributes hash
52+
def self.span_attrs_for_old(method)
53+
client_context_attrs = OpenTelemetry::Common::HTTP::ClientContext.attributes
54+
normalized = METHOD_CACHE[method]
55+
attributes = client_context_attrs.dup
56+
57+
# Determine base span name and method value
58+
if normalized
59+
span_name = normalized
60+
method_value = normalized
61+
else
62+
span_name = 'HTTP'
63+
method_value = '_OTHER'
64+
end
65+
66+
attributes['http.method'] ||= method_value
5967

60-
private_constant :METHOD_CACHE, :OLD_SPAN_NAMES
68+
SpanCreationAttributes.new(span_name: span_name, attributes: attributes)
69+
end
6170

62-
# Prepares all span data for the specified semantic convention in a single call
71+
# Prepares span data using stable semantic conventions
6372
# @param method [String, Symbol] The HTTP method
64-
# @param semconv [Symbol] The semantic convention to use (:stable or :old)
65-
# @return [SpanCreationAttributes] struct containing span_name, normalized_method, and original_method
66-
def self.span_attrs_for(method, semconv: :stable)
73+
# @return [SpanCreationAttributes] struct containing span_name and attributes hash
74+
def self.span_attrs_for_stable(method)
75+
client_context_attrs = OpenTelemetry::Common::HTTP::ClientContext.attributes
76+
url_template = client_context_attrs['url.template']
6777
normalized = METHOD_CACHE[method]
78+
attributes = client_context_attrs.dup
79+
80+
# Determine base span name and method value
6881
if normalized
69-
span_name = semconv == :old ? OLD_SPAN_NAMES[normalized] : normalized
70-
SpanCreationAttributes.new(
71-
span_name: span_name,
72-
normalized_method: normalized,
73-
original_method: nil
74-
)
82+
base_name = normalized
83+
method_value = normalized
84+
original = nil
7585
else
76-
SpanCreationAttributes.new(
77-
span_name: 'HTTP',
78-
normalized_method: '_OTHER',
79-
original_method: method.to_s
80-
)
86+
base_name = 'HTTP'
87+
method_value = '_OTHER'
88+
original = method.to_s
8189
end
90+
91+
span_name = url_template ? "#{base_name} #{url_template}" : base_name
92+
attributes['http.request.method'] ||= method_value
93+
attributes['http.request.method_original'] ||= original if original
94+
95+
SpanCreationAttributes.new(span_name: span_name, attributes: attributes)
96+
end
97+
98+
# Prepares span data using both old and stable semantic conventions
99+
# @param method [String, Symbol] The HTTP method
100+
# @return [SpanCreationAttributes] struct containing span_name and attributes hash
101+
def self.span_attrs_for_dup(method)
102+
client_context_attrs = OpenTelemetry::Common::HTTP::ClientContext.attributes
103+
url_template = client_context_attrs['url.template']
104+
normalized = METHOD_CACHE[method]
105+
attributes = client_context_attrs.dup
106+
107+
# Determine base span name and method value
108+
if normalized
109+
base_name = normalized
110+
method_value = normalized
111+
original = nil
112+
else
113+
base_name = 'HTTP'
114+
method_value = '_OTHER'
115+
original = method.to_s
116+
end
117+
118+
span_name = url_template ? "#{base_name} #{url_template}" : base_name
119+
attributes['http.method'] ||= method_value
120+
attributes['http.request.method'] ||= method_value
121+
attributes['http.request.method_original'] ||= original if original
122+
123+
SpanCreationAttributes.new(span_name: span_name, attributes: attributes)
82124
end
83125
end
84126
end

instrumentation/excon/lib/opentelemetry/instrumentation/excon/middlewares/dup/tracer_middleware.rb

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,26 @@ class TracerMiddleware < ::Excon::Middleware::Base
1717
def request_call(datum)
1818
return @stack.request_call(datum) if untraced?(datum)
1919

20-
span_data = HttpHelper.span_attrs_for(datum[:method])
20+
span_data = HttpHelper.span_attrs_for_dup(datum[:method])
2121

2222
cleansed_url = OpenTelemetry::Common::Utilities.cleanse_url(::Excon::Utils.request_uri(datum))
2323
attributes = {
2424
OpenTelemetry::SemanticConventions::Trace::HTTP_HOST => datum[:host],
25-
OpenTelemetry::SemanticConventions::Trace::HTTP_METHOD => span_data.normalized_method,
2625
OpenTelemetry::SemanticConventions::Trace::HTTP_SCHEME => datum[:scheme],
2726
OpenTelemetry::SemanticConventions::Trace::HTTP_TARGET => datum[:path],
2827
OpenTelemetry::SemanticConventions::Trace::HTTP_URL => cleansed_url,
2928
OpenTelemetry::SemanticConventions::Trace::NET_PEER_NAME => datum[:hostname],
3029
OpenTelemetry::SemanticConventions::Trace::NET_PEER_PORT => datum[:port],
31-
'http.request.method' => span_data.normalized_method,
3230
'url.scheme' => datum[:scheme],
3331
'url.path' => datum[:path],
3432
'url.full' => cleansed_url,
3533
'server.address' => datum[:hostname],
3634
'server.port' => datum[:port]
3735
}
38-
attributes['http.request.method_original'] = span_data.original_method if span_data.original_method
3936
attributes['url.query'] = datum[:query] if datum[:query]
4037
peer_service = Excon::Instrumentation.instance.config[:peer_service]
4138
attributes[OpenTelemetry::SemanticConventions::Trace::PEER_SERVICE] = peer_service if peer_service
42-
attributes.merge!(OpenTelemetry::Common::HTTP::ClientContext.attributes)
39+
attributes.merge!(span_data.attributes)
4340
span = tracer.start_span(span_data.span_name, attributes: attributes, kind: :client)
4441
ctx = OpenTelemetry::Trace.context_with_span(span)
4542
datum[:otel_span] = span

0 commit comments

Comments
 (0)