Class: Sentry::Transaction

Inherits:
Span
  • Object
show all
Includes:
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.



56
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
# File 'lib/sentry/transaction.rb', line 56

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

#baggageBaggage? (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:



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

def baggage
  @baggage
end

#contextsHash (readonly)

Additional contexts stored directly on the transaction object.

Returns:

  • (Hash)


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

def contexts
  @contexts
end

#effective_sample_rateFloat? (readonly)

The effective sample rate at which this transaction was sampled.

Returns:

  • (Float, nil)


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

def effective_sample_rate
  @effective_sample_rate
end

#measurementsHash (readonly)

The measurements added to the transaction.

Returns:

  • (Hash)


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

def measurements
  @measurements
end

#nameString (readonly)

The name of the transaction.

Returns:

  • (String)


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

def name
  @name
end

#parent_sampledString (readonly)

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

Returns:

  • (String)


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

def parent_sampled
  @parent_sampled
end

#profilerProfiler (readonly)

The Profiler instance for this transaction.

Returns:



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

def profiler
  @profiler
end

#sample_randString (readonly)

Sample rand value generated from trace_id

Returns:

  • (String)


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

def sample_rand
  @sample_rand
end

#sourceSymbol (readonly)

The source of the transaction name.

Returns:

  • (Symbol)


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

def source
  @source
end

Instance Method Details

#deep_dupTransaction

Returns:



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

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:



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/sentry/transaction.rb', line 192

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_baggageBaggage

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

Returns:



226
227
228
229
# File 'lib/sentry/transaction.rb', line 226

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

#parent_sample_rateObject



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

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)


245
246
247
# File 'lib/sentry/transaction.rb', line 245

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).



130
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
# File 'lib/sentry/transaction.rb', line 130

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

#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



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

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)


236
237
238
239
# File 'lib/sentry/transaction.rb', line 236

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)


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

def source_low_quality?
  source == :url
end

#start_profiler!void

This method returns an undefined value.

Start the profiler.



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

def start_profiler!
  return unless profiler

  profiler.set_initial_sample_decision(sampled)
  profiler.start
end

#to_hHash

Returns:

  • (Hash)


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

def to_h
  hash = super

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

  hash
end