class Sentry::Transaction

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(end_timestamp: nil)

Returns:
  • (TransactionEvent) -
def finish(end_timestamp: nil)
  super(end_timestamp: end_timestamp)
  if @name.nil?
    @name = UNLABELD_NAME
  end
  hub = Sentry.get_current_hub
  return unless hub
  hub.stop_profiler!(self)
  if @sampled && ignore_status_code?
    @sampled = false
    status_code = get_http_status_code
    log_debug("#{MESSAGE_PREFIX} Discarding #{generate_transaction_description} due to ignored HTTP status code: #{status_code}")
    hub.current_client.transport.record_lost_event(:event_processor, "transaction")
    hub.current_client.transport.record_lost_event(:event_processor, "span")
  elsif @sampled
    event = hub.current_client.event_from_transaction(self)
    hub.capture_event(event)
  else
    is_backpressure = Sentry.backpressure_monitor&.downsample_factor&.positive?
    reason = is_backpressure ? :backpressure : :sample_rate
    hub.current_client.transport.record_lost_event(reason, "transaction")
    hub.current_client.transport.record_lost_event(reason, "span")
  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 get_http_status_code

def get_http_status_code
  @data && @data[Span::DataConventions::HTTP_STATUS_CODE]
end

def ignore_status_code?

def ignore_status_code?
  trace_ignore_status_codes = Sentry.configuration&.trace_ignore_status_codes
  return false unless trace_ignore_status_codes
  status_code = get_http_status_code
  return false unless status_code
  trace_ignore_status_codes.any? do |ignored|
    ignored.is_a?(Range) ? ignored.include?(status_code) : status_code == ignored
  end
end

def init_profiler

def init_profiler
  hub = Sentry.get_current_hub
  return unless hub
  unless hub.profiler_running?
    @profiler = hub.configuration.profiler_class.new(hub.configuration)
  end
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(
  name: nil,
  source: :custom,
  parent_sampled: nil,
  baggage: nil,
  sample_rand: nil,
  **options
)
  super(transaction: self, **options)
  set_name(name, source: source)
  @parent_sampled = parent_sampled
  @baggage = baggage
  @effective_sample_rate = nil
  @contexts = {}
  @measurements = {}
  @sample_rand = sample_rand
  init_span_recorder
  init_profiler
  unless @sample_rand
    generator = Utils::SampleRand.new(trace_id: @trace_id)
    @sample_rand = generator.generate_from_trace_id
  end
end

def parent_sample_rate

def parent_sample_rate
  return unless @baggage&.items
  sample_rate_str = @baggage.items["sample_rate"]
  sample_rate_str&.to_f
end

def populate_head_baggage

def populate_head_baggage
  configuration = Sentry.configuration
  items = {
    "trace_id" => trace_id,
    "sample_rate" => effective_sample_rate&.to_s,
    "sample_rand" => Utils::SampleRand.format(@sample_rand),
    "sampled" => sampled&.to_s,
    "environment" => configuration&.environment,
    "release" => configuration&.release,
    "public_key" => configuration&.dsn&.public_key,
    "org_id" => configuration&.effective_org_id
  }
  items["transaction"] = name unless source_low_quality?
  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:)
  configuration = Sentry.configuration
  unless configuration && configuration.tracing_enabled?
    @sampled = false
    return
  end
  unless @sampled.nil?
    @effective_sample_rate = @sampled ? 1.0 : 0.0
    return
  end
  sample_rate =
    if configuration.traces_sampler.is_a?(Proc)
      configuration.traces_sampler.call(sampling_context)
    elsif !sampling_context[:parent_sampled].nil?
      sampling_context[:parent_sampled]
    else
      configuration.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
    if Sentry.backpressure_monitor
      factor = Sentry.backpressure_monitor.downsample_factor
      @effective_sample_rate /= 2**factor
    end
    @sampled = @sample_rand < @effective_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!
  return unless profiler
  profiler.set_initial_sample_decision(sampled)
  profiler.start
end

def to_h

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