Skip to content

Commit 3b55baa

Browse files
Merge branch 'main' into update-rdkafka-requirement
2 parents 8eb9e36 + 266a8ba commit 3b55baa

File tree

34 files changed

+1473
-122
lines changed

34 files changed

+1473
-122
lines changed

.cspell.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,4 @@ words:
8282
- webmocks
8383
- Xuan
8484
- yardoc
85+
- traceparent

.github/workflows/ci-contrib.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ jobs:
6969
- ottrace
7070
- vitess
7171
- xray
72+
- google_cloud_trace_context
7273
os:
7374
- ubuntu-latest
7475
name: "propagator-${{ matrix.gem }} / ${{ matrix.os }}"

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ This repository contains multiple Ruby gems:
7171
* Various resource detector gems located in subdirectories of `resources`
7272
* `opentelemetry-propagator-xray` located in the `propagator/xray` directory
7373
* `opentelemetry-propagator-ottrace` located in the `propagator/ottrace` directory
74+
* `opentelemetry-propagator-google_cloud_trace_context` located in the `propagator/google_cloud_trace_context` directory
7475

7576
Each of these gems has its configuration and tests.
7677

instrumentation/dalli/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Release History: opentelemetry-instrumentation-dalli
22

3+
### v0.27.2 / 2025-04-30
4+
5+
* FIXED: Do not pollute the `OpenTelemetry::Instrumentation` namespace
6+
37
### v0.27.1 / 2025-04-21
48

59
* FIXED: Only prepend Dalli patch if binary protocol defined

instrumentation/dalli/lib/opentelemetry/instrumentation/dalli/utils.rb

Lines changed: 59 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -6,70 +6,72 @@
66

77
module OpenTelemetry
88
module Instrumentation
9-
# Utility functions
10-
module Utils
11-
extend self
9+
module Dalli
10+
# Utility functions
11+
module Utils
12+
extend self
1213

13-
STRING_PLACEHOLDER = ''.encode(::Encoding::UTF_8).freeze
14-
CMD_MAX_LEN = 500
14+
STRING_PLACEHOLDER = ''.encode(::Encoding::UTF_8).freeze
15+
CMD_MAX_LEN = 500
1516

16-
OPNAME_MAPPING = {
17-
'get' => 'get',
18-
'cas' => 'get',
19-
'set' => 'set',
20-
'add' => 'add',
21-
'replace' => 'replace',
22-
'delete' => 'delete',
23-
'incr' => 'incr',
24-
'decr' => 'decr',
25-
'flush' => 'flush',
26-
'write_noop' => 'noop',
27-
'version' => 'version',
28-
'send_multiget' => 'getkq',
29-
# TODO: add better support for PipelinedGetter
30-
# In dalli 3.1, multiget has been refactored to use a more robust PipelinedGetter class.
31-
# The `pipelined_get` method has been introduced to the Dalli::Server to support this new class.
32-
# If PipelinedGetter makes instrumentation of multi operations easier, we should then migrate
33-
# instrumentation to Dalli::Client, since it seems to be a more stable chokepoint.
34-
# For now we're just ensuring we support this new Dalli::Server method.
35-
'pipelined_get' => 'getkq',
36-
'append' => 'append',
37-
'prepend' => 'prepend',
38-
'stats' => 'stat',
39-
'reset_stats' => 'stat',
40-
'multi_set' => 'setq',
41-
'multi_add' => 'addq',
42-
'multi_replace' => 'replaceq',
43-
'multi_delete' => 'deleteq',
44-
'touch' => 'touch',
45-
'gat' => 'gat'
46-
# 'sasl_authentication' => 'auth_negotiation',
47-
# 'sasl_authentication' => 'auth_request',
48-
}.freeze
17+
OPNAME_MAPPING = {
18+
'get' => 'get',
19+
'cas' => 'get',
20+
'set' => 'set',
21+
'add' => 'add',
22+
'replace' => 'replace',
23+
'delete' => 'delete',
24+
'incr' => 'incr',
25+
'decr' => 'decr',
26+
'flush' => 'flush',
27+
'write_noop' => 'noop',
28+
'version' => 'version',
29+
'send_multiget' => 'getkq',
30+
# TODO: add better support for PipelinedGetter
31+
# In dalli 3.1, multiget has been refactored to use a more robust PipelinedGetter class.
32+
# The `pipelined_get` method has been introduced to the Dalli::Server to support this new class.
33+
# If PipelinedGetter makes instrumentation of multi operations easier, we should then migrate
34+
# instrumentation to Dalli::Client, since it seems to be a more stable chokepoint.
35+
# For now we're just ensuring we support this new Dalli::Server method.
36+
'pipelined_get' => 'getkq',
37+
'append' => 'append',
38+
'prepend' => 'prepend',
39+
'stats' => 'stat',
40+
'reset_stats' => 'stat',
41+
'multi_set' => 'setq',
42+
'multi_add' => 'addq',
43+
'multi_replace' => 'replaceq',
44+
'multi_delete' => 'deleteq',
45+
'touch' => 'touch',
46+
'gat' => 'gat'
47+
# 'sasl_authentication' => 'auth_negotiation',
48+
# 'sasl_authentication' => 'auth_request',
49+
}.freeze
4950

