class Sentry::Transaction

def self.extract_sentry_trace(sentry_trace)

Returns:
  • (Array, nil) -

Parameters:
  • sentry_trace (String) -- the sentry-trace header value from the previous transaction.
def self.extract_sentry_trace(sentry_trace)
  match = SENTRY_TRACE_REGEXP.match(sentry_trace)
  return nil if match.nil?
  trace_id, parent_span_id, sampled_flag = match[1..3]
  parent_sampled = sampled_flag.nil? ? nil : sampled_flag != "0"
  [trace_id, parent_span_id, parent_sampled]
end

def self.from_sentry_trace(sentry_trace, baggage: nil, hub: Sentry.get_current_hub, **options)

Returns:
  • (Transaction, nil) -

Parameters:
  • options (Hash) -- the options you want to use to initialize a Transaction instance.
  • hub (Hub) -- the hub that'll be responsible for sending this transaction when it's finished.
  • baggage (String, nil) -- the incoming baggage header string.
  • sentry_trace (String) -- the trace string from the previous transaction.
def self.from_sentry_trace(sentry_trace, baggage: nil, hub: Sentry.get_current_hub, **options)
  return unless hub.configuration.tracing_enabled?
  return unless sentry_trace
  sentry_trace_data = extract_sentry_trace(sentry_trace)
  return unless sentry_trace_data
  trace_id, parent_span_id, parent_sampled = sentry_trace_data
  baggage = if baggage && !baggage.empty?
              Baggage.from_incoming_header(baggage)
            else
              # If there's an incoming sentry-trace but no incoming baggage header,
              # for instance in traces coming from older SDKs,
              # baggage will be empty and frozen and won't be populated as head SDK.
              Baggage.new({})
            end
  baggage.freeze!
  new(
    trace_id: trace_id,
    parent_span_id: parent_span_id,
    parent_sampled: parent_sampled,
    hub: hub,
    baggage: baggage,
    **options
  )
end

def deep_dup

Returns:
  • (Transaction) -
def deep_dup
  copy = super
  copy.init_span_recorder(@span_recorder.max_length)
  @span_recorder.spans.each do |span|
    # span_recorder's first span is the current span, which should not be added to the copy's spans
    next if span == self
    copy.span_recorder.add(span.dup)
  end
  copy
end

def finish(hub: nil, end_timestamp: nil)

Returns:
  • (TransactionEvent) -

Parameters:
  • hub (Hub) -- the hub that'll send this transaction. (Deprecated)
def finish(hub: nil, end_timestamp: nil)
  if hub
    log_warn(
      <<~MSG
        Specifying a different hub in `Transaction#finish` will be deprecated in version 5.0.
        Please use `Hub#start_transaction` with the designated hub.
      MSG
    )
  end
  hub ||= @hub
  super(end_timestamp: end_timestamp)
  if @name.nil?
    @name = UNLABELD_NAME
  end
  @profiler.stop
  if @sampled
    event = hub.current_client.event_from_transaction(self)
    hub.capture_event(event)
  else
    hub.current_client.transport.record_lost_event(:sample_rate, 'transaction')
  end
end

def generate_transaction_description

def generate_transaction_description
  result = op.nil? ? "" : "<#{@op}> "
  result += "transaction"
  result += " <#{@name}>" if @name
  result
end

def get_baggage

Returns:
  • (Baggage) -
def get_baggage
  populate_head_baggage if @baggage.nil? || @baggage.mutable
  @baggage
end

def init_span_recorder(limit = 1000)

def init_span_recorder(limit = 1000)
  @span_recorder = SpanRecorder.new(limit)
  @span_recorder.add(self)
end

