123456789_123456789_123456789_123456789_123456789_

Class: Mongo::Tracing::OpenTelemetry::CommandTracer Private

Do not use. This class is for internal use only.
Relationships & Source Files
Inherits: Object
Defined in: lib/mongo/tracing/open_telemetry/command_tracer.rb

Overview

CommandTracer is responsible for tracing MongoDB server commands using ::Mongo::Tracing::OpenTelemetry.

Constant Summary

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Instance Attribute Details

#query_text?Boolean (readonly, private)

Checks if query text capture is enabled.

Returns:

  • (Boolean)

    true if query text should be captured.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/command_tracer.rb', line 254

def query_text?
  @query_text_max_length.positive?
end

Instance Method Details

#base_attributes(message) ⇒ Hash (private)

Returns base database and command attributes.

Parameters:

Returns:

  • (Hash)

    base span attributes.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/command_tracer.rb', line 136

def base_attributes(message)
  {
    'db.system' => 'mongodb',
    'db.namespace' => database(message),
    'db.collection.name' => collection_name(message),
    'db.command.name' => command_name(message),
    'db.query.summary' => query_summary(message),
    'db.query.text' => query_text(message)
  }
end

#collection_name(message) ⇒ String | nil (private)

Extracts the collection name from the command message.

Parameters:

Returns:

  • (String | nil)

    the collection name, or nil if not applicable.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/command_tracer.rb', line 220

def collection_name(message)
  case command_name(message)
  when 'getMore'
    message.documents.first['collection'].to_s
  when 'listCollections', 'listDatabases', 'commitTransaction', 'abortTransaction'
    nil
  else
    value = message.documents.first.values.first
    # Return nil if the value is not a string (e.g., for admin commands that have numeric values)
    value.is_a?(String) ? value : nil
  end
end

#command_name(message) ⇒ String (private)

Extracts the command name from the message.

Parameters:

Returns:

  • (String)

    the command name.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/command_tracer.rb', line 238

def command_name(message)
  message.documents.first.keys.first.to_s
end

#connection_attributes(connection) ⇒ Hash (private)

Returns connection-related attributes.

Parameters:

Returns:

  • (Hash)

    connection span attributes.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/command_tracer.rb', line 152

def connection_attributes(connection)
  {
    'server.port' => connection.address.port,
    'server.address' => connection.address.host,
    'network.transport' => connection.transport.to_s,
    'db.mongodb.server_connection_id' => connection.server.description.server_connection_id,
    'db.mongodb.driver_connection_id' => connection.id
  }
end

#create_command_span(message, connection) ⇒ OpenTelemetry::Trace::Span (private)

Creates a span for a command.

Parameters:

Returns:

  • (OpenTelemetry::Trace::Span)

    the created span.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/command_tracer.rb', line 85

def create_command_span(message, connection)
  @otel_tracer.start_span(
    command_name(message),
    attributes: span_attributes(message, connection),
    kind: :client
  )
end

#cursor_id(message) ⇒ Integer | nil (private)

Extracts the cursor ID from getMore commands.

Parameters:

Returns:

  • (Integer | nil)

    the cursor ID, or nil if not a getMore command.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/command_tracer.rb', line 263

def cursor_id(message)
  return unless command_name(message) == 'getMore'

  message.documents.first['getMore'].value
end

#database(message) ⇒ String (private)

Extracts the database name from the message.

Parameters:

Returns:

  • (String)

    the database name.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/command_tracer.rb', line 247

def database(message)
  message.documents.first['$db'].to_s
end

#handle_command_exception(span, exception) (private)

Handles exceptions that occur during command execution.

Parameters:

  • span (OpenTelemetry::Trace::Span | nil)

    the span.

  • exception (Exception)

    the exception that occurred.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/command_tracer.rb', line 108

def handle_command_exception(span, exception)
  return unless span

  if exception.is_a?(Mongo::Error::OperationFailure)
    span.set_attribute('db.response.status_code', exception.code.to_s)
  end
  span.record_exception(exception)
  span.status = ::OpenTelemetry::Trace::Status.error("Unhandled exception of type: #{exception.class}")
end

#lsid(message) ⇒ BSON::Binary | nil (private)

Extracts the logical session ID from the command.

Parameters:

Returns:

  • (BSON::Binary | nil)

    the session ID, or nil if not present.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/command_tracer.rb', line 274

def lsid(message)
  lsid_doc = message.documents.first['lsid']
  return unless lsid_doc

  lsid_doc['id'].to_uuid
end

#maybe_trace_error(result, span) (private)

Records error status code if the command failed.

