opentelemetry-instrumentation-httpx 0.5.1 → 0.6.1
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 +8 -2
- data/README.md +1 -1
- data/lib/opentelemetry/instrumentation/httpx/dup/plugin.rb +81 -103
- data/lib/opentelemetry/instrumentation/httpx/http_helper.rb +77 -29
- data/lib/opentelemetry/instrumentation/httpx/instrumentation.rb +1 -1
- data/lib/opentelemetry/instrumentation/httpx/old/plugin.rb +70 -90
- data/lib/opentelemetry/instrumentation/httpx/plugin.rb +54 -0
- data/lib/opentelemetry/instrumentation/httpx/stable/plugin.rb +70 -91
- data/lib/opentelemetry/instrumentation/httpx/version.rb +1 -1
- metadata +6 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7f74f31524d99c5a0565edf84892292d068322376584c58b7dc4aabbdea724fd
|
|
4
|
+
data.tar.gz: 74cdecfb4c9c7a7e3fe0164ce3977d53e33d4fa96c140265629b401f1e0dce77
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7f78e75c32d2fd6df5635ffac615ff7fdf7ff987b9ad93970d32d171887184ca2652f081569d843705dd45f715c6a08a592ef461186f02b1c610045a5ffabe11
|
|
7
|
+
data.tar.gz: efa536643cab0d4d878acb262d62ab2936bb0ce82e70368d2bd7c6367a2781afe327eedfaafb08fa6df1e5f4cb91eb63763008f416698b1130d8b834902e57df
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Release History: opentelemetry-instrumentation-httpx
|
|
2
2
|
|
|
3
|
+
### v0.6.1 / 2026-02-27
|
|
4
|
+
|
|
5
|
+
* FIXED: Httpx support to ~> 1.6 adapt to request init_time (#2030)
|
|
6
|
+
|
|
7
|
+
### v0.6.0 / 2026-01-13
|
|
8
|
+
|
|
9
|
+
* ADDED: HTTP Client Semconv v1.17 Span Naming
|
|
10
|
+
|
|
3
11
|
### v0.5.1 / 2025-11-25
|
|
4
12
|
|
|
5
13
|
* FIXED: Update support for unknown HTTP methods to match semantic conventions
|
|
@@ -7,7 +15,6 @@
|
|
|
7
15
|
### v0.5.0 / 2025-10-22
|
|
8
16
|
|
|
9
17
|
* BREAKING CHANGE: Min Ruby Version 3.2
|
|
10
|
-
|
|
11
18
|
* ADDED: Min Ruby Version 3.2
|
|
12
19
|
|
|
13
20
|
### v0.4.1 / 2025-09-30
|
|
@@ -29,7 +36,6 @@
|
|
|
29
36
|
### v0.2.0 / 2025-01-16
|
|
30
37
|
|
|
31
38
|
* BREAKING CHANGE: Set minimum supported version to Ruby 3.1
|
|
32
|
-
|
|
33
39
|
* ADDED: Set minimum supported version to Ruby 3.1
|
|
34
40
|
|
|
35
41
|
### v0.1.3 / 2024-11-26
|
data/README.md
CHANGED
|
@@ -63,4 +63,4 @@ When setting the value for `OTEL_SEMCONV_STABILITY_OPT_IN`, you can specify whic
|
|
|
63
63
|
|
|
64
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
65
|
|
|
66
|
-
For additional information on migration, please refer to our [documentation](https://opentelemetry.io/docs/specs/semconv/non-normative/http-migration/).
|
|
66
|
+
For additional information on migration, please refer to our [documentation](https://opentelemetry.io/docs/specs/semconv/non-normative/http-migration/).
|
|
@@ -8,129 +8,107 @@ module OpenTelemetry
|
|
|
8
8
|
module Instrumentation
|
|
9
9
|
module HTTPX
|
|
10
10
|
module Dup
|
|
11
|
+
# Dup Plugin
|
|
11
12
|
module Plugin
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
module_function
|
|
13
|
+
def self.load_dependencies(klass)
|
|
14
|
+
require_relative '../plugin'
|
|
15
|
+
klass.plugin(HTTPX::Plugin)
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
HTTPX::Plugin.const_set(:RequestTracer, RequestTracer)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
21
|
+
# Instruments around HTTPX's request/response lifecycle in order to generate
|
|
22
|
+
# an OTEL trace.
|
|
23
|
+
module RequestTracer
|
|
24
|
+
extend self
|
|
32
25
|
|
|
33
|
-
|
|
34
|
-
|
|
26
|
+
# initializes tracing on the +request+.
|
|
27
|
+
def call(request)
|
|
28
|
+
span = nil
|
|
35
29
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
# request objects are reused, when already buffered requests get rerouted to a different
|
|
31
|
+
# connection due to connection issues, or when they already got a response, but need to
|
|
32
|
+
# be retried. In such situations, the original span needs to be extended for the former,
|
|
33
|
+
# while a new is required for the latter.
|
|
34
|
+
request.on(:idle) do
|
|
35
|
+
span = nil
|
|
36
|
+
end
|
|
37
|
+
# the span is initialized when the request is buffered in the parser, which is the closest
|
|
38
|
+
# one gets to actually sending the request.
|
|
39
|
+
request.on(:headers) do
|
|
40
|
+
next if span
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
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)
|
|
42
|
+
span = initialize_span(request)
|
|
43
|
+
end
|
|
45
44
|
|
|
46
|
-
|
|
45
|
+
request.on(:response) do |response|
|
|
46
|
+
span = initialize_span(request, request.init_time) if !span && request.init_time
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
end
|
|
48
|
+
finish(response, span)
|
|
50
49
|
end
|
|
50
|
+
end
|
|
51
51
|
|
|
52
|
-
|
|
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
|
|
52
|
+
private
|
|
66
53
|
|
|
67
|
-
|
|
68
|
-
|
|
54
|
+
def finish(response, span)
|
|
55
|
+
if response.is_a?(::HTTPX::ErrorResponse)
|
|
56
|
+
span.record_exception(response.error)
|
|
57
|
+
span.status = Trace::Status.error(response.error.to_s)
|
|
58
|
+
else
|
|
59
|
+
span.set_attribute(OpenTelemetry::SemanticConventions::Trace::HTTP_STATUS_CODE, response.status)
|
|
60
|
+
span.set_attribute('http.response.status_code', response.status)
|
|
69
61
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
span_data = HttpHelper.span_attrs_for(verb)
|
|
76
|
-
|
|
77
|
-
config = HTTPX::Instrumentation.instance.config
|
|
78
|
-
|
|
79
|
-
attributes = {
|
|
80
|
-
OpenTelemetry::SemanticConventions::Trace::HTTP_HOST => uri.host,
|
|
81
|
-
OpenTelemetry::SemanticConventions::Trace::HTTP_METHOD => span_data.normalized_method,
|
|
82
|
-
OpenTelemetry::SemanticConventions::Trace::HTTP_SCHEME => uri.scheme,
|
|
83
|
-
OpenTelemetry::SemanticConventions::Trace::HTTP_TARGET => uri.path,
|
|
84
|
-
OpenTelemetry::SemanticConventions::Trace::HTTP_URL => "#{uri.scheme}://#{uri.host}",
|
|
85
|
-
OpenTelemetry::SemanticConventions::Trace::NET_PEER_NAME => uri.host,
|
|
86
|
-
OpenTelemetry::SemanticConventions::Trace::NET_PEER_PORT => uri.port,
|
|
87
|
-
'http.request.method' => span_data.normalized_method,
|
|
88
|
-
'url.scheme' => uri.scheme,
|
|
89
|
-
'url.path' => uri.path,
|
|
90
|
-
'url.full' => "#{uri.scheme}://#{uri.host}",
|
|
91
|
-
'server.address' => uri.host,
|
|
92
|
-
'server.port' => uri.port
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
attributes['http.request.method_original'] = span_data.original_method if span_data.original_method
|
|
96
|
-
attributes['url.query'] = uri.query unless uri.query.nil?
|
|
97
|
-
attributes[OpenTelemetry::SemanticConventions::Trace::PEER_SERVICE] = config[:peer_service] if config[:peer_service]
|
|
98
|
-
attributes.merge!(OpenTelemetry::Common::HTTP::ClientContext.attributes)
|
|
99
|
-
|
|
100
|
-
span = tracer.start_span(span_data.span_name, attributes: attributes, kind: :client, start_timestamp: start_time)
|
|
101
|
-
|
|
102
|
-
OpenTelemetry::Trace.with_span(span) do
|
|
103
|
-
OpenTelemetry.propagation.inject(request.headers)
|
|
62
|
+
if response.status.between?(400, 599)
|
|
63
|
+
err = ::HTTPX::HTTPError.new(response)
|
|
64
|
+
span.record_exception(err)
|
|
65
|
+
span.status = Trace::Status.error(err.to_s)
|
|
104
66
|
end
|
|
105
|
-
|
|
106
|
-
span
|
|
107
|
-
rescue StandardError => e
|
|
108
|
-
OpenTelemetry.handle_error(exception: e)
|
|
109
67
|
end
|
|
110
68
|
|
|
111
|
-
|
|
112
|
-
HTTPX::Instrumentation.instance.tracer
|
|
113
|
-
end
|
|
69
|
+
span.finish
|
|
114
70
|
end
|
|
115
71
|
|
|
116
|
-
#
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
72
|
+
# return a span initialized with the +@request+ state.
|
|
73
|
+
def initialize_span(request, start_time = ::Time.now)
|
|
74
|
+
verb = request.verb
|
|
75
|
+
uri = request.uri
|
|
76
|
+
|
|
77
|
+
span_data = HttpHelper.span_attrs_for_dup(verb)
|
|
78
|
+
|
|
79
|
+
config = HTTPX::Instrumentation.instance.config
|
|
80
|
+
|
|
81
|
+
attributes = {
|
|
82
|
+
OpenTelemetry::SemanticConventions::Trace::HTTP_HOST => uri.host,
|
|
83
|
+
OpenTelemetry::SemanticConventions::Trace::HTTP_SCHEME => uri.scheme,
|
|
84
|
+
OpenTelemetry::SemanticConventions::Trace::HTTP_TARGET => uri.path,
|
|
85
|
+
OpenTelemetry::SemanticConventions::Trace::HTTP_URL => "#{uri.scheme}://#{uri.host}",
|
|
86
|
+
OpenTelemetry::SemanticConventions::Trace::NET_PEER_NAME => uri.host,
|
|
87
|
+
OpenTelemetry::SemanticConventions::Trace::NET_PEER_PORT => uri.port,
|
|
88
|
+
'url.scheme' => uri.scheme,
|
|
89
|
+
'url.path' => uri.path,
|
|
90
|
+
'url.full' => "#{uri.scheme}://#{uri.host}",
|
|
91
|
+
'server.address' => uri.host,
|
|
92
|
+
'server.port' => uri.port
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
attributes['url.query'] = uri.query unless uri.query.nil?
|
|
96
|
+
attributes[OpenTelemetry::SemanticConventions::Trace::PEER_SERVICE] = config[:peer_service] if config[:peer_service]
|
|
97
|
+
attributes.merge!(span_data.attributes)
|
|
98
|
+
|
|
99
|
+
span = tracer.start_span(span_data.span_name, attributes: attributes, kind: :client, start_timestamp: start_time)
|
|
100
|
+
|
|
101
|
+
OpenTelemetry::Trace.with_span(span) do
|
|
102
|
+
OpenTelemetry.propagation.inject(request.headers)
|
|
122
103
|
end
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
# Connection patch to start monitoring on initialization.
|
|
126
|
-
module ConnectionMethods
|
|
127
|
-
attr_reader :init_time
|
|
128
104
|
|
|
129
|
-
|
|
130
|
-
|
|
105
|
+
span
|
|
106
|
+
rescue StandardError => e
|
|
107
|
+
OpenTelemetry.handle_error(exception: e)
|
|
108
|
+
end
|
|
131
109
|
|
|
132
|
-
|
|
133
|
-
|
|
110
|
+
def tracer
|
|
111
|
+
HTTPX::Instrumentation.instance.tracer
|
|
134
112
|
end
|
|
135
113
|
end
|
|
136
114
|
end
|
|
@@ -11,7 +11,7 @@ module OpenTelemetry
|
|
|
11
11
|
# @api private
|
|
12
12
|
module HttpHelper
|
|
13
13
|
# Lightweight struct to hold span creation attributes
|
|
14
|
-
SpanCreationAttributes = Struct.new(:span_name, :
|
|
14
|
+
SpanCreationAttributes = Struct.new(:span_name, :attributes, keyword_init: true)
|
|
15
15
|
|
|
16
16
|
# Pre-computed mapping to avoid string allocations during normalization
|
|
17
17
|
METHOD_CACHE = {
|
|
@@ -44,41 +44,89 @@ module OpenTelemetry
|
|
|
44
44
|
:trace => 'TRACE'
|
|
45
45
|
}.freeze
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
+
OLD_SPAN_NAMES_BY_METHOD = METHOD_CACHE.values.uniq.each_with_object({}) do |method, hash|
|
|
50
|
+
hash[method] = "HTTP #{method}"
|
|
51
|
+
end.freeze
|
|
59
52
|
|
|
60
|
-
private_constant :
|
|
53
|
+
private_constant :OLD_SPAN_NAMES_BY_METHOD
|
|
61
54
|
|
|
62
|
-
# Prepares
|
|
55
|
+
# Prepares span data using old semantic conventions
|
|
63
56
|
# @param method [String, Symbol] The HTTP method
|
|
64
|
-
# @
|
|
65
|
-
|
|
66
|
-
|
|
57
|
+
# @return [SpanCreationAttributes] struct containing span_name and attributes hash
|
|
58
|
+
def self.span_attrs_for_old(method)
|
|
59
|
+
client_context_attrs = OpenTelemetry::Common::HTTP::ClientContext.attributes
|
|
67
60
|
normalized = METHOD_CACHE[method]
|
|
61
|
+
attributes = client_context_attrs.dup
|
|
62
|
+
|
|
63
|
+
# Determine base span name and method value
|
|
64
|
+
if normalized
|
|
65
|
+
span_name = OLD_SPAN_NAMES_BY_METHOD[normalized]
|
|
66
|
+
method_value = normalized
|
|
67
|
+
else
|
|
68
|
+
span_name = 'HTTP'
|
|
69
|
+
method_value = '_OTHER'
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
attributes['http.method'] ||= method_value
|
|
73
|
+
|
|
74
|
+
SpanCreationAttributes.new(span_name: span_name, attributes: attributes)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Prepares span data using stable semantic conventions
|
|
78
|
+
# @param method [String, Symbol] The HTTP method
|
|
79
|
+
# @return [SpanCreationAttributes] struct containing span_name and attributes hash
|
|
80
|
+
def self.span_attrs_for_stable(method)
|
|
81
|
+
client_context_attrs = OpenTelemetry::Common::HTTP::ClientContext.attributes
|
|
82
|
+
url_template = client_context_attrs['url.template']
|
|
83
|
+
normalized = METHOD_CACHE[method]
|
|
84
|
+
attributes = client_context_attrs.dup
|
|
85
|
+
|
|
86
|
+
# Determine base span name and method value
|
|
68
87
|
if normalized
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
normalized_method: normalized,
|
|
73
|
-
original_method: nil
|
|
74
|
-
)
|
|
88
|
+
base_name = normalized
|
|
89
|
+
method_value = normalized
|
|
90
|
+
original = nil
|
|
75
91
|
else
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
original_method: method.to_s
|
|
80
|
-
)
|
|
92
|
+
base_name = 'HTTP'
|
|
93
|
+
method_value = '_OTHER'
|
|
94
|
+
original = method.to_s
|
|
81
95
|
end
|
|
96
|
+
|
|
97
|
+
span_name = url_template ? "#{base_name} #{url_template}" : base_name
|
|
98
|
+
attributes['http.request.method'] ||= method_value
|
|
99
|
+
attributes['http.request.method_original'] ||= original if original
|
|
100
|
+
|
|
101
|
+
SpanCreationAttributes.new(span_name: span_name, attributes: attributes)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Prepares span data using both old and stable semantic conventions
|
|
105
|
+
# @param method [String, Symbol] The HTTP method
|
|
106
|
+
# @return [SpanCreationAttributes] struct containing span_name and attributes hash
|
|
107
|
+
def self.span_attrs_for_dup(method)
|
|
108
|
+
client_context_attrs = OpenTelemetry::Common::HTTP::ClientContext.attributes
|
|
109
|
+
url_template = client_context_attrs['url.template']
|
|
110
|
+
normalized = METHOD_CACHE[method]
|
|
111
|
+
attributes = client_context_attrs.dup
|
|
112
|
+
|
|
113
|
+
# Determine base span name and method value
|
|
114
|
+
if normalized
|
|
115
|
+
base_name = normalized
|
|
116
|
+
method_value = normalized
|
|
117
|
+
original = nil
|
|
118
|
+
else
|
|
119
|
+
base_name = 'HTTP'
|
|
120
|
+
method_value = '_OTHER'
|
|
121
|
+
original = method.to_s
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
span_name = url_template ? "#{base_name} #{url_template}" : base_name
|
|
125
|
+
attributes['http.method'] ||= method_value
|
|
126
|
+
attributes['http.request.method'] ||= method_value
|
|
127
|
+
attributes['http.request.method_original'] ||= original if original
|
|
128
|
+
|
|
129
|
+
SpanCreationAttributes.new(span_name: span_name, attributes: attributes)
|
|
82
130
|
end
|
|
83
131
|
end
|
|
84
132
|
end
|
|
@@ -8,120 +8,100 @@ module OpenTelemetry
|
|
|
8
8
|
module Instrumentation
|
|
9
9
|
module HTTPX
|
|
10
10
|
module Old
|
|
11
|
+
# Old Plugin
|
|
11
12
|
module Plugin
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
module_function
|
|
13
|
+
def self.load_dependencies(klass)
|
|
14
|
+
require_relative '../plugin'
|
|
15
|
+
klass.plugin(HTTPX::Plugin)
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
|
17
|
+
HTTPX::Plugin.const_set(:RequestTracer, RequestTracer)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
35
20
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
21
|
+
# Instruments around HTTPX's request/response lifecycle in order to generate
|
|
22
|
+
# an OTEL trace.
|
|
23
|
+
module RequestTracer
|
|
24
|
+
extend self
|
|
39
25
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
# and the tracing object in inserted before the on response callback is called.
|
|
44
|
-
span = initialize_span(request, response.error.connection.init_time)
|
|
26
|
+
# initializes tracing on the +request+.
|
|
27
|
+
def call(request)
|
|
28
|
+
span = nil
|
|
45
29
|
|
|
46
|
-
|
|
30
|
+
# request objects are reused, when already buffered requests get rerouted to a different
|
|
31
|
+
# connection due to connection issues, or when they already got a response, but need to
|
|
32
|
+
# be retried. In such situations, the original span needs to be extended for the former,
|
|
33
|
+
# while a new is required for the latter.
|
|
34
|
+
request.on(:idle) do
|
|
35
|
+
span = nil
|
|
36
|
+
end
|
|
37
|
+
# the span is initialized when the request is buffered in the parser, which is the closest
|
|
38
|
+
# one gets to actually sending the request.
|
|
39
|
+
request.on(:headers) do
|
|
40
|
+
next if span
|
|
47
41
|
|
|
48
|
-
|
|
49
|
-
end
|
|
42
|
+
span = initialize_span(request)
|
|
50
43
|
end
|
|
51
44
|
|
|
52
|
-
|
|
53
|
-
if
|
|
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
|
|
45
|
+
request.on(:response) do |response|
|
|
46
|
+
span = initialize_span(request, request.init_time) if !span && request.init_time
|
|
65
47
|
|
|
66
|
-
span
|
|
48
|
+
finish(response, span)
|
|
67
49
|
end
|
|
50
|
+
end
|
|
68
51
|
|
|
69
|
-
|
|
70
|
-
def initialize_span(request, start_time = ::Time.now)
|
|
71
|
-
verb = request.verb
|
|
72
|
-
uri = request.uri
|
|
52
|
+
private
|
|
73
53
|
|
|
74
|
-
|
|
54
|
+
def finish(response, span)
|
|
55
|
+
if response.is_a?(::HTTPX::ErrorResponse)
|
|
56
|
+
span.record_exception(response.error)
|
|
57
|
+
span.status = Trace::Status.error(response.error.to_s)
|
|
58
|
+
else
|
|
59
|
+
span.set_attribute(OpenTelemetry::SemanticConventions::Trace::HTTP_STATUS_CODE, response.status)
|
|
75
60
|
|
|
76
|
-
|
|
61
|
+
if response.status.between?(400, 599)
|
|
62
|
+
err = ::HTTPX::HTTPError.new(response)
|
|
63
|
+
span.record_exception(err)
|
|
64
|
+
span.status = Trace::Status.error(err.to_s)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
77
67
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
OpenTelemetry::SemanticConventions::Trace::HTTP_METHOD => span_data.normalized_method,
|
|
81
|
-
OpenTelemetry::SemanticConventions::Trace::HTTP_SCHEME => uri.scheme,
|
|
82
|
-
OpenTelemetry::SemanticConventions::Trace::HTTP_TARGET => uri.path,
|
|
83
|
-
OpenTelemetry::SemanticConventions::Trace::HTTP_URL => "#{uri.scheme}://#{uri.host}",
|
|
84
|
-
OpenTelemetry::SemanticConventions::Trace::NET_PEER_NAME => uri.host,
|
|
85
|
-
OpenTelemetry::SemanticConventions::Trace::NET_PEER_PORT => uri.port
|
|
86
|
-
}
|
|
68
|
+
span.finish
|
|
69
|
+
end
|
|
87
70
|
|
|
88
|
-
|
|
89
|
-
|
|
71
|
+
# return a span initialized with the +@request+ state.
|
|
72
|
+
def initialize_span(request, start_time = ::Time.now)
|
|
73
|
+
verb = request.verb
|
|
74
|
+
uri = request.uri
|
|
90
75
|
|
|
91
|
-
|
|
76
|
+
span_data = HttpHelper.span_attrs_for_old(verb)
|
|
92
77
|
|
|
93
|
-
|
|
94
|
-
OpenTelemetry.propagation.inject(request.headers)
|
|
95
|
-
end
|
|
78
|
+
config = HTTPX::Instrumentation.instance.config
|
|
96
79
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
OpenTelemetry.
|
|
100
|
-
|
|
80
|
+
attributes = {
|
|
81
|
+
OpenTelemetry::SemanticConventions::Trace::HTTP_HOST => uri.host,
|
|
82
|
+
OpenTelemetry::SemanticConventions::Trace::HTTP_SCHEME => uri.scheme,
|
|
83
|
+
OpenTelemetry::SemanticConventions::Trace::HTTP_TARGET => uri.path,
|
|
84
|
+
OpenTelemetry::SemanticConventions::Trace::HTTP_URL => "#{uri.scheme}://#{uri.host}",
|
|
85
|
+
OpenTelemetry::SemanticConventions::Trace::NET_PEER_NAME => uri.host,
|
|
86
|
+
OpenTelemetry::SemanticConventions::Trace::NET_PEER_PORT => uri.port
|
|
87
|
+
}
|
|
101
88
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
end
|
|
105
|
-
end
|
|
89
|
+
attributes[OpenTelemetry::SemanticConventions::Trace::PEER_SERVICE] = config[:peer_service] if config[:peer_service]
|
|
90
|
+
attributes.merge!(span_data.attributes)
|
|
106
91
|
|
|
107
|
-
|
|
108
|
-
module RequestMethods
|
|
109
|
-
def initialize(*)
|
|
110
|
-
super
|
|
92
|
+
span = tracer.start_span(span_data.span_name, attributes: attributes, kind: :client, start_timestamp: start_time)
|
|
111
93
|
|
|
112
|
-
|
|
94
|
+
OpenTelemetry::Trace.with_span(span) do
|
|
95
|
+
OpenTelemetry.propagation.inject(request.headers)
|
|
113
96
|
end
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
# Connection patch to start monitoring on initialization.
|
|
117
|
-
module ConnectionMethods
|
|
118
|
-
attr_reader :init_time
|
|
119
97
|
|
|
120
|
-
|
|
121
|
-
|
|
98
|
+
span
|
|
99
|
+
rescue StandardError => e
|
|
100
|
+
OpenTelemetry.handle_error(exception: e)
|
|
101
|
+
end
|
|
122
102
|
|
|
123
|
-
|
|
124
|
-
|
|
103
|
+
def tracer
|
|
104
|
+
HTTPX::Instrumentation.instance.tracer
|
|
125
105
|
end
|
|
126
106
|
end
|
|
127
107
|
end
|
|
@@ -0,0 +1,54 @@
|
|
|
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
|
+
# Base Plugin
|
|
11
|
+
module Plugin
|
|
12
|
+
# Request patch to initiate the trace on initialization.
|
|
13
|
+
module RequestMethods
|
|
14
|
+
attr_accessor :init_time
|
|
15
|
+
|
|
16
|
+
# intercepts request initialization to inject the tracing logic.
|
|
17
|
+
def initialize(*)
|
|
18
|
+
super
|
|
19
|
+
|
|
20
|
+
@init_time = nil
|
|
21
|
+
|
|
22
|
+
RequestTracer.call(self)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def response=(*)
|
|
26
|
+
# init_time should be set when it's send to a connection.
|
|
27
|
+
# However, there are situations where connection initialization fails.
|
|
28
|
+
# Example is the :ssrf_filter plugin, which raises an error on
|
|
29
|
+
# initialize if the host is an IP which matches against the known set.
|
|
30
|
+
# in such cases, we'll just set here right here.
|
|
31
|
+
@init_time ||= ::Time.now
|
|
32
|
+
|
|
33
|
+
super
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Connection mixin
|
|
38
|
+
module ConnectionMethods
|
|
39
|
+
def initialize(*)
|
|
40
|
+
super
|
|
41
|
+
|
|
42
|
+
@init_time = ::Time.now
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def send(request)
|
|
46
|
+
request.init_time ||= @init_time
|
|
47
|
+
|
|
48
|
+
super
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -8,120 +8,99 @@ module OpenTelemetry
|
|
|
8
8
|
module Instrumentation
|
|
9
9
|
module HTTPX
|
|
10
10
|
module Stable
|
|
11
|
+
# Stable Plugin
|
|
11
12
|
module Plugin
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
module_function
|
|
13
|
+
def self.load_dependencies(klass)
|
|
14
|
+
require_relative '../plugin'
|
|
15
|
+
klass.plugin(HTTPX::Plugin)
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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)
|
|
17
|
+
HTTPX::Plugin.const_set(:RequestTracer, RequestTracer)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
39
20
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
span = initialize_span(request, response.error.connection.init_time)
|
|
21
|
+
# Instruments around HTTPX's request/response lifecycle in order to generate
|
|
22
|
+
# an OTEL trace.
|
|
23
|
+
module RequestTracer
|
|
24
|
+
extend self
|
|
45
25
|
|
|
46
|
-
|
|
26
|
+
# initializes tracing on the +request+.
|
|
27
|
+
def call(request)
|
|
28
|
+
span = nil
|
|
47
29
|
|
|
48
|
-
|
|
49
|
-
|
|
30
|
+
# request objects are reused, when already buffered requests get rerouted to a different
|
|
31
|
+
# connection due to connection issues, or when they already got a response, but need to
|
|
32
|
+
# be retried. In such situations, the original span needs to be extended for the former,
|
|
33
|
+
# while a new is required for the latter.
|
|
34
|
+
request.on(:idle) do
|
|
35
|
+
span = nil
|
|
50
36
|
end
|
|
37
|
+
# the span is initialized when the request is buffered in the parser, which is the closest
|
|
38
|
+
# one gets to actually sending the request.
|
|
39
|
+
request.on(:headers) do
|
|
40
|
+
next if span
|
|
51
41
|
|
|
52
|
-
|
|
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
|
|
42
|
+
span = initialize_span(request)
|
|
67
43
|
end
|
|
68
44
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
verb = request.verb
|
|
72
|
-
uri = request.uri
|
|
45
|
+
request.on(:response) do |response|
|
|
46
|
+
span = initialize_span(request, request.init_time) if !span && request.init_time
|
|
73
47
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
48
|
+
finish(response, span)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
77
51
|
|
|
78
|
-
|
|
79
|
-
'http.request.method' => span_data.normalized_method,
|
|
80
|
-
'url.scheme' => uri.scheme,
|
|
81
|
-
'url.path' => uri.path,
|
|
82
|
-
'url.full' => "#{uri.scheme}://#{uri.host}",
|
|
83
|
-
'server.address' => uri.host,
|
|
84
|
-
'server.port' => uri.port
|
|
85
|
-
}
|
|
86
|
-
attributes['http.request.method_original'] = span_data.original_method if span_data.original_method
|
|
87
|
-
attributes['url.query'] = uri.query unless uri.query.nil?
|
|
88
|
-
attributes[OpenTelemetry::SemanticConventions::Trace::PEER_SERVICE] = config[:peer_service] if config[:peer_service]
|
|
89
|
-
attributes.merge!(OpenTelemetry::Common::HTTP::ClientContext.attributes)
|
|
52
|
+
private
|
|
90
53
|
|
|
91
|
-
|
|
54
|
+
def finish(response, span)
|
|
55
|
+
if response.is_a?(::HTTPX::ErrorResponse)
|
|
56
|
+
span.record_exception(response.error)
|
|
57
|
+
span.status = Trace::Status.error(response.error.to_s)
|
|
58
|
+
else
|
|
59
|
+
span.set_attribute('http.response.status_code', response.status)
|
|
92
60
|
|
|
93
|
-
|
|
94
|
-
|
|
61
|
+
if response.status.between?(400, 599)
|
|
62
|
+
err = ::HTTPX::HTTPError.new(response)
|
|
63
|
+
span.record_exception(err)
|
|
64
|
+
span.status = Trace::Status.error(err.to_s)
|
|
95
65
|
end
|
|
96
|
-
|
|
97
|
-
span
|
|
98
|
-
rescue StandardError => e
|
|
99
|
-
OpenTelemetry.handle_error(exception: e)
|
|
100
66
|
end
|
|
101
67
|
|
|
102
|
-
|
|
103
|
-
HTTPX::Instrumentation.instance.tracer
|
|
104
|
-
end
|
|
68
|
+
span.finish
|
|
105
69
|
end
|
|
106
70
|
|
|
107
|
-
#
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
71
|
+
# return a span initialized with the +@request+ state.
|
|
72
|
+
def initialize_span(request, start_time = ::Time.now)
|
|
73
|
+
verb = request.verb
|
|
74
|
+
uri = request.uri
|
|
111
75
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
76
|
+
span_data = HttpHelper.span_attrs_for_stable(verb)
|
|
77
|
+
|
|
78
|
+
config = HTTPX::Instrumentation.instance.config
|
|
115
79
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
80
|
+
attributes = {
|
|
81
|
+
'url.scheme' => uri.scheme,
|
|
82
|
+
'url.path' => uri.path,
|
|
83
|
+
'url.full' => "#{uri.scheme}://#{uri.host}",
|
|
84
|
+
'server.address' => uri.host,
|
|
85
|
+
'server.port' => uri.port
|
|
86
|
+
}
|
|
87
|
+
attributes['url.query'] = uri.query unless uri.query.nil?
|
|
88
|
+
attributes[OpenTelemetry::SemanticConventions::Trace::PEER_SERVICE] = config[:peer_service] if config[:peer_service]
|
|
89
|
+
attributes.merge!(span_data.attributes)
|
|
119
90
|
|
|
120
|
-
|
|
121
|
-
super
|
|
91
|
+
span = tracer.start_span(span_data.span_name, attributes: attributes, kind: :client, start_timestamp: start_time)
|
|
122
92
|
|
|
123
|
-
|
|
93
|
+
OpenTelemetry::Trace.with_span(span) do
|
|
94
|
+
OpenTelemetry.propagation.inject(request.headers)
|
|
124
95
|
end
|
|
96
|
+
|
|
97
|
+
span
|
|
98
|
+
rescue StandardError => e
|
|
99
|
+
OpenTelemetry.handle_error(exception: e)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def tracer
|
|
103
|
+
HTTPX::Instrumentation.instance.tracer
|
|
125
104
|
end
|
|
126
105
|
end
|
|
127
106
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: opentelemetry-instrumentation-httpx
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- OpenTelemetry Authors
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: opentelemetry-instrumentation-base
|
|
@@ -42,17 +41,17 @@ files:
|
|
|
42
41
|
- lib/opentelemetry/instrumentation/httpx/http_helper.rb
|
|
43
42
|
- lib/opentelemetry/instrumentation/httpx/instrumentation.rb
|
|
44
43
|
- lib/opentelemetry/instrumentation/httpx/old/plugin.rb
|
|
44
|
+
- lib/opentelemetry/instrumentation/httpx/plugin.rb
|
|
45
45
|
- lib/opentelemetry/instrumentation/httpx/stable/plugin.rb
|
|
46
46
|
- lib/opentelemetry/instrumentation/httpx/version.rb
|
|
47
47
|
homepage: https://github.com/open-telemetry/opentelemetry-ruby-contrib
|
|
48
48
|
licenses:
|
|
49
49
|
- Apache-2.0
|
|
50
50
|
metadata:
|
|
51
|
-
changelog_uri: https://rubydoc.info/gems/opentelemetry-instrumentation-httpx/0.
|
|
51
|
+
changelog_uri: https://rubydoc.info/gems/opentelemetry-instrumentation-httpx/0.6.1/file/CHANGELOG.md
|
|
52
52
|
source_code_uri: https://github.com/open-telemetry/opentelemetry-ruby-contrib/tree/main/instrumentation/http
|
|
53
53
|
bug_tracker_uri: https://github.com/open-telemetry/opentelemetry-ruby-contrib/issues
|
|
54
|
-
documentation_uri: https://rubydoc.info/gems/opentelemetry-instrumentation-httpx/0.
|
|
55
|
-
post_install_message:
|
|
54
|
+
documentation_uri: https://rubydoc.info/gems/opentelemetry-instrumentation-httpx/0.6.1
|
|
56
55
|
rdoc_options: []
|
|
57
56
|
require_paths:
|
|
58
57
|
- lib
|
|
@@ -67,8 +66,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
67
66
|
- !ruby/object:Gem::Version
|
|
68
67
|
version: '0'
|
|
69
68
|
requirements: []
|
|
70
|
-
rubygems_version:
|
|
71
|
-
signing_key:
|
|
69
|
+
rubygems_version: 4.0.3
|
|
72
70
|
specification_version: 4
|
|
73
71
|
summary: HTTPX instrumentation for the OpenTelemetry framework
|
|
74
72
|
test_files: []
|