diff --git a/CHANGELOG.md b/CHANGELOG.md index 38f5c9b23..0a1c93e6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,15 @@ end ``` +- Support `Sentry::Transaction#set_measurement` [#1838](https://github.com/getsentry/sentry-ruby/pull/1838) + + Usage: + + ```rb + transaction = Sentry.get_current_scope.get_transaction + transaction.set_measurement("metrics.foo", 0.5, "millisecond") + ``` + ### Bug Fixes - Support redis-rb 5.0+ [#1963](https://github.com/getsentry/sentry-ruby/pull/1963) diff --git a/sentry-ruby/lib/sentry/transaction.rb b/sentry-ruby/lib/sentry/transaction.rb index 55a4b9e81..52efa6fe6 100644 --- a/sentry-ruby/lib/sentry/transaction.rb +++ b/sentry-ruby/lib/sentry/transaction.rb @@ -37,6 +37,10 @@ class Transaction < Span # @return [Baggage, nil] attr_reader :baggage + # The measurements added to the transaction. + # @return [Hash] + attr_reader :measurements + # @deprecated Use Sentry.get_current_hub instead. attr_reader :hub @@ -78,6 +82,7 @@ def initialize( @dsn = hub.configuration.dsn @effective_sample_rate = nil @contexts = {} + @measurements = {} init_span_recorder end @@ -163,6 +168,15 @@ def deep_dup copy end + # Sets a custom measurement on the transaction. + # @param name [String] name of the measurement + # @param value [Float] value of the measurement + # @param unit [String] unit of the measurement + # @return [void] + def set_measurement(name, value, unit = "") + @measurements[name] = { value: value, unit: unit } + end + # Sets initial sampling decision of the transaction. # @param sampling_context [Hash] a context Hash that'll be passed to `traces_sampler` (if provided). # @return [void] diff --git a/sentry-ruby/lib/sentry/transaction_event.rb b/sentry-ruby/lib/sentry/transaction_event.rb index 389ca45b0..4eaaa0282 100644 --- a/sentry-ruby/lib/sentry/transaction_event.rb +++ b/sentry-ruby/lib/sentry/transaction_event.rb @@ -11,6 +11,9 @@ class TransactionEvent < Event # @return [Hash, nil] attr_accessor :dynamic_sampling_context + # @return [Hash] + attr_accessor :measurements + # @return [Float, nil] attr_reader :start_timestamp @@ -25,6 +28,7 @@ def initialize(transaction:, **options) self.start_timestamp = transaction.start_timestamp self.tags = transaction.tags self.dynamic_sampling_context = transaction.get_baggage.dynamic_sampling_context + self.measurements = transaction.measurements finished_spans = transaction.span_recorder.spans.select { |span| span.timestamp && span != transaction } self.spans = finished_spans.map(&:to_hash) @@ -42,6 +46,7 @@ def to_hash data = super data[:spans] = @spans.map(&:to_hash) if @spans data[:start_timestamp] = @start_timestamp + data[:measurements] = @measurements data end end diff --git a/sentry-ruby/spec/sentry/transaction_spec.rb b/sentry-ruby/spec/sentry/transaction_spec.rb index 665095aa1..27c03ba22 100644 --- a/sentry-ruby/spec/sentry/transaction_spec.rb +++ b/sentry-ruby/spec/sentry/transaction_spec.rb @@ -151,6 +151,32 @@ end end + describe "#set_measurement" do + it "sets the measurement" do + subject.set_measurement("metric.foo", 0.1, "second") + subject.set_measurement("metric.bar", 1.0, "minute") + subject.set_measurement("metric.baz", 1.0) + + expect(subject.measurements).to eq( + { + "metric.foo" => { value: 0.1, unit: "second" }, + "metric.bar" => { value: 1.0, unit: "minute" }, + "metric.baz" => { value: 1.0, unit: "" }, + } + ) + + subject.set_measurement("metric.foo", 2, "second") + + expect(subject.measurements).to eq( + { + "metric.foo" => { value: 2, unit: "second" }, + "metric.bar" => { value: 1.0, unit: "minute" }, + "metric.baz" => { value: 1.0, unit: "" }, + } + ) + end + end + describe "#start_child" do it "initializes a new child Span and assigns the 'transaction' attribute with itself" do # create subject span and wait for a sec for making time difference @@ -463,6 +489,18 @@ expect(subject.name).to eq("") end end + + describe "#set_measurement" do + it "adds measurements the event" do + subject.set_measurement("metric.foo", 0.5, "second") + subject.finish + + transaction = events.last.to_hash + expect(transaction[:measurements]).to eq( + { "metric.foo" => { value: 0.5, unit: "second" } } + ) + end + end end describe "#get_baggage" do