Class: Sentry::Transaction

Inherits:
Span
  • Object
show all
Includes:
CallbackHelper, LoggingHelper
Defined in:
lib/sentry/transaction.rb

Defined Under Namespace

Classes: SpanRecorder

Constant Summary collapse

UNLABELD_NAME =
"<unlabeled transaction>"
MESSAGE_PREFIX =
"[Tracing]"
SOURCES =
%i[custom url route view component task]

Constants inherited from Span

Span::DEFAULT_SPAN_ORIGIN, Span::STATUS_MAP

Instance Attribute Summary collapse

Attributes inherited from Span

#data, #description, #op, #origin, #parent_span_id, #sampled, #span_id, #span_recorder, #start_timestamp, #status, #tags, #timestamp, #trace_id, #transaction

Instance Method Summary collapse

Methods inherited from Span

#get_dynamic_sampling_context, #get_trace_context, #set_data, #set_description, #set_http_status, #set_op, #set_origin, #set_status, #set_tag, #set_timestamp, #start_child, #to_baggage, #to_sentry_trace, #with_child_span

Constructor Details

#initialize(name: nil, source: :custom, parent_sampled: nil, baggage: nil, sample_rand: nil, **options) ⇒ Transaction

Returns a new instance of Transaction.



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/sentry/transaction.rb', line 57

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

Instance Attribute Details

#baggage ⇒ Baggage? (readonly)

The parsed incoming W3C baggage header. This is only for accessing the current baggage variable. Please use the #get_baggage method for interfacing outside this class.

Returns:



35
36
37
# File 'lib/sentry/transaction.rb', line 35

def baggage
  @baggage
end

#contexts ⇒ Hash (readonly)

Additional contexts stored directly on the transaction object.

Returns:

  • (Hash)


47
48
49
# File 'lib/sentry/transaction.rb', line 47

def contexts
  @contexts
end

#effective_sample_rate ⇒ Float? (readonly)

The effective sample rate at which this transaction was sampled.

Returns:

  • (Float, nil)


43
44
45
# File 'lib/sentry/transaction.rb', line 43

def effective_sample_rate
  @effective_sample_rate
end

#measurements ⇒ Hash (readonly)

The measurements added to the transaction.

Returns:

  • (Hash)


39
40
41
# File 'lib/sentry/transaction.rb', line 39

def measurements
  @measurements
end

#name ⇒ String (readonly)

The name of the transaction.

Returns:

  • (String)


21
22
23
# File 'lib/sentry/transaction.rb', line 21

def name
  @name
end

#parent_sampled ⇒ String (readonly)

The sampling decision of the parent transaction, which will be considered when making the current transaction's sampling decision.

Returns:

  • (String)


29
30
31
# File 'lib/sentry/transaction.rb', line 29

def parent_sampled
  @parent_sampled
end

#profiler ⇒ Profiler (readonly)

The Profiler instance for this transaction.

Returns:



51
52
53
# File 'lib/sentry/transaction.rb', line 51

def profiler
  @profiler
end

#sample_rand ⇒ String (readonly)

Sample rand value generated from trace_id

Returns:

  • (String)


55
56
57
# File 'lib/sentry/transaction.rb', line 55

def sample_rand
  @sample_rand
end

#source ⇒ Symbol (readonly)

The source of the transaction name.

Returns:

  • (Symbol)


25
26
27
# File 'lib/sentry/transaction.rb', line 25

def source
  @source
end

Instance Method Details

#deep_dup ⇒ Transaction

Returns:



106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/sentry/transaction.rb', line 106

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

#finish(end_timestamp: nil) ⇒ TransactionEvent

Finishes the transaction's recording and send it to Sentry.

Returns:



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/sentry/transaction.rb', line 198

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

#get_baggage ⇒ Baggage

Get the existing frozen incoming baggage or populate one with sentry- items as the head SDK.

Returns:



232
233
234
235
# File 'lib/sentry/transaction.rb', line 232

def get_baggage
  populate_head_baggage if @baggage.nil? || @baggage.mutable
  @baggage
end

#parent_sample_rate ⇒ Object



98
99
100
101
102
103
# File 'lib/sentry/transaction.rb', line 98

def parent_sample_rate
  return unless @baggage&.items

  sample_rate_str = @baggage.items["sample_rate"]
  sample_rate_str&.to_f
end

#set_context(key, value) ⇒ void

This method returns an undefined value.

Set contexts directly on the transaction.

Parameters:

  • key (String, Symbol)
  • value (Object)


251
252
253
# File 'lib/sentry/transaction.rb', line 251

def set_context(key, value)
  @contexts[key] = value
end

#set_initial_sample_decision(sampling_context:) ⇒ void

This method returns an undefined value.

Sets initial sampling decision of the transaction.

Parameters:

  • sampling_context (Hash) —

    a context Hash that'll be passed to traces_sampler (if provided).



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/sentry/transaction.rb', line 131

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)
      safe_dispatch_callback(
        "traces_sampler",
        configuration.traces_sampler,
        [sampling_context],
        fallback: configuration.traces_sample_rate
      )
    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

#set_measurement(name, value, unit = "") ⇒ void

This method returns an undefined value.

Sets a custom measurement on the transaction.

Parameters:

  • name (String) —

    name of the measurement

  • value (Float) —

    value of the measurement

  • unit (String) (defaults to: "") —

    unit of the measurement



124
125
126
# File 'lib/sentry/transaction.rb', line 124

def set_measurement(name, value, unit = "")
  @measurements[name] = { value: value, unit: unit }
end

#set_name(name, source: :custom) ⇒ void

This method returns an undefined value.

Set the transaction name directly. Considered internal api since it bypasses the usual scope logic.

Parameters:

  • name (String)
  • source (Symbol) (defaults to: :custom)


242
243
244
245
# File 'lib/sentry/transaction.rb', line 242

def set_name(name, source: :custom)
  @name = name
  @source = SOURCES.include?(source) ? source.to_sym : :custom
end

#source_low_quality? ⇒ Boolean

These are high cardinality and thus bad

Returns:

  • (Boolean)


265
266
267
# File 'lib/sentry/transaction.rb', line 265

def source_low_quality?
  source == :url
end

#start_profiler! ⇒ void

This method returns an undefined value.

Start the profiler.



257
258
259
260
261
262
# File 'lib/sentry/transaction.rb', line 257

def start_profiler!
  return unless profiler

  profiler.set_initial_sample_decision(sampled)
  profiler.start
end

#to_h ⇒ Hash

Returns:

  • (Hash)


85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/sentry/transaction.rb', line 85

def to_h
  hash = super

  hash.merge!(
    name: @name,
    source: @source,
    sampled: @sampled,
    parent_sampled: @parent_sampled
  )

  hash
end