123456789_123456789_123456789_123456789_123456789_

Class: Mongo::Tracing::OpenTelemetry::OperationTracer Private

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, Forwardable
Inherits: Object
Defined in: lib/mongo/tracing/open_telemetry/operation_tracer.rb

Overview

OperationTracer is responsible for tracing MongoDB driver operations using ::Mongo::Tracing::OpenTelemetry.

Class Method Summary

Instance Method Summary

Constructor Details

.new(otel_tracer, parent_tracer) ⇒ OperationTracer

Initializes a new OperationTracer.

Parameters:

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/operation_tracer.rb', line 37

def initialize(otel_tracer, parent_tracer)
  @otel_tracer = otel_tracer
  @parent_tracer = parent_tracer
end

Instance Method Details

#collection_key_for_operation(operation) ⇒ Symbol | nil (private)

Returns the collection key for a given operation type.

Parameters:

Returns:

  • (Symbol | nil) —

    the collection key symbol or nil.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/operation_tracer.rb', line 224

def collection_key_for_operation(operation)
  case operation
  when Operation::Aggregate then :aggregate
  when Operation::Count then :count
  when Operation::Create then :create
  when Operation::Distinct then :distinct
  when Operation::Drop then :drop
  when Operation::WriteCommand then :first_value
  end
end

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

Extracts the collection name from the operation.

Parameters:

Returns:

  • (String | nil) —

    the collection name, or nil if not applicable.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/operation_tracer.rb', line 196

def collection_name(operation)
  return operation.coll_name.to_s if operation.respond_to?(:coll_name) && operation.coll_name

  extract_collection_from_spec(operation)
end

#create_operation_span(operation, operation_context, op_name) ⇒ OpenTelemetry::Trace::Span (private)

Creates an ::Mongo::Tracing::OpenTelemetry span for the operation.

Parameters:

Returns:

  • (OpenTelemetry::Trace::Span) —

    the created span.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/operation_tracer.rb', line 100

def create_operation_span(operation, operation_context, op_name)
  parent_context = parent_context_for(operation_context, operation.cursor_id)
  @otel_tracer.start_span(
    operation_span_name(operation, op_name),
    attributes: span_attributes(operation, op_name),
    with_parent: parent_context,
    kind: :client
  )
end

#execute_with_span(span, operation, recording) { ... } ⇒ Object (private)

Executes the operation block within the span context.

Parameters:

  • span (OpenTelemetry::Trace::Span) —

    the span.

  • operation (Mongo::Operation) —

    the operation.

  • recording (true | false) —

    whether the span is recording.

Yields:

  • the block to execute.

Returns:

  • (Object) —

    the result of the block.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/operation_tracer.rb', line 119

def execute_with_span(span, operation, recording)
  ::OpenTelemetry::Trace.with_span(span) do |s, c|
    yield.tap do |result|
      process_cursor_context(result, operation.cursor_id, c, s, recording)
    end
  end
end

#extract_collection_from_spec(operation) ⇒ String | nil (private)

Extracts collection name from operation spec based on operation type.

Parameters:

Returns:

  • (String | nil) —

    the collection name, or nil if not found.

[ GitHub ]

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

def extract_collection_from_spec(operation)
  collection_key = collection_key_for_operation(operation)
  return nil unless collection_key

  value = if collection_key == :first_value
            operation.spec[:selector].values.first
          else
            operation.spec[:selector][collection_key]
          end
  value&.to_s
end

#handle_span_exception(span, exception) (private)

Handles exception for the span.

Parameters:

  • span (OpenTelemetry::Trace::Span) —

    the span.

  • exception (Exception) —

    the exception.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/operation_tracer.rb', line 131

def handle_span_exception(span, exception)
  span.record_exception(exception)
  span.status = ::OpenTelemetry::Trace::Status.error(
    "Unhandled exception of type: #{exception.class}"
  )
end

#operation_name(operation, op_name = nil) ⇒ String (private)

Returns the operation name from the provided name or operation class.

Parameters:

  • operation (Mongo::Operation) —

    the operation.

  • op_name (String | nil) (defaults to: nil) —

    optional operation name.

Returns:

  • (String) —

    the operation name in lowercase.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/operation_tracer.rb', line 144

def operation_name(operation, op_name = nil)
  op_name || operation.class.name.split('::').last.downcase
end

#operation_span_name(operation, op_name = nil) ⇒ String (private)