50-
def opname(operation, multi)
51-
lookup_name = multi ? "multi_#{operation}" : operation.to_s
52-
OPNAME_MAPPING[lookup_name] || operation.to_s
53-
end
51+
def opname(operation, multi)
52+
lookup_name = multi ? "multi_#{operation}" : operation.to_s
53+
OPNAME_MAPPING[lookup_name] || operation.to_s
54+
end
5455

55-
def format_command(operation, args)
56-
placeholder = "#{operation} BLOB (OMITTED)"
56+
def format_command(operation, args)
57+
placeholder = "#{operation} BLOB (OMITTED)"
5758

58-
command = +operation.to_s
59-
args.flatten.each do |arg|
60-
str = arg.to_s
61-
if str.bytesize >= CMD_MAX_LEN
62-
command << ' BLOB (OMITTED)'
63-
elsif !str.empty?
64-
command << ' ' << str
59+
command = +operation.to_s
60+
args.flatten.each do |arg|
61+
str = arg.to_s
62+
if str.bytesize >= CMD_MAX_LEN
63+
command << ' BLOB (OMITTED)'
64+
elsif !str.empty?
65+
command << ' ' << str
66+
end
6567
end
66-
end
6768

68-
command = OpenTelemetry::Common::Utilities.utf8_encode(command, binary: true, placeholder: placeholder)
69-
OpenTelemetry::Common::Utilities.truncate(command, CMD_MAX_LEN)
70-
rescue StandardError => e
71-
OpenTelemetry.logger.debug("Error sanitizing Dalli operation: #{e}")
72-
placeholder
69+
command = OpenTelemetry::Common::Utilities.utf8_encode(command, binary: true, placeholder: placeholder)
70+
OpenTelemetry::Common::Utilities.truncate(command, CMD_MAX_LEN)
71+
rescue StandardError => e
72+
OpenTelemetry.logger.debug("Error sanitizing Dalli operation: #{e}")
73+
placeholder
74+
end
7375
end
7476
end
7577
end

instrumentation/dalli/lib/opentelemetry/instrumentation/dalli/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
module OpenTelemetry
88
module Instrumentation
99
module Dalli
10-
VERSION = '0.27.1'
10+
VERSION = '0.27.2'
1111
end
1212
end
1313
end

instrumentation/httpx/Appraisals

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,3 @@
77
appraise 'httpx-1' do
88
gem 'httpx', '~> 1.0'
99
end
10-
11-
appraise 'httpx-0' do
12-
gem 'httpx', '~> 0.24'
13-
end

instrumentation/httpx/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Release History: opentelemetry-instrumentation-httpx
22

3+
### v0.2.1 / 2025-04-29
4+
5+
* FIXED: Httpx instrumentation trace context propagation
6+
37
### v0.2.0 / 2025-01-16
48

59
* BREAKING CHANGE: Set minimum supported version to Ruby 3.1

instrumentation/httpx/lib/opentelemetry/instrumentation/httpx/plugin.rb

Lines changed: 75 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -10,83 +10,114 @@ module HTTPX
1010
module Plugin
1111
# Instruments around HTTPX's request/response lifecycle in order to generate
1212
# an OTEL trace.
13-
class RequestTracer
14-
# Constant for the HTTP status range
15-
HTTP_STATUS_SUCCESS_RANGE = (100..399)
13+
module RequestTracer
14+
module_function
15+
16+
# initializes tracing on the +request+.
17+
def call(request)
18+
span = nil
19+
20+
# request objects are reused, when already buffered requests get rerouted to a different
21+
# connection due to connection issues, or when they already got a response, but need to
22+
# be retried. In such situations, the original span needs to be extended for the former,
23+
# while a new is required for the latter.
24+
request.on(:idle) do
25+
span = nil
26+
end
27+
# the span is initialized when the request is buffered in the parser, which is the closest
28+
# one gets to actually sending the request.
29+
request.on(:headers) do
30+
next if span
31+
32+
span = initialize_span(request)
33+
end
34+
35+
request.on(:response) do |response|
36+
unless span
37+
next unless response.is_a?(::HTTPX::ErrorResponse) && response.error.respond_to?(:connection)
1638

17-
def initialize(request)
18-
@request = request
39+
# handles the case when the +error+ happened during name resolution, which means
40+
# that the tracing start point hasn't been triggered yet; in such cases, the approximate
41+
# initial resolving time is collected from the connection, and used as span start time,
42+
# and the tracing object in inserted before the on response callback is called.
43+
span = initialize_span(request, response.error.connection.init_time)
44+
45+
end
46+
47+
finish(response, span)
48+
end
1949
end
2050