def initialize(

def initialize(
  hub:,
  name: nil,
  source: :custom,
  parent_sampled: nil,
  baggage: nil,
  **options
)
  super(transaction: self, **options)
  set_name(name, source: source)
  @parent_sampled = parent_sampled
  @hub = hub
  @baggage = baggage
  @configuration = hub.configuration # to be removed
  @tracing_enabled = hub.configuration.tracing_enabled?
  @traces_sampler = hub.configuration.traces_sampler
  @traces_sample_rate = hub.configuration.traces_sample_rate
  @logger = hub.configuration.logger
  @release = hub.configuration.release
  @environment = hub.configuration.environment
  @dsn = hub.configuration.dsn
  @effective_sample_rate = nil
  @contexts = {}
  @measurements = {}
  @profiler = Profiler.new(@configuration)
  init_span_recorder
end

def populate_head_baggage

def populate_head_baggage
  items = {
    "trace_id" => trace_id,
    "sample_rate" => effective_sample_rate&.to_s,
    "environment" => @environment,
    "release" => @release,
    "public_key" => @dsn&.public_key
  }
  items["transaction"] = name unless source_low_quality?
  user = @hub.current_scope&.user
  items["user_segment"] = user["segment"] if user && user["segment"]
  items.compact!
  @baggage = Baggage.new(items, mutable: false)
end

def set_context(key, value)

Returns:
  • (void) -

Parameters:
  • value (Object) --
  • key (String, Symbol) --
def set_context(key, value)
  @contexts[key] = value
end

def set_initial_sample_decision(sampling_context:)

Returns:
  • (void) -

Parameters:
  • sampling_context (Hash) -- a context Hash that'll be passed to `traces_sampler` (if provided).
def set_initial_sample_decision(sampling_context:)
  unless @tracing_enabled
    @sampled = false
    return
  end
  unless @sampled.nil?
    @effective_sample_rate = @sampled ? 1.0 : 0.0
    return
  end
  sample_rate =
    if @traces_sampler.is_a?(Proc)
      @traces_sampler.call(sampling_context)
    elsif !sampling_context[:parent_sampled].nil?
      sampling_context[:parent_sampled]
    else
      @traces_sample_rate
    end
  transaction_description = generate_transaction_description
  if [true, false].include?(sample_rate)
    @effective_sample_rate = sample_rate ? 1.0 : 0.0
  elsif sample_rate.is_a?(Numeric) && sample_rate >= 0.0 && sample_rate <= 1.0
    @effective_sample_rate = sample_rate.to_f
  else
    @sampled = false
    log_warn("#{MESSAGE_PREFIX} Discarding #{transaction_description} because of invalid sample_rate: #{sample_rate}")
    return
  end
  if sample_rate == 0.0 || sample_rate == false
    @sampled = false
    log_debug("#{MESSAGE_PREFIX} Discarding #{transaction_description} because traces_sampler returned 0 or false")
    return
  end
  if sample_rate == true
    @sampled = true
  else
    @sampled = Random.rand < sample_rate
  end
  if @sampled
    log_debug("#{MESSAGE_PREFIX} Starting #{transaction_description}")
  else
    log_debug(
      "#{MESSAGE_PREFIX} Discarding #{transaction_description} because it's not included in the random sample (sampling rate = #{sample_rate})"
    )
  end
end

def set_measurement(name, value, unit = "")

Returns:
  • (void) -

Parameters:
  • unit (String) -- unit of the measurement
  • value (Float) -- value of the measurement
  • name (String) -- name of the measurement
def set_measurement(name, value, unit = "")
  @measurements[name] = { value: value, unit: unit }
end

def set_name(name, source: :custom)

Returns:
  • (void) -

Parameters:
  • source (Symbol) --
  • name (String) --
def set_name(name, source: :custom)
  @name = name
  @source = SOURCES.include?(source) ? source.to_sym : :custom
end

def source_low_quality?

These are high cardinality and thus bad
def source_low_quality?
  source == :url
end

def start_profiler!

Returns:
  • (void) -
def start_profiler!
  profiler.set_initial_sample_decision(sampled)
  profiler.start
end

def to_hash

Returns:
  • (Hash) -
def to_hash
  hash = super
  hash.merge!(
    name: @name,
    source: @source,
    sampled: @sampled,
    parent_sampled: @parent_sampled
  )
  hash
end