Skip to content

Commit b78e04a

Browse files
RUBY-3612 OpenTelemetry support
1 parent 7d0f906 commit b78e04a

File tree

22 files changed

+557
-34
lines changed

22 files changed

+557
-34
lines changed

gemfiles/standard.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
def standard_dependencies
55
gem 'yard', '>= 0.9.35'
66
gem 'ffi'
7+
gem 'opentelemetry-sdk'
78

89
group :development, :testing do
910
gem 'jruby-openssl', platforms: :jruby

lib/mongo.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
require 'mongo/socket'
7575
require 'mongo/srv'
7676
require 'mongo/timeout'
77+
require 'mongo/tracing'
7778
require 'mongo/uri'
7879
require 'mongo/version'
7980
require 'mongo/write_concern'

lib/mongo/client.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ class Client
112112
:ssl_verify_hostname,
113113
:ssl_verify_ocsp_endpoint,
114114
:timeout_ms,
115+
:tracing,
115116
:truncate_logs,
116117
:user,
117118
:wait_queue_timeout,
@@ -437,6 +438,20 @@ def hash
437438
# See Ruby's Zlib module for valid levels.
438439
# @option options [ Hash ] :resolv_options For internal driver use only.
439440
# Options to pass through to Resolv::DNS constructor for SRV lookups.
441+
# @option options [ Hash ] :tracing OpenTelemetry tracing options.
442+
# - :enabled => Boolean, whether to enable OpenTelemetry tracing. The default
443+
# value is nil that means that the configuration will be taken from the
444+
# OTEL_RUBY_INSTRUMENTATION_MONGODB_ENABLED environment variable.
445+
# - :tracer => OpenTelemetry::Trace::Tracer, the tracer to use for
446+
# tracing. Must be an implementation of OpenTelemetry::Trace::Tracer
447+
# interface.
448+
# - :query_text_max_length => Integer, the maximum length of the query text
449+
# to be included in the span attributes. If the query text exceeds this
450+
# length, it will be truncated. Value 0 means no query text
451+
# will be included in the span attributes. The default value is nil that
452+
# means that the configuration will be taken from the
453+
# OTEL_RUBY_INSTRUMENTATION_MONGODB_QUERY_TEXT_MAX_LENGTH environment
454+
# variable.
440455
# @option options [ Hash ] :auto_encryption_options Auto-encryption related
441456
# options.
442457
# - :key_vault_client => Client | nil, a client connected to the MongoDB
@@ -1195,6 +1210,15 @@ def timeout_sec
11951210
end
11961211
end
11971212

1213+
def tracer
1214+
tracing_opts = @options[:tracing] || {}
1215+
@tracer ||= Tracing.create_tracer(
1216+
enabled: tracing_opts[:enabled],
1217+
query_text_max_length: tracing_opts[:query_text_max_length],
1218+
otel_tracer: tracing_opts[:tracer],
1219+
)
1220+
end
1221+
11981222
private
11991223

12001224
# Attempts to parse the given list of addresses, using the provided options.

lib/mongo/collection.rb

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ class Collection
5757
# Delegate to the cluster for the next primary.
5858
def_delegators :cluster, :next_primary
5959

60+
def_delegators :client, :tracer
61+
6062
# Options that can be updated on a new Collection instance via the #with method.
6163
#
6264
# @since 2.1.0
@@ -865,19 +867,22 @@ def insert_one(document, opts = {})
865867
session: session,
866868
operation_timeouts: operation_timeouts(opts)
867869
)
868-
write_with_retry(write_concern, context: context) do |connection, txn_num, context|
869-
Operation::Insert.new(
870-
:documents => [ document ],
871-
:db_name => database.name,
872-
:coll_name => name,
873-
:write_concern => write_concern,
874-
:bypass_document_validation => !!opts[:bypass_document_validation],
875-
:options => opts,
876-
:id_generator => client.options[:id_generator],
877-
:session => session,
878-
:txn_num => txn_num,
879-
:comment => opts[:comment]
880-
).execute_with_connection(connection, context: context)
870+
operation = Operation::Insert.new(
871+
:documents => [ document ],
872+
:db_name => database.name,
873+
:coll_name => name,
874+
:write_concern => write_concern,
875+
:bypass_document_validation => !!opts[:bypass_document_validation],
876+
:options => opts,
877+
:id_generator => client.options[:id_generator],
878+
:session => session,
879+
:comment => opts[:comment]
880+
)
881+
tracer.trace_operation('insert_one', operation, context) do
882+
write_with_retry(write_concern, context: context) do |connection, txn_num, context|
883+
operation.txn_num = txn_num
884+
operation.execute_with_connection(connection, context: context)
885+
end
881886
end
882887
end
883888
end