Parameters:

  • result (Object)

    the command result.

  • span (OpenTelemetry::Trace::Span)

    the current span.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/command_tracer.rb', line 191

def maybe_trace_error(result, span)
  return if result.successful?

  span.set_attribute('db.response.status_code', result.error.code.to_s)
  begin
    result.validate!
  rescue Mongo::Error::OperationFailure => e
    span.record_exception(e)
  end
end

#process_command_result(result, cursor_id, context, span) (private)

Processes the command result and updates span attributes.

Parameters:

  • result (Object)

    the command result.

  • cursor_id (Integer | nil)

    the cursor ID.

  • context (OpenTelemetry::Context)

    the context.

  • span (OpenTelemetry::Trace::Span)

    the current span.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/command_tracer.rb', line 99

def process_command_result(result, cursor_id, context, span)
  process_cursor_context(result, cursor_id, context, span)
  maybe_trace_error(result, span)
end

#process_cursor_context(result, _cursor_id, _context, span) (private)

Processes cursor context from the command result.

Parameters:

  • result (Object)

    the command result.

  • _cursor_id (Integer | nil)

    the cursor ID (unused).

  • _context (OpenTelemetry::Context)

    the context (unused).

  • span (OpenTelemetry::Trace::Span)

    the current span.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/command_tracer.rb', line 181

def process_cursor_context(result, _cursor_id, _context, span)
  return unless result.has_cursor_id? && result.cursor_id.positive?

  span.set_attribute('db.mongodb.cursor_id', result.cursor_id)
end

#query_summary(message) ⇒ String (private)

Generates a summary string for the query.

Parameters:

Returns:

  • (String)

    summary in format “command_name db.collection” or “command_name db”.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/command_tracer.rb', line 207

def query_summary(message)
  if (coll_name = collection_name(message))
    "#{command_name(message)} #{database(message)}.#{coll_name}"
  else
    "#{command_name(message)} #{database(message)}"
  end
end

#query_text(message) ⇒ String | nil (readonly, private)

Extracts and formats the query text from the command.

Parameters:

Returns:

  • (String | nil)

    JSON representation of the command, truncated if necessary, or nil if disabled.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/command_tracer.rb', line 304

def query_text(message)
  return unless query_text?

  text = message
         .payload['command']
         .reject { |key, _| EXCLUDED_KEYS.include?(key) }
         .to_json
  if text.length > @query_text_max_length
    "#{text[0...@query_text_max_length]}#{ELLIPSIS}"
  else
    text
  end
end

#session_attributes(message) ⇒ Hash (private)

Returns session and transaction attributes.

Parameters:

Returns:

  • (Hash)

    session span attributes.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/command_tracer.rb', line 167

def session_attributes(message)
  {
    'db.mongodb.cursor_id' => cursor_id(message),
    'db.mongodb.lsid' => lsid(message),
    'db.mongodb.txn_number' => txn_number(message)
  }
end

#span_attributes(message, connection) ⇒ Hash (private)

Builds span attributes for the command.

Parameters:

Returns:

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/command_tracer.rb', line 124

def span_attributes(message, connection)
  base_attributes(message)
    .merge(connection_attributes(connection))
    .merge(session_attributes(message))
    .compact
end

#start_span(message, operation_context, connection)

Starts a span for a MongoDB command.

Parameters:

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/command_tracer.rb', line 42

def start_span(message, operation_context, connection); end

#trace_command(message, _operation_context, connection) { ... } ⇒ Object

Trace a MongoDB command.

Creates an ::Mongo::Tracing::OpenTelemetry span for the command, capturing attributes such as command name, database name, collection name, server address, connection IDs, and optionally query text. The span is automatically nested under the current operation span and is finished when the command completes or fails.

Parameters:

Yields:

  • the block representing the command to be traced.

Returns:

  • (Object)

    the result of the command.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/command_tracer.rb', line 59

def trace_command(message, _operation_context, connection)
  # Commands should always be nested under their operation span, not directly under
  # the transaction span. Don't pass with_parent to use automatic parent resolution
  # from the currently active span (the operation span).
  span = create_command_span(message, connection)
  ::OpenTelemetry::Trace.with_span(span) do |s, c|
    yield.tap do |result|
      process_command_result(result, cursor_id(message), c, s)
    end
  end
rescue Exception => e
  handle_command_exception(span, e)
  raise e
ensure
  span&.finish
end

#txn_number(message) ⇒ Integer | nil (private)

Extracts the transaction number from the command.

Parameters:

Returns:

  • (Integer | nil)

    the transaction number, or nil if not present.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/command_tracer.rb', line 286

def txn_number(message)
  txn_num = message.documents.first['txnNumber']
  return unless txn_num

  txn_num.value
end