Skip to content
2 changes: 2 additions & 0 deletions sentry-ruby/lib/sentry-ruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ def capture_event(event)

# Takes or initializes a new Sentry::Transaction and makes a sampling decision for it.
def start_transaction(**options)
return unless configuration.tracing_enabled?

get_current_hub&.start_transaction(**options)
end

Expand Down
2 changes: 1 addition & 1 deletion sentry-ruby/lib/sentry/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ def enabled_in_current_env?
end

def tracing_enabled?
!!((@traces_sample_rate && @traces_sample_rate > 0.0) || @traces_sampler)
!!(@traces_sampler || (@traces_sample_rate.is_a?(Numeric) && @traces_sample_rate >= 0.0 && @traces_sample_rate <= 1.0))
end

def stacktrace_builder
Expand Down
2 changes: 2 additions & 0 deletions sentry-ruby/lib/sentry/hub.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ def pop_scope
end

def start_transaction(transaction: nil, configuration: Sentry.configuration, **options)
return unless configuration.tracing_enabled?

transaction ||= Transaction.new(**options)
transaction.set_initial_sample_decision(configuration: current_client.configuration)
transaction
Expand Down
19 changes: 13 additions & 6 deletions sentry-ruby/lib/sentry/rack/capture_exceptions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,23 @@ def call(env)
scope.set_rack_env(env)

transaction = start_transaction(env, scope)
scope.set_span(transaction)
transaction ? scope.set_span(transaction) : nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we don't need the return value from this line, it's not necessary to return the nil. this should do the work too

scope.set_span(transaction) if transaction

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So is that formulation more idiomatic for Ruby? As someone coming from Python and JS/TS, having the condition after the action made my brain hurt a little, which is why I went with the ternary in the end. But if the do x if y pattern is the accepted way, that's good to know. 🙂

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah this is a common practice in Ruby 😂 we're weirdos 😉


begin
response = @app.call(env)
rescue Sentry::Error
finish_transaction(transaction, 500)
transaction ? finish_transaction(transaction, 500) : nil
Comment thread
lobsterkatie marked this conversation as resolved.
raise # Don't capture Sentry errors
rescue Exception => e
capture_exception(e)
finish_transaction(transaction, 500)
transaction ? finish_transaction(transaction, 500) : nil
raise
end

exception = collect_exception(env)
capture_exception(exception) if exception

finish_transaction(transaction, response[0])
transaction ? finish_transaction(transaction, response[0]) : nil

response
end
Expand All @@ -54,13 +54,20 @@ def capture_exception(exception)
end

def start_transaction(env, scope)
return unless Sentry.configuration.tracing_enabled?

sentry_trace = env["HTTP_SENTRY_TRACE"]
transaction = Sentry::Transaction.from_sentry_trace(sentry_trace, name: scope.transaction_name, op: transaction_op) if sentry_trace
transaction || Sentry.start_transaction(name: scope.transaction_name, op: transaction_op)
options = {name: scope.transaction_name, op: transaction_op}

# if tracing is disabled, these will both return nil
transaction = Sentry::Transaction.from_sentry_trace(sentry_trace, **options) if sentry_trace

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense, will adopt it in my PR 👍

Sentry.start_transaction(transaction: transaction, **options)
end


def finish_transaction(transaction, status_code)
return unless transaction

