opentelemetry-instrumentation-excon 0.23.0 → 0.25.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 +18 -10
- data/README.md +17 -0
- data/lib/opentelemetry/instrumentation/excon/instrumentation.rb +50 -10
- data/lib/opentelemetry/instrumentation/excon/middlewares/dup/tracer_middleware.rb +120 -0
- data/lib/opentelemetry/instrumentation/excon/middlewares/old/tracer_middleware.rb +111 -0
- data/lib/opentelemetry/instrumentation/excon/middlewares/stable/tracer_middleware.rb +111 -0
- data/lib/opentelemetry/instrumentation/excon/patches/dup/socket.rb +65 -0
- data/lib/opentelemetry/instrumentation/excon/patches/old/socket.rb +60 -0
- data/lib/opentelemetry/instrumentation/excon/patches/stable/socket.rb +60 -0
- data/lib/opentelemetry/instrumentation/excon/version.rb +1 -1
- metadata +12 -162
- data/lib/opentelemetry/instrumentation/excon/middlewares/tracer_middleware.rb +0 -109
- data/lib/opentelemetry/instrumentation/excon/patches/socket.rb +0 -58
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7ea79e9fcc8cbc462d3c21702cab73776349b0e7ee46d9b34dd642d4aa3ba73e
|
4
|
+
data.tar.gz: e60efad9f2b231bcc032be874b3012d0e4585c5c5119465651418263e6d0a634
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eb4ceb5db9e8aeb6c6667ef36231e2352d3f449b5ff93792585e1fba2bd09f98dc73c84c3793f93f5e35544d4761d32d2e98e5ad238c978504dbf09ac6fc2dcb
|
7
|
+
data.tar.gz: b1e5916299e2a3c09245861d95e8c618e48dfba216f09d9a2964ab2a2eb6505fbd5e953c86a0bb9e690b14b3a24cea1d7cd4de9d9a60dea6d1266b164771faf4
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Release History: opentelemetry-instrumentation-excon
|
2
2
|
|
3
|
+
### v0.25.0 / 2025-09-30
|
4
|
+
|
5
|
+
* ADDED: Bump minimum API Version to 1.7
|
6
|
+
|
7
|
+
### v0.24.0 / 2025-08-13
|
8
|
+
|
9
|
+
* ADDED: Add Excon `OTEL_SEMCONV_STABILITY_OPT_IN` environment variable [#1569](https://github.com/open-telemetry/opentelemetry-ruby-contrib/pull/1569)
|
10
|
+
|
3
11
|
### v0.23.0 / 2025-01-16
|
4
12
|
|
5
13
|
* BREAKING CHANGE: Set minimum supported version to Ruby 3.1
|
@@ -43,23 +51,23 @@
|
|
43
51
|
|
44
52
|
### v0.21.1 / 2023-06-05
|
45
53
|
|
46
|
-
* FIXED: Base config options
|
54
|
+
* FIXED: Base config options
|
47
55
|
|
48
56
|
### v0.21.0 / 2023-04-17
|
49
57
|
|
50
|
-
* BREAKING CHANGE: Drop support for EoL Ruby 2.7
|
58
|
+
* BREAKING CHANGE: Drop support for EoL Ruby 2.7
|
51
59
|
|
52
|
-
* ADDED: Drop support for EoL Ruby 2.7
|
60
|
+
* ADDED: Drop support for EoL Ruby 2.7
|
53
61
|
|
54
62
|
### v0.20.1 / 2023-01-14
|
55
63
|
|
56
|
-
* DOCS: Fix gem homepage
|
57
|
-
* DOCS: More gem documentation fixes
|
64
|
+
* DOCS: Fix gem homepage
|
65
|
+
* DOCS: More gem documentation fixes
|
58
66
|
|
59
67
|
### v0.20.0 / 2022-06-09
|
60
68
|
|
61
69
|
* Upgrading Base dependency version
|
62
|
-
* FIXED: Broken test file requirements
|
70
|
+
* FIXED: Broken test file requirements
|
63
71
|
|
64
72
|
### v0.19.5 / 2022-05-05
|
65
73
|
|
@@ -67,7 +75,7 @@
|
|
67
75
|
|
68
76
|
### v0.19.4 / 2022-02-02
|
69
77
|
|
70
|
-
* FIXED: Excessive hash creation on context attr merging
|
78
|
+
* FIXED: Excessive hash creation on context attr merging
|
71
79
|
|
72
80
|
### v0.19.3 / 2021-12-02
|
73
81
|
|
@@ -79,13 +87,13 @@
|
|
79
87
|
|
80
88
|
### v0.19.1 / 2021-08-12
|
81
89
|
|
82
|
-
* DOCS: Update docs to rely more on environment variable configuration
|
90
|
+
* DOCS: Update docs to rely more on environment variable configuration
|
83
91
|
|
84
92
|
### v0.19.0 / 2021-06-23
|
85
93
|
|
86
|
-
* BREAKING CHANGE: Total order constraint on span.status=
|
94
|
+
* BREAKING CHANGE: Total order constraint on span.status=
|
87
95
|
|
88
|
-
* FIXED: Total order constraint on span.status=
|
96
|
+
* FIXED: Total order constraint on span.status=
|
89
97
|
|
90
98
|
### v0.18.0 / 2021-05-21
|
91
99
|
|
data/README.md
CHANGED
@@ -48,3 +48,20 @@ The `opentelemetry-instrumentation-all` gem is distributed under the Apache 2.0
|
|
48
48
|
[community-meetings]: https://github.com/open-telemetry/community#community-meetings
|
49
49
|
[slack-channel]: https://cloud-native.slack.com/archives/C01NWKKMKMY
|
50
50
|
[discussions-url]: https://github.com/open-telemetry/opentelemetry-ruby/discussions
|
51
|
+
|
52
|
+
|
53
|
+
## HTTP semantic convention stability
|
54
|
+
|
55
|
+
In the OpenTelemetry ecosystem, HTTP semantic conventions have now reached a stable state. However, the initial Excon instrumentation was introduced before this stability was achieved, which resulted in HTTP attributes being based on an older version of the semantic conventions.
|
56
|
+
|
57
|
+
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.
|
58
|
+
|
59
|
+
When setting the value for `OTEL_SEMCONV_STABILITY_OPT_IN`, you can specify which conventions you wish to adopt:
|
60
|
+
|
61
|
+
- `http` - Emits the stable HTTP and networking conventions and ceases emitting the old conventions previously emitted by the instrumentation.
|
62
|
+
- `http/dup` - Emits both the old and stable HTTP and networking conventions, enabling a phased rollout of the stable semantic conventions.
|
63
|
+
- Default behavior (in the absence of either value) is to continue emitting the old HTTP and networking conventions the instrumentation previously emitted.
|
64
|
+
|
65
|
+
During the transition from old to stable conventions, Excon instrumentation code comes in three patch versions: `dup`, `old`, and `stable`. These versions are identical except for the attributes they send. Any changes to Excon instrumentation should consider all three patches.
|
66
|
+
|
67
|
+
For additional information on migration, please refer to our [documentation](https://opentelemetry.io/docs/specs/semconv/non-normative/http-migration/).
|
@@ -15,9 +15,10 @@ module OpenTelemetry
|
|
15
15
|
include OpenTelemetry::Instrumentation::Concerns::UntracedHosts
|
16
16
|
|
17
17
|
install do |_config|
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
patch_type = determine_semconv
|
19
|
+
send(:"require_dependencies_#{patch_type}")
|
20
|
+
send(:"add_middleware_#{patch_type}")
|
21
|
+
send(:"patch_#{patch_type}")
|
21
22
|
end
|
22
23
|
|
23
24
|
present do
|
@@ -28,17 +29,56 @@ module OpenTelemetry
|
|
28
29
|
|
29
30
|
private
|
30
31
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
32
|
+
def determine_semconv
|
33
|
+
stability_opt_in = ENV.fetch('OTEL_SEMCONV_STABILITY_OPT_IN', '')
|
34
|
+
values = stability_opt_in.split(',').map(&:strip)
|
35
|
+
|
36
|
+
if values.include?('http/dup')
|
37
|
+
'dup'
|
38
|
+
elsif values.include?('http')
|
39
|
+
'stable'
|
40
|
+
else
|
41
|
+
'old'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def require_dependencies_dup
|
46
|
+
require_relative 'middlewares/dup/tracer_middleware'
|
47
|
+
require_relative 'patches/dup/socket'
|
48
|
+
end
|
49
|
+
|
50
|
+
def require_dependencies_stable
|
51
|
+
require_relative 'middlewares/stable/tracer_middleware'
|
52
|
+
require_relative 'patches/stable/socket'
|
53
|
+
end
|
54
|
+
|
55
|
+
def require_dependencies_old
|
56
|
+
require_relative 'middlewares/old/tracer_middleware'
|
57
|
+
require_relative 'patches/old/socket'
|
58
|
+
end
|
59
|
+
|
60
|
+
def add_middleware_dup
|
61
|
+
::Excon.defaults[:middlewares] = Middlewares::Dup::TracerMiddleware.around_default_stack
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_middleware_stable
|
65
|
+
::Excon.defaults[:middlewares] = Middlewares::Stable::TracerMiddleware.around_default_stack
|
66
|
+
end
|
67
|
+
|
68
|
+
def add_middleware_old
|
69
|
+
::Excon.defaults[:middlewares] = Middlewares::Old::TracerMiddleware.around_default_stack
|
70
|
+
end
|
71
|
+
|
72
|
+
def patch_dup
|
73
|
+
::Excon::Socket.prepend(Patches::Dup::Socket)
|
34
74
|
end
|
35
75
|
|
36
|
-
def
|
37
|
-
::Excon.
|
76
|
+
def patch_stable
|
77
|
+
::Excon::Socket.prepend(Patches::Stable::Socket)
|
38
78
|
end
|
39
79
|
|
40
|
-
def
|
41
|
-
::Excon::Socket.prepend(Patches::Socket)
|
80
|
+
def patch_old
|
81
|
+
::Excon::Socket.prepend(Patches::Old::Socket)
|
42
82
|
end
|
43
83
|
end
|
44
84
|
end
|
@@ -0,0 +1,120 @@
|
|
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 Excon
|
10
|
+
module Middlewares
|
11
|
+
module Dup
|
12
|
+
# Excon middleware for instrumentation
|
13
|
+
class TracerMiddleware < ::Excon::Middleware::Base
|
14
|
+
HTTP_METHODS_TO_UPPERCASE = %w[connect delete get head options patch post put trace].each_with_object({}) do |method, hash|
|
15
|
+
uppercase_method = method.upcase
|
16
|
+
hash[method] = uppercase_method
|
17
|
+
hash[method.to_sym] = uppercase_method
|
18
|
+
hash[uppercase_method] = uppercase_method
|
19
|
+
end.freeze
|
20
|
+
|
21
|
+
HTTP_METHODS_TO_SPAN_NAMES = HTTP_METHODS_TO_UPPERCASE.values.each_with_object({}) do |uppercase_method, hash|
|
22
|
+
hash[uppercase_method] ||= uppercase_method
|
23
|
+
end.freeze
|
24
|
+
|
25
|
+
# Constant for the HTTP status range
|
26
|
+
HTTP_STATUS_SUCCESS_RANGE = (100..399)
|
27
|
+
|
28
|
+
def request_call(datum)
|
29
|
+
return @stack.request_call(datum) if untraced?(datum)
|
30
|
+
|
31
|
+
http_method = HTTP_METHODS_TO_UPPERCASE[datum[:method]]
|
32
|
+
cleansed_url = OpenTelemetry::Common::Utilities.cleanse_url(::Excon::Utils.request_uri(datum))
|
33
|
+
attributes = {
|
34
|
+
OpenTelemetry::SemanticConventions::Trace::HTTP_HOST => datum[:host],
|
35
|
+
OpenTelemetry::SemanticConventions::Trace::HTTP_METHOD => http_method,
|
36
|
+
OpenTelemetry::SemanticConventions::Trace::HTTP_SCHEME => datum[:scheme],
|
37
|
+
OpenTelemetry::SemanticConventions::Trace::HTTP_TARGET => datum[:path],
|
38
|
+
OpenTelemetry::SemanticConventions::Trace::HTTP_URL => cleansed_url,
|
39
|
+
OpenTelemetry::SemanticConventions::Trace::NET_PEER_NAME => datum[:hostname],
|
40
|
+
OpenTelemetry::SemanticConventions::Trace::NET_PEER_PORT => datum[:port],
|
41
|
+
'http.request.method' => http_method,
|
42
|
+
'url.scheme' => datum[:scheme],
|
43
|
+
'url.path' => datum[:path],
|
44
|
+
'url.full' => cleansed_url,
|
45
|
+
'server.address' => datum[:hostname],
|
46
|
+
'server.port' => datum[:port]
|
47
|
+
}
|
48
|
+
attributes['url.query'] = datum[:query] if datum[:query]
|
49
|
+
peer_service = Excon::Instrumentation.instance.config[:peer_service]
|
50
|
+
attributes[OpenTelemetry::SemanticConventions::Trace::PEER_SERVICE] = peer_service if peer_service
|
51
|
+
attributes.merge!(OpenTelemetry::Common::HTTP::ClientContext.attributes)
|
52
|
+
span = tracer.start_span(HTTP_METHODS_TO_SPAN_NAMES[http_method], attributes: attributes, kind: :client)
|
53
|
+
ctx = OpenTelemetry::Trace.context_with_span(span)
|
54
|
+
datum[:otel_span] = span
|
55
|
+
datum[:otel_token] = OpenTelemetry::Context.attach(ctx)
|
56
|
+
OpenTelemetry.propagation.inject(datum[:headers])
|
57
|
+
@stack.request_call(datum)
|
58
|
+
end
|
59
|
+
|
60
|
+
def response_call(datum)
|
61
|
+
@stack.response_call(datum).tap do |d|
|
62
|
+
handle_response(d)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def error_call(datum)
|
67
|
+
handle_response(datum)
|
68
|
+
@stack.error_call(datum)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns a copy of the default stack with the trace middleware injected
|
72
|
+
def self.around_default_stack
|
73
|
+
::Excon.defaults[:middlewares].dup.tap do |default_stack|
|
74
|
+
# If the default stack contains a version of the trace middleware already...
|
75
|
+
existing_trace_middleware = default_stack.find { |m| m <= TracerMiddleware }
|
76
|
+
default_stack.delete(existing_trace_middleware) if existing_trace_middleware
|
77
|
+
# Inject after the ResponseParser middleware
|
78
|
+
response_middleware_index = default_stack.index(::Excon::Middleware::ResponseParser).to_i
|
79
|
+
default_stack.insert(response_middleware_index + 1, self)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def handle_response(datum)
|
86
|
+
datum.delete(:otel_span)&.tap do |span|
|
87
|
+
return unless span.recording?
|
88
|
+
|
89
|
+
if datum.key?(:response)
|
90
|
+
response = datum[:response]
|
91
|
+
span.set_attribute(OpenTelemetry::SemanticConventions::Trace::HTTP_STATUS_CODE, response[:status])
|
92
|
+
span.set_attribute('http.response.status_code', response[:status])
|
93
|
+
span.status = OpenTelemetry::Trace::Status.error unless HTTP_STATUS_SUCCESS_RANGE.cover?(response[:status].to_i)
|
94
|
+
end
|
95
|
+
|
96
|
+
if datum.key?(:error)
|
97
|
+
span.status = OpenTelemetry::Trace::Status.error('Request has failed')
|
98
|
+
span.record_exception(datum[:error])
|
99
|
+
end
|
100
|
+
|
101
|
+
span.finish
|
102
|
+
OpenTelemetry::Context.detach(datum.delete(:otel_token)) if datum.include?(:otel_token)
|
103
|
+
end
|
104
|
+
rescue StandardError => e
|
105
|
+
OpenTelemetry.handle_error(e)
|
106
|
+
end
|
107
|
+
|
108
|
+
def tracer
|
109
|
+
Excon::Instrumentation.instance.tracer
|
110
|
+
end
|
111
|
+
|
112
|
+
def untraced?(datum)
|
113
|
+
datum.key?(:otel_span) || Excon::Instrumentation.instance.untraced?(datum[:host])
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,111 @@
|
|
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 Excon
|
10
|
+
module Middlewares
|
11
|
+
module Old
|
12
|
+
# Excon middleware for instrumentation
|
13
|
+
class TracerMiddleware < ::Excon::Middleware::Base
|
14
|
+
HTTP_METHODS_TO_UPPERCASE = %w[connect delete get head options patch post put trace].each_with_object({}) do |method, hash|
|
15
|
+
uppercase_method = method.upcase
|
16
|
+
hash[method] = uppercase_method
|
17
|
+
hash[method.to_sym] = uppercase_method
|
18
|
+
hash[uppercase_method] = uppercase_method
|
19
|
+
end.freeze
|
20
|
+
|
21
|
+
HTTP_METHODS_TO_SPAN_NAMES = HTTP_METHODS_TO_UPPERCASE.values.each_with_object({}) do |uppercase_method, hash|
|
22
|
+
hash[uppercase_method] ||= "HTTP #{uppercase_method}"
|
23
|
+
end.freeze
|
24
|
+
|
25
|
+
# Constant for the HTTP status range
|
26
|
+
HTTP_STATUS_SUCCESS_RANGE = (100..399)
|
27
|
+
|
28
|
+
def request_call(datum)
|
29
|
+
return @stack.request_call(datum) if untraced?(datum)
|
30
|
+
|
31
|
+
http_method = HTTP_METHODS_TO_UPPERCASE[datum[:method]]
|
32
|
+
attributes = {
|
33
|
+
OpenTelemetry::SemanticConventions::Trace::HTTP_HOST => datum[:host],
|
34
|
+
OpenTelemetry::SemanticConventions::Trace::HTTP_METHOD => http_method,
|
35
|
+
OpenTelemetry::SemanticConventions::Trace::HTTP_SCHEME => datum[:scheme],
|
36
|
+
OpenTelemetry::SemanticConventions::Trace::HTTP_TARGET => datum[:path],
|
37
|
+
OpenTelemetry::SemanticConventions::Trace::HTTP_URL => OpenTelemetry::Common::Utilities.cleanse_url(::Excon::Utils.request_uri(datum)),
|
38
|
+
OpenTelemetry::SemanticConventions::Trace::NET_PEER_NAME => datum[:hostname],
|
39
|
+
OpenTelemetry::SemanticConventions::Trace::NET_PEER_PORT => datum[:port]
|
40
|
+
}
|
41
|
+
peer_service = Excon::Instrumentation.instance.config[:peer_service]
|
42
|
+
attributes[OpenTelemetry::SemanticConventions::Trace::PEER_SERVICE] = peer_service if peer_service
|
43
|
+
attributes.merge!(OpenTelemetry::Common::HTTP::ClientContext.attributes)
|
44
|
+
span = tracer.start_span(HTTP_METHODS_TO_SPAN_NAMES[http_method], attributes: attributes, kind: :client)
|
45
|
+
ctx = OpenTelemetry::Trace.context_with_span(span)
|
46
|
+
datum[:otel_span] = span
|
47
|
+
datum[:otel_token] = OpenTelemetry::Context.attach(ctx)
|
48
|
+
OpenTelemetry.propagation.inject(datum[:headers])
|
49
|
+
@stack.request_call(datum)
|
50
|
+
end
|
51
|
+
|
52
|
+
def response_call(datum)
|
53
|
+
@stack.response_call(datum).tap do |d|
|
54
|
+
handle_response(d)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def error_call(datum)
|
59
|
+
handle_response(datum)
|
60
|
+
@stack.error_call(datum)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns a copy of the default stack with the trace middleware injected
|
64
|
+
def self.around_default_stack
|
65
|
+
::Excon.defaults[:middlewares].dup.tap do |default_stack|
|
66
|
+
# If the default stack contains a version of the trace middleware already...
|
67
|
+
existing_trace_middleware = default_stack.find { |m| m <= TracerMiddleware }
|
68
|
+
default_stack.delete(existing_trace_middleware) if existing_trace_middleware
|
69
|
+
# Inject after the ResponseParser middleware
|
70
|
+
response_middleware_index = default_stack.index(::Excon::Middleware::ResponseParser).to_i
|
71
|
+
default_stack.insert(response_middleware_index + 1, self)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def handle_response(datum)
|
78
|
+
datum.delete(:otel_span)&.tap do |span|
|
79
|
+
return unless span.recording?
|
80
|
+
|
81
|
+
if datum.key?(:response)
|
82
|
+
response = datum[:response]
|
83
|
+
span.set_attribute(OpenTelemetry::SemanticConventions::Trace::HTTP_STATUS_CODE, response[:status])
|
84
|
+
span.status = OpenTelemetry::Trace::Status.error unless HTTP_STATUS_SUCCESS_RANGE.cover?(response[:status].to_i)
|
85
|
+
end
|
86
|
+
|
87
|
+
if datum.key?(:error)
|
88
|
+
span.status = OpenTelemetry::Trace::Status.error('Request has failed')
|
89
|
+
span.record_exception(datum[:error])
|
90
|
+
end
|
91
|
+
|
92
|
+
span.finish
|
93
|
+
OpenTelemetry::Context.detach(datum.delete(:otel_token)) if datum.include?(:otel_token)
|
94
|
+
end
|
95
|
+
rescue StandardError => e
|
96
|
+
OpenTelemetry.handle_error(e)
|
97
|
+
end
|
98
|
+
|
99
|
+
def tracer
|
100
|
+
Excon::Instrumentation.instance.tracer
|
101
|
+
end
|
102
|
+
|
103
|
+
def untraced?(datum)
|
104
|
+
datum.key?(:otel_span) || Excon::Instrumentation.instance.untraced?(datum[:host])
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,111 @@
|
|
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 Excon
|
10
|
+
module Middlewares
|
11
|
+
module Stable
|
12
|
+
# Excon middleware for instrumentation
|
13
|
+
class TracerMiddleware < ::Excon::Middleware::Base
|
14
|
+
HTTP_METHODS_TO_UPPERCASE = %w[connect delete get head options patch post put trace].each_with_object({}) do |method, hash|
|
15
|
+
uppercase_method = method.upcase
|
16
|
+
hash[method] = uppercase_method
|
17
|
+
hash[method.to_sym] = uppercase_method
|
18
|
+
hash[uppercase_method] = uppercase_method
|
19
|
+
end.freeze
|
20
|
+
|
21
|
+
HTTP_METHODS_TO_SPAN_NAMES = HTTP_METHODS_TO_UPPERCASE.values.each_with_object({}) do |uppercase_method, hash|
|
22
|
+
hash[uppercase_method] ||= uppercase_method
|
23
|
+
end.freeze
|
24
|
+
|
25
|
+
# Constant for the HTTP status range
|
26
|
+
HTTP_STATUS_SUCCESS_RANGE = (100..399)
|
27
|
+
|
28
|
+
def request_call(datum)
|
29
|
+
return @stack.request_call(datum) if untraced?(datum)
|
30
|
+
|
31
|
+
http_method = HTTP_METHODS_TO_UPPERCASE[datum[:method]]
|
32
|
+
attributes = {
|
33
|
+
'http.request.method' => http_method,
|
34
|
+
'url.scheme' => datum[:scheme],
|
35
|
+
'url.path' => datum[:path],
|
36
|
+
'url.full' => OpenTelemetry::Common::Utilities.cleanse_url(::Excon::Utils.request_uri(datum)),
|
37
|
+
'server.address' => datum[:hostname],
|
38
|
+
'server.port' => datum[:port]
|
39
|
+
}
|
40
|
+
attributes['url.query'] = datum[:query] if datum[:query]
|
41
|
+
peer_service = Excon::Instrumentation.instance.config[:peer_service]
|
42
|
+
attributes[OpenTelemetry::SemanticConventions::Trace::PEER_SERVICE] = peer_service if peer_service
|
43
|
+
attributes.merge!(OpenTelemetry::Common::HTTP::ClientContext.attributes)
|
44
|
+
span = tracer.start_span(HTTP_METHODS_TO_SPAN_NAMES[http_method], attributes: attributes, kind: :client)
|
45
|
+
ctx = OpenTelemetry::Trace.context_with_span(span)
|
46
|
+
datum[:otel_span] = span
|
47
|
+
datum[:otel_token] = OpenTelemetry::Context.attach(ctx)
|
48
|
+
OpenTelemetry.propagation.inject(datum[:headers])
|
49
|
+
@stack.request_call(datum)
|
50
|
+
end
|
51
|
+
|
52
|
+
def response_call(datum)
|
53
|
+
@stack.response_call(datum).tap do |d|
|
54
|
+
handle_response(d)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def error_call(datum)
|
59
|
+
handle_response(datum)
|
60
|
+
@stack.error_call(datum)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns a copy of the default stack with the trace middleware injected
|
64
|
+
def self.around_default_stack
|
65
|
+
::Excon.defaults[:middlewares].dup.tap do |default_stack|
|
66
|
+
# If the default stack contains a version of the trace middleware already...
|
67
|
+
existing_trace_middleware = default_stack.find { |m| m <= TracerMiddleware }
|
68
|
+
default_stack.delete(existing_trace_middleware) if existing_trace_middleware
|
69
|
+
# Inject after the ResponseParser middleware
|
70
|
+
response_middleware_index = default_stack.index(::Excon::Middleware::ResponseParser).to_i
|
71
|
+
default_stack.insert(response_middleware_index + 1, self)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def handle_response(datum)
|
78
|
+
datum.delete(:otel_span)&.tap do |span|
|
79
|
+
return unless span.recording?
|
80
|
+
|
81
|
+
if datum.key?(:response)
|
82
|
+
response = datum[:response]
|
83
|
+
span.set_attribute('http.response.status_code', response[:status])
|
84
|
+
span.status = OpenTelemetry::Trace::Status.error unless HTTP_STATUS_SUCCESS_RANGE.cover?(response[:status].to_i)
|
85
|
+
end
|
86
|
+
|
87
|
+
if datum.key?(:error)
|
88
|
+
span.status = OpenTelemetry::Trace::Status.error('Request has failed')
|
89
|
+
span.record_exception(datum[:error])
|
90
|
+
end
|
91
|
+
|
92
|
+
span.finish
|
93
|
+
OpenTelemetry::Context.detach(datum.delete(:otel_token)) if datum.include?(:otel_token)
|
94
|
+
end
|
95
|
+
rescue StandardError => e
|
96
|
+
OpenTelemetry.handle_error(e)
|
97
|
+
end
|
98
|
+
|
99
|
+
def tracer
|
100
|
+
Excon::Instrumentation.instance.tracer
|
101
|
+
end
|
102
|
+
|
103
|
+
def untraced?(datum)
|
104
|
+
datum.key?(:otel_span) || Excon::Instrumentation.instance.untraced?(datum[:host])
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,65 @@
|
|
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 Excon
|
10
|
+
module Patches
|
11
|
+
module Dup
|
12
|
+
# Module to prepend to an Excon Socket for instrumentation
|
13
|
+
module Socket
|
14
|
+
private
|
15
|
+
|
16
|
+
def connect
|
17
|
+
return super if untraced?
|
18
|
+
|
19
|
+
if @data[:proxy]
|
20
|
+
conn_address = @data.dig(:proxy, :hostname)
|
21
|
+
conn_port = @data.dig(:proxy, :port)
|
22
|
+
else
|
23
|
+
conn_address = @data[:hostname]
|
24
|
+
conn_port = @port
|
25
|
+
end
|
26
|
+
|
27
|
+
attributes = {
|
28
|
+
OpenTelemetry::SemanticConventions::Trace::NET_PEER_NAME => conn_address,
|
29
|
+
OpenTelemetry::SemanticConventions::Trace::NET_PEER_PORT => conn_port,
|
30
|
+
'server.address' => conn_address,
|
31
|
+
'server.port' => conn_port
|
32
|
+
}.merge!(OpenTelemetry::Common::HTTP::ClientContext.attributes)
|
33
|
+
|
34
|
+
if is_a?(::Excon::SSLSocket) && @data[:proxy]
|
35
|
+
span_name = 'CONNECT'
|
36
|
+
span_kind = :client
|
37
|
+
else
|
38
|
+
span_name = 'connect'
|
39
|
+
span_kind = :internal
|
40
|
+
end
|
41
|
+
|
42
|
+
tracer.in_span(span_name, attributes: attributes, kind: span_kind) do
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def tracer
|
48
|
+
Excon::Instrumentation.instance.tracer
|
49
|
+
end
|
50
|
+
|
51
|
+
def untraced?
|
52
|
+
address = if @data[:proxy]
|
53
|
+
@data.dig(:proxy, :hostname)
|
54
|
+
else
|
55
|
+
@data[:hostname]
|
56
|
+
end
|
57
|
+
|
58
|
+
Excon::Instrumentation.instance.untraced?(address)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,60 @@
|
|
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 Excon
|
10
|
+
module Patches
|
11
|
+
module Old
|
12
|
+
# Module to prepend to an Excon Socket for instrumentation
|
13
|
+
module Socket
|
14
|
+
private
|
15
|
+
|
16
|
+
def connect
|
17
|
+
return super if untraced?
|
18
|
+
|
19
|
+
if @data[:proxy]
|
20
|
+
conn_address = @data.dig(:proxy, :hostname)
|
21
|
+
conn_port = @data.dig(:proxy, :port)
|
22
|
+
else
|
23
|
+
conn_address = @data[:hostname]
|
24
|
+
conn_port = @port
|
25
|
+
end
|
26
|
+
|
27
|
+
attributes = { OpenTelemetry::SemanticConventions::Trace::NET_PEER_NAME => conn_address, OpenTelemetry::SemanticConventions::Trace::NET_PEER_PORT => conn_port }.merge!(OpenTelemetry::Common::HTTP::ClientContext.attributes)
|
28
|
+
|
29
|
+
if is_a?(::Excon::SSLSocket) && @data[:proxy]
|
30
|
+
span_name = 'HTTP CONNECT'
|
31
|
+
span_kind = :client
|
32
|
+
else
|
33
|
+
span_name = 'connect'
|
34
|
+
span_kind = :internal
|
35
|
+
end
|
36
|
+
|
37
|
+
tracer.in_span(span_name, attributes: attributes, kind: span_kind) do
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def tracer
|
43
|
+
Excon::Instrumentation.instance.tracer
|
44
|
+
end
|
45
|
+
|
46
|
+
def untraced?
|
47
|
+
address = if @data[:proxy]
|
48
|
+
@data.dig(:proxy, :hostname)
|
49
|
+
else
|
50
|
+
@data[:hostname]
|
51
|
+
end
|
52
|
+
|
53
|
+
Excon::Instrumentation.instance.untraced?(address)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,60 @@
|
|
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 Excon
|
10
|
+
module Patches
|
11
|
+
module Stable
|
12
|
+
# Module to prepend to an Excon Socket for instrumentation
|
13
|
+
module Socket
|
14
|
+
private
|
15
|
+
|
16
|
+
def connect
|
17
|
+
return super if untraced?
|
18
|
+
|
19
|
+
if @data[:proxy]
|
20
|
+
conn_address = @data.dig(:proxy, :hostname)
|
21
|
+
conn_port = @data.dig(:proxy, :port)
|
22
|
+
else
|
23
|
+
conn_address = @data[:hostname]
|
24
|
+
conn_port = @port
|
25
|
+
end
|
26
|
+
|
27
|
+
attributes = { 'server.address' => conn_address, 'server.port' => conn_port }.merge!(OpenTelemetry::Common::HTTP::ClientContext.attributes)
|
28
|
+
|
29
|
+
if is_a?(::Excon::SSLSocket) && @data[:proxy]
|
30
|
+
span_name = 'CONNECT'
|
31
|
+
span_kind = :client
|
32
|
+
else
|
33
|
+
span_name = 'connect'
|
34
|
+
span_kind = :internal
|
35
|
+
end
|
36
|
+
|
37
|
+
tracer.in_span(span_name, attributes: attributes, kind: span_kind) do
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def tracer
|
43
|
+
Excon::Instrumentation.instance.tracer
|
44
|
+
end
|
45
|
+
|
46
|
+
def untraced?
|
47
|
+
address = if @data[:proxy]
|
48
|
+
@data.dig(:proxy, :hostname)
|
49
|
+
else
|
50
|
+
@data[:hostname]
|
51
|
+
end
|
52
|
+
|
53
|
+
Excon::Instrumentation.instance.untraced?(address)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
metadata
CHANGED
@@ -1,183 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: opentelemetry-instrumentation-excon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.25.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-09-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: opentelemetry-api
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '1.0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '1.0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: opentelemetry-instrumentation-base
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
30
16
|
requirements:
|
31
17
|
- - "~>"
|
32
18
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.23
|
19
|
+
version: '0.23'
|
34
20
|
type: :runtime
|
35
21
|
prerelease: false
|
36
22
|
version_requirements: !ruby/object:Gem::Requirement
|
37
23
|
requirements:
|
38
24
|
- - "~>"
|
39
25
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.23
|
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: webmock
|
155
|
-
requirement: !ruby/object:Gem::Requirement
|
156
|
-
requirements:
|
157
|
-
- - "~>"
|
158
|
-
- !ruby/object:Gem::Version
|
159
|
-
version: 3.24.0
|
160
|
-
type: :development
|
161
|
-
prerelease: false
|
162
|
-
version_requirements: !ruby/object:Gem::Requirement
|
163
|
-
requirements:
|
164
|
-
- - "~>"
|
165
|
-
- !ruby/object:Gem::Version
|
166
|
-
version: 3.24.0
|
167
|
-
- !ruby/object:Gem::Dependency
|
168
|
-
name: yard
|
169
|
-
requirement: !ruby/object:Gem::Requirement
|
170
|
-
requirements:
|
171
|
-
- - "~>"
|
172
|
-
- !ruby/object:Gem::Version
|
173
|
-
version: '0.9'
|
174
|
-
type: :development
|
175
|
-
prerelease: false
|
176
|
-
version_requirements: !ruby/object:Gem::Requirement
|
177
|
-
requirements:
|
178
|
-
- - "~>"
|
179
|
-
- !ruby/object:Gem::Version
|
180
|
-
version: '0.9'
|
26
|
+
version: '0.23'
|
181
27
|
description: Excon instrumentation for the OpenTelemetry framework
|
182
28
|
email:
|
183
29
|
- cncf-opentelemetry-contributors@lists.cncf.io
|
@@ -194,17 +40,21 @@ files:
|
|
194
40
|
- lib/opentelemetry/instrumentation/concerns/untraced_hosts.rb
|
195
41
|
- lib/opentelemetry/instrumentation/excon.rb
|
196
42
|
- lib/opentelemetry/instrumentation/excon/instrumentation.rb
|
197
|
-
- lib/opentelemetry/instrumentation/excon/middlewares/tracer_middleware.rb
|
198
|
-
- lib/opentelemetry/instrumentation/excon/
|
43
|
+
- lib/opentelemetry/instrumentation/excon/middlewares/dup/tracer_middleware.rb
|
44
|
+
- lib/opentelemetry/instrumentation/excon/middlewares/old/tracer_middleware.rb
|
45
|
+
- lib/opentelemetry/instrumentation/excon/middlewares/stable/tracer_middleware.rb
|
46
|
+
- lib/opentelemetry/instrumentation/excon/patches/dup/socket.rb
|
47
|
+
- lib/opentelemetry/instrumentation/excon/patches/old/socket.rb
|
48
|
+
- lib/opentelemetry/instrumentation/excon/patches/stable/socket.rb
|
199
49
|
- lib/opentelemetry/instrumentation/excon/version.rb
|
200
50
|
homepage: https://github.com/open-telemetry/opentelemetry-ruby-contrib
|
201
51
|
licenses:
|
202
52
|
- Apache-2.0
|
203
53
|
metadata:
|
204
|
-
changelog_uri: https://rubydoc.info/gems/opentelemetry-instrumentation-excon/0.
|
54
|
+
changelog_uri: https://rubydoc.info/gems/opentelemetry-instrumentation-excon/0.25.0/file/CHANGELOG.md
|
205
55
|
source_code_uri: https://github.com/open-telemetry/opentelemetry-ruby-contrib/tree/main/instrumentation/excon
|
206
56
|
bug_tracker_uri: https://github.com/open-telemetry/opentelemetry-ruby-contrib/issues
|
207
|
-
documentation_uri: https://rubydoc.info/gems/opentelemetry-instrumentation-excon/0.
|
57
|
+
documentation_uri: https://rubydoc.info/gems/opentelemetry-instrumentation-excon/0.25.0
|
208
58
|
post_install_message:
|
209
59
|
rdoc_options: []
|
210
60
|
require_paths:
|
@@ -1,109 +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 Excon
|
10
|
-
module Middlewares
|
11
|
-
# Excon middleware for instrumentation
|
12
|
-
class TracerMiddleware < ::Excon::Middleware::Base
|
13
|
-
HTTP_METHODS_TO_UPPERCASE = %w[connect delete get head options patch post put trace].each_with_object({}) do |method, hash|
|
14
|
-
uppercase_method = method.upcase
|
15
|
-
hash[method] = uppercase_method
|
16
|
-
hash[method.to_sym] = uppercase_method
|
17
|
-
hash[uppercase_method] = uppercase_method
|
18
|
-
end.freeze
|
19
|
-
|
20
|
-
HTTP_METHODS_TO_SPAN_NAMES = HTTP_METHODS_TO_UPPERCASE.values.each_with_object({}) do |uppercase_method, hash|
|
21
|
-
hash[uppercase_method] ||= "HTTP #{uppercase_method}"
|
22
|
-
end.freeze
|
23
|
-
|
24
|
-
# Constant for the HTTP status range
|
25
|
-
HTTP_STATUS_SUCCESS_RANGE = (100..399)
|
26
|
-
|
27
|
-
def request_call(datum)
|
28
|
-
return @stack.request_call(datum) if untraced?(datum)
|
29
|
-
|
30
|
-
http_method = HTTP_METHODS_TO_UPPERCASE[datum[:method]]
|
31
|
-
attributes = {
|
32
|
-
OpenTelemetry::SemanticConventions::Trace::HTTP_HOST => datum[:host],
|
33
|
-
OpenTelemetry::SemanticConventions::Trace::HTTP_METHOD => http_method,
|
34
|
-
OpenTelemetry::SemanticConventions::Trace::HTTP_SCHEME => datum[:scheme],
|
35
|
-
OpenTelemetry::SemanticConventions::Trace::HTTP_TARGET => datum[:path],
|
36
|
-
OpenTelemetry::SemanticConventions::Trace::HTTP_URL => OpenTelemetry::Common::Utilities.cleanse_url(::Excon::Utils.request_uri(datum)),
|
37
|
-
OpenTelemetry::SemanticConventions::Trace::NET_PEER_NAME => datum[:hostname],
|
38
|
-
OpenTelemetry::SemanticConventions::Trace::NET_PEER_PORT => datum[:port]
|
39
|
-
}
|
40
|
-
peer_service = Excon::Instrumentation.instance.config[:peer_service]
|
41
|
-
attributes[OpenTelemetry::SemanticConventions::Trace::PEER_SERVICE] = peer_service if peer_service
|
42
|
-
attributes.merge!(OpenTelemetry::Common::HTTP::ClientContext.attributes)
|
43
|
-
span = tracer.start_span(HTTP_METHODS_TO_SPAN_NAMES[http_method], attributes: attributes, kind: :client)
|
44
|
-
ctx = OpenTelemetry::Trace.context_with_span(span)
|
45
|
-
datum[:otel_span] = span
|
46
|
-
datum[:otel_token] = OpenTelemetry::Context.attach(ctx)
|
47
|
-
OpenTelemetry.propagation.inject(datum[:headers])
|
48
|
-
@stack.request_call(datum)
|
49
|
-
end
|
50
|
-
|
51
|
-
def response_call(datum)
|
52
|
-
@stack.response_call(datum).tap do |d|
|
53
|
-
handle_response(d)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def error_call(datum)
|
58
|
-
handle_response(datum)
|
59
|
-
@stack.error_call(datum)
|
60
|
-
end
|
61
|
-
|
62
|
-
# Returns a copy of the default stack with the trace middleware injected
|
63
|
-
def self.around_default_stack
|
64
|
-
::Excon.defaults[:middlewares].dup.tap do |default_stack|
|
65
|
-
# If the default stack contains a version of the trace middleware already...
|
66
|
-
existing_trace_middleware = default_stack.find { |m| m <= TracerMiddleware }
|
67
|
-
default_stack.delete(existing_trace_middleware) if existing_trace_middleware
|
68
|
-
# Inject after the ResponseParser middleware
|
69
|
-
response_middleware_index = default_stack.index(::Excon::Middleware::ResponseParser).to_i
|
70
|
-
default_stack.insert(response_middleware_index + 1, self)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
private
|
75
|
-
|
76
|
-
def handle_response(datum)
|
77
|
-
datum.delete(:otel_span)&.tap do |span|
|
78
|
-
return unless span.recording?
|
79
|
-
|
80
|
-
if datum.key?(:response)
|
81
|
-
response = datum[:response]
|
82
|
-
span.set_attribute(OpenTelemetry::SemanticConventions::Trace::HTTP_STATUS_CODE, response[:status])
|
83
|
-
span.status = OpenTelemetry::Trace::Status.error unless HTTP_STATUS_SUCCESS_RANGE.cover?(response[:status].to_i)
|
84
|
-
end
|
85
|
-
|
86
|
-
if datum.key?(:error)
|
87
|
-
span.status = OpenTelemetry::Trace::Status.error('Request has failed')
|
88
|
-
span.record_exception(datum[:error])
|
89
|
-
end
|
90
|
-
|
91
|
-
span.finish
|
92
|
-
OpenTelemetry::Context.detach(datum.delete(:otel_token)) if datum.include?(:otel_token)
|
93
|
-
end
|
94
|
-
rescue StandardError => e
|
95
|
-
OpenTelemetry.handle_error(e)
|
96
|
-
end
|
97
|
-
|
98
|
-
def tracer
|
99
|
-
Excon::Instrumentation.instance.tracer
|
100
|
-
end
|
101
|
-
|
102
|
-
def untraced?(datum)
|
103
|
-
datum.key?(:otel_span) || Excon::Instrumentation.instance.untraced?(datum[:host])
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
@@ -1,58 +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 Excon
|
10
|
-
module Patches
|
11
|
-
# Module to prepend to an Excon Socket for instrumentation
|
12
|
-
module Socket
|
13
|
-
private
|
14
|
-
|
15
|
-
def connect
|
16
|
-
return super if untraced?
|
17
|
-
|
18
|
-
if @data[:proxy]
|
19
|
-
conn_address = @data.dig(:proxy, :hostname)
|
20
|
-
conn_port = @data.dig(:proxy, :port)
|
21
|
-
else
|
22
|
-
conn_address = @data[:hostname]
|
23
|
-
conn_port = @port
|
24
|
-
end
|
25
|
-
|
26
|
-
attributes = { OpenTelemetry::SemanticConventions::Trace::NET_PEER_NAME => conn_address, OpenTelemetry::SemanticConventions::Trace::NET_PEER_PORT => conn_port }.merge!(OpenTelemetry::Common::HTTP::ClientContext.attributes)
|
27
|
-
|
28
|
-
if is_a?(::Excon::SSLSocket) && @data[:proxy]
|
29
|
-
span_name = 'HTTP CONNECT'
|
30
|
-
span_kind = :client
|
31
|
-
else
|
32
|
-
span_name = 'connect'
|
33
|
-
span_kind = :internal
|
34
|
-
end
|
35
|
-
|
36
|
-
tracer.in_span(span_name, attributes: attributes, kind: span_kind) do
|
37
|
-
super
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def tracer
|
42
|
-
Excon::Instrumentation.instance.tracer
|
43
|
-
end
|
44
|
-
|
45
|
-
def untraced?
|
46
|
-
address = if @data[:proxy]
|
47
|
-
@data.dig(:proxy, :hostname)
|
48
|
-
else
|
49
|
-
@data[:hostname]
|
50
|
-
end
|
51
|
-
|
52
|
-
Excon::Instrumentation.instance.untraced?(address)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|