Generates the span name for the operation.

Parameters:

  • operation (Mongo::Operation) —

    the operation.

  • op_name (String | nil) (defaults to: nil) —

    optional operation name.

Returns:

  • (String) —

    span name in format "operation_name db.collection" or "operation_name db".

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/operation_tracer.rb', line 241

def operation_span_name(operation, op_name = nil)
  coll_name = collection_name(operation)
  if coll_name && !coll_name.empty?
    "#{operation_name(operation, op_name)} #{operation.db_name}.#{coll_name}"
  else
    "#{operation_name(operation, op_name)} #{operation.db_name}"
  end
end

#process_cursor_context(result, cursor_id, context, span, recording) (private)

Processes cursor context after operation execution.

Updates the cursor context map based on the result. Removes closed cursors and stores context for newly created cursors.

Parameters:

  • result (Object) —

    the operation result.

  • cursor_id (Integer | nil) —

    the cursor ID before the operation.

  • context (OpenTelemetry::Context) —
  • span (OpenTelemetry::Trace::Span) —

    the current span.

  • recording (true | false) —

    whether the span is recording.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/operation_tracer.rb', line 178

def process_cursor_context(result, cursor_id, context, span, recording)
  return unless result.is_a?(Cursor)

  if result.id.zero?
    # If the cursor is closed, remove it from the context map.
    cursor_context_map.delete(cursor_id)
  elsif result.id && cursor_id.nil?
    # New cursor created, store its context.
    cursor_context_map[result.id] = context
    span.set_attribute('db.mongodb.cursor_id', result.id) if recording
  end
end

#span_attributes(operation, op_name) ⇒ Hash (private)

Builds the attributes passed at span creation. The cursor id is set by trace_with_span once the span is known to record. db.operation.summary is outside the spec's recommended creation set, but it is the span name, which is built anyway, and an attribute passed at creation is cheaper than one set later.

Parameters:

  • operation (Mongo::Operation) —

    the operation.

  • op_name (String | nil) —

    optional operation name.

Returns:

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/operation_tracer.rb', line 158

def span_attributes(operation, op_name)
  {
    'db.system.name' => 'mongodb',
    'db.namespace' => operation.db_name.to_s,
    'db.collection.name' => collection_name(operation),
    'db.operation.name' => operation_name(operation, op_name),
    'db.operation.summary' => operation_span_name(operation, op_name)
  }.compact
end

#trace_operation(operation, operation_context, op_name: nil) { ... } ⇒ Object

Trace a MongoDB operation.

Creates an ::Mongo::Tracing::OpenTelemetry span for the operation, capturing attributes such as database name, collection name, operation name, and cursor ID. The span is finished automatically when the operation completes or fails.

Parameters:

  • operation (Mongo::Operation) —

    the MongoDB operation to trace.

  • operation_context (Mongo::Operation::Context) —

    the context of the operation.

  • op_name (String | nil) —

    an optional name for the operation. If nil, the operation class name is used.

Yields:

  • the block representing the operation to be traced.

Returns:

  • (Object) —

    the result of the operation.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/operation_tracer.rb', line 56

def trace_operation(operation, operation_context, op_name: nil, &block)
  span = create_operation_span(operation, operation_context, op_name)
  # With no SDK and no valid parent, the API returns a span with an
  # invalid context. It cannot be made current, recorded or
  # propagated, so it gets no work at all, not even finishing it.
  return yield unless span.context.valid?

  trace_with_span(span, operation, &block)
end

#trace_with_span(span, operation, &block) ⇒ Object (private)

Runs the operation with the span made current, and finishes the span. A span that is not recording is still made current, so that the command spans created within it nest under it, but gets no further attributes and no outcome: the SDK would discard them.

Parameters:

  • span (OpenTelemetry::Trace::Span) —

    a span with a valid context.

  • operation (Mongo::Operation) —

    the operation.

Returns:

  • (Object) —

    the result of the operation.

[ GitHub ]

  
# File 'lib/mongo/tracing/open_telemetry/operation_tracer.rb', line 79

def trace_with_span(span, operation, &block)
  recording = span.recording?
  if recording && !operation.cursor_id.nil?
    span.set_attribute('db.mongodb.cursor_id', operation.cursor_id)
  end
  execute_with_span(span, operation, recording, &block)
rescue Exception => e
  handle_span_exception(span, e) if recording
  raise e
ensure
  span.finish
end