transaction.set_http_status(status_code)
transaction.finish
end
Expand Down
43 changes: 27 additions & 16 deletions sentry-ruby/lib/sentry/transaction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,16 @@ def self.from_sentry_trace(sentry_trace, configuration: Sentry.configuration, **
return if match.nil?
trace_id, parent_span_id, sampled_flag = match[1..3]

sampled = sampled_flag != "0"
if sampled_flag == "1"
parent_sampled = true
elsif sampled_flag == "0"
parent_sampled = false
else
parent_sampled = nil
end

new(trace_id: trace_id, parent_span_id: parent_span_id, parent_sampled: sampled, sampled: sampled, **options)

new(trace_id: trace_id, parent_span_id: parent_span_id, parent_sampled: parent_sampled, **options)
end

def to_hash
Expand Down Expand Up @@ -69,26 +76,28 @@ def deep_dup
end

def set_initial_sample_decision(sampling_context: {}, configuration: Sentry.configuration)
logger = configuration.logger
transaction_description = generate_transaction_description

unless configuration.tracing_enabled?
@sampled = false
return
# we shouldn't ever get here (because if tracing is disabled, start_transaction should bail
# before it calls set_initial_sample_decision), so warn, because something's gone wrong
logger.warn("#{MESSAGE_PREFIX} Tracing is disabled. #{transaction_description} will not be sent.")
return nil
end

return unless @sampled.nil?

transaction_description = generate_transaction_description

logger = configuration.logger
sample_rate = configuration.traces_sample_rate
traces_sampler = configuration.traces_sampler

if traces_sampler.is_a?(Proc)
if configuration.traces_sampler.is_a?(Proc)
sampling_context = sampling_context.merge(
parent_sampled: @parent_sampled,
transaction_context: self.to_hash
)

sample_rate = traces_sampler.call(sampling_context)
sample_rate = configuration.traces_sampler.call(sampling_context)
elsif @parent_sampled
sample_rate = @parent_sampled
else
sample_rate = configuration.traces_sample_rate
end

unless [true, false].include?(sample_rate) || (sample_rate.is_a?(Numeric) && sample_rate >= 0.0 && sample_rate <= 1.0)
Expand All @@ -99,7 +108,7 @@ def set_initial_sample_decision(sampling_context: {}, configuration: Sentry.conf

if sample_rate == 0.0 || sample_rate == false
@sampled = false
logger.debug("#{MESSAGE_PREFIX} Discarding #{transaction_description} because traces_sampler returned 0 or false")
logger.debug("#{MESSAGE_PREFIX} Discarding #{transaction_description} because traces_sample_rate is 0 or traces_sampler returned 0 or false")
return
end

Expand All @@ -119,14 +128,16 @@ def set_initial_sample_decision(sampling_context: {}, configuration: Sentry.conf
end

def finish(hub: nil)
# it's unlikely we'd get this far if tracing is disabled, but just in case...
return unless Sentry.configuration.tracing_enabled?
return unless @sampled

super() # Span#finish doesn't take arguments

if @name.nil?
@name = UNLABELD_NAME
end

return unless @sampled || @parent_sampled

hub ||= Sentry.get_current_hub
event = hub.current_client.event_from_transaction(self)
hub.capture_event(event)
Expand Down
20 changes: 15 additions & 5 deletions sentry-ruby/spec/sentry/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,36 @@
end

describe "#tracing_enabled?" do
it "returns false by default" do
expect(subject.tracing_enabled?).to eq(false)
context "when neither traces_sample_rate nor traces_sampler is set" do
it "returns false" do
expect(subject.tracing_enabled?).to eq(false)
end
end

context "when traces_sample_rate == 0.0" do
it "returns false" do
it "returns true" do
subject.traces_sample_rate = 0.0

expect(subject.tracing_enabled?).to eq(false)
expect(subject.tracing_enabled?).to eq(true)
end
end

context "when traces_sample_rate > 0" do
context "when traces_sample_rate > 0 and <= 1" do
it "returns true" do
subject.traces_sample_rate = 0.1

expect(subject.tracing_enabled?).to eq(true)
end
end

context "when traces_sample_rate > 1" do
it "returns false" do
subject.traces_sample_rate = 1.1

expect(subject.tracing_enabled?).to eq(false)
end
end

context "when traces_sampler is set" do
it "returns true" do
subject.traces_sampler = proc { true }
Expand Down
7 changes: 4 additions & 3 deletions sentry-ruby/spec/sentry/rack/capture_exceptions_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@

stack.call(env)


transaction = transport.events.last
expect(transaction.type).to eq("transaction")
expect(transaction.timestamp).not_to be_nil
Expand Down Expand Up @@ -215,7 +216,7 @@
allow(Random).to receive(:rand).and_return(0.4)
end

it "starts a span and finishes it" do
it "starts a transaction and finishes it" do
app = ->(_) do
[200, {}, ["ok"]]
end
Expand Down Expand Up @@ -282,9 +283,9 @@
end
end

context "when traces_sample_rate is not set" do
context "when tracing is disabled" do
before do
Sentry.configuration.traces_sample_rate = nil
allow(Sentry.configuration).to receive(:tracing_enabled?).and_return(false)
end

let(:stack) do
Expand Down
Loading