21-
def call
22-
@request.on(:response, &method(:finish)) # rubocop:disable Performance/MethodObjectAsBlock
51+
def finish(response, span)
52+
if response.is_a?(::HTTPX::ErrorResponse)
53+
span.record_exception(response.error)
54+
span.status = Trace::Status.error(response.error.to_s)
55+
else
56+
span.set_attribute(OpenTelemetry::SemanticConventions::Trace::HTTP_STATUS_CODE, response.status)
57+
58+
if response.status.between?(400, 599)
59+
err = ::HTTPX::HTTPError.new(response)
60+
span.record_exception(err)
61+
span.status = Trace::Status.error(err.to_s)
62+
end
63+
end
2364

24-
uri = @request.uri
25-
request_method = @request.verb
26-
span_name = "HTTP #{request_method}"
65+
span.finish
66+
end
67+
68+
# return a span initialized with the +@request+ state.
69+
def initialize_span(request, start_time = ::Time.now)
70+
verb = request.verb
71+
uri = request.uri
72+
73+
config = HTTPX::Instrumentation.instance.config
2774

2875
attributes = {
2976
OpenTelemetry::SemanticConventions::Trace::HTTP_HOST => uri.host,
30-
OpenTelemetry::SemanticConventions::Trace::HTTP_METHOD => request_method,
77+
OpenTelemetry::SemanticConventions::Trace::HTTP_METHOD => verb,
3178
OpenTelemetry::SemanticConventions::Trace::HTTP_SCHEME => uri.scheme,
3279
OpenTelemetry::SemanticConventions::Trace::HTTP_TARGET => uri.path,
3380
OpenTelemetry::SemanticConventions::Trace::HTTP_URL => "#{uri.scheme}://#{uri.host}",
3481
OpenTelemetry::SemanticConventions::Trace::NET_PEER_NAME => uri.host,
3582
OpenTelemetry::SemanticConventions::Trace::NET_PEER_PORT => uri.port
3683
}
37-
config = HTTPX::Instrumentation.instance.config
38-
attributes[OpenTelemetry::SemanticConventions::Trace::PEER_SERVICE] = config[:peer_service] if config[:peer_service]
39-
attributes.merge!(
40-
OpenTelemetry::Common::HTTP::ClientContext.attributes
41-
)
4284

43-
@span = tracer.start_span(span_name, attributes: attributes, kind: :client)
44-
trace_ctx = OpenTelemetry::Trace.context_with_span(@span)
45-
@trace_token = OpenTelemetry::Context.attach(trace_ctx)
46-
47-
OpenTelemetry.propagation.inject(@request.headers)
48-
rescue StandardError => e
49-
OpenTelemetry.handle_error(exception: e)
50-
end
85+
attributes[OpenTelemetry::SemanticConventions::Trace::PEER_SERVICE] = config[:peer_service] if config[:peer_service]
86+
attributes.merge!(OpenTelemetry::Common::HTTP::ClientContext.attributes)
5187

52-
def finish(response)
53-
return unless @span
88+
span = tracer.start_span("HTTP #{verb}", attributes: attributes, kind: :client, start_timestamp: start_time)
5489

55-
if response.is_a?(::HTTPX::ErrorResponse)
56-
@span.record_exception(response.error)
57-
@span.status = Trace::Status.error("Unhandled exception of type: #{response.error.class}")
58-
else
59-
@span.set_attribute(OpenTelemetry::SemanticConventions::Trace::HTTP_STATUS_CODE, response.status)
60-
@span.status = Trace::Status.error unless HTTP_STATUS_SUCCESS_RANGE.cover?(response.status)
90+
OpenTelemetry::Trace.with_span(span) do
91+
OpenTelemetry.propagation.inject(request.headers)
6192
end
6293

63-
OpenTelemetry::Context.detach(@trace_token) if @trace_token
64-
@span.finish
94+
span
95+
rescue StandardError => e
96+
OpenTelemetry.handle_error(exception: e)
6597
end
6698

67-
private
68-
6999
def tracer
70100
HTTPX::Instrumentation.instance.tracer
71101
end
72102
end
73103

74-
# HTTPX::Request overrides
104+
# Request patch to initiate the trace on initialization.
75105
module RequestMethods
76-
def __otel_enable_trace!
77-
return if @__otel_enable_trace
106+
def initialize(*)
107+
super
78108

79-
RequestTracer.new(self).call
80-
@__otel_enable_trace = true
109+
RequestTracer.call(self)
81110
end
82111
end
83112

84-
# HTTPX::Connection overrides
113+
# Connection patch to start monitoring on initialization.
85114
module ConnectionMethods
86-
def send(request)
87-
request.__otel_enable_trace!
115+
attr_reader :init_time
88116

117+
def initialize(*)
89118
super
119+
120+
@init_time = ::Time.now
90121
end
91122
end
92123
end

instrumentation/httpx/lib/opentelemetry/instrumentation/httpx/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
module OpenTelemetry
88
module Instrumentation
99
module HTTPX
10-
VERSION = '0.2.0'
10+
VERSION = '0.2.1'
1111
end
1212
end
1313
end

0 commit comments

Comments
 (0)