lib/mongo/collection/view.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ class View
7272
# Delegate to the cluster for the next primary.
7373
def_delegators :cluster, :next_primary
7474

75+
def_delegators :client, :tracer
76+
7577
alias :selector :filter
7678

7779
# @return [ Integer | nil | The timeout_ms value that was passed as an

lib/mongo/collection/view/iterable.rb

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -88,19 +88,21 @@ def select_cursor(session)
8888
operation_timeouts: operation_timeouts,
8989
view: self
9090
)
91-
92-
if respond_to?(:write?, true) && write?
93-
server = server_selector.select_server(cluster, nil, session, write_aggregation: true)
94-
result = send_initial_query(server, context)
95-
96-
if use_query_cache?
97-
CachingCursor.new(view, result, server, session: session, context: context)
91+
op = initial_query_op(session)
92+
tracer.trace_operation('get_more', op, context) do
93+
if respond_to?(:write?, true) && write?
94+
server = server_selector.select_server(cluster, nil, session, write_aggregation: true)
95+
result = send_initial_query(server, context)
96+
97+
if use_query_cache?
98+
CachingCursor.new(view, result, server, session: session, context: context)
99+
else
100+
Cursor.new(view, result, server, session: session, context: context)
101+
end
98102
else
99-
Cursor.new(view, result, server, session: session, context: context)
100-
end
101-
else
102-
read_with_retry_cursor(session, server_selector, view, context: context) do |server|
103-
send_initial_query(server, context)
103+
read_with_retry_cursor(session, server_selector, view, context: context) do |server|
104+
send_initial_query(server, context)
105+
end
104106
end
105107
end
106108
end

lib/mongo/cursor.rb

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class Cursor
3939
def_delegators :@view, :collection
4040
def_delegators :collection, :client, :database
4141
def_delegators :@server, :cluster
42+
def_delegators :client, :tracer
4243

4344
# @return [ Collection::View ] view The collection view.
4445
attr_reader :view
@@ -514,10 +515,12 @@ def unregister
514515

515516
def execute_operation(op, context: nil)
516517
op_context = context || possibly_refreshed_context
517-
if @connection.nil?
518-
op.execute(@server, context: op_context)
519-
else
520-
op.execute_with_connection(@connection, context: op_context)
518+
tracer.trace_operation('find', op, op_context) do
519+
if @connection.nil?
520+
op.execute(@server, context: op_context)
521+
else
522+
op.execute_with_connection(@connection, context: op_context)
523+
end
521524
end
522525
end
523526

lib/mongo/operation/context.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ def initialize(
6969
attr_reader :session
7070
attr_reader :view
7171
attr_reader :options
72+
attr_accessor :tracer
7273

7374
# Returns a new Operation::Context with the deadline refreshed
7475
# and relative to the current moment.

lib/mongo/operation/insert/op_msg.rb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,14 @@ class OpMsg < OpMsgBase
3434
private
3535

3636
def get_result(connection, context, options = {})
37-
# This is a Mongo::Operation::Insert::Result
38-
Result.new(*dispatch_message(connection, context), @ids, context: context)
37+
message = build_message(connection, context)
38+
if (tracer = context.tracer)
39+
tracer.trace_command(message, context, connection) do
40+
Result.new(*dispatch_message(message, connection, context), @ids, context: context)
41+
end
42+
else
43+
Result.new(*dispatch_message(message, connection, context), @ids, context: context)
44+
end
3945
end
4046

4147
def selector(connection)

lib/mongo/operation/shared/executable.rb

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,18 @@ def result_class
104104
end
105105

106106
def get_result(connection, context, options = {})
107-
result_class.new(*dispatch_message(connection, context, options), context: context, connection: connection)
107+
message = build_message(connection, context)
108+
if (tracer = context.tracer)
109+
tracer.trace_command(message, context, connection) do
110+
result_class.new(*dispatch_message(message, connection, context, options), context: context, connection: connection)
111+
end
112+
else
113+
result_class.new(*dispatch_message(message, connection, context, options), context: context, connection: connection)
114+
end
108115
end
109116

110117
# Returns a Protocol::Message or nil as reply.
111-
def dispatch_message(connection, context, options = {})
112-
message = build_message(connection, context)
118+
def dispatch_message(message, connection, context, options = {})
113119
message = message.maybe_encrypt(connection, context)
114120
reply = connection.dispatch([ message ], context, options)
115121
[reply, connection.description, connection.global_id]

0 commit comments

Comments
 (0)