Skip to content

Commit

Permalink
Merge pull request #248 from jordan-brough/distribution-time
Browse files Browse the repository at this point in the history
Add a "distribution_time" method
  • Loading branch information
remeh committed Apr 11, 2022
2 parents 43844fe + ab46b8a commit 1870ba7
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 0 deletions.
20 changes: 20 additions & 0 deletions lib/datadog/statsd.rb
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,26 @@ def distribution(stat, value, opts = EMPTY_OPTIONS)
send_stats(stat, value, DISTRIBUTION_TYPE, opts)
end

# Reports execution time of the provided block as a distribution.
#
# If the block fails, the stat is still reported, then the error
# is reraised
#
# @param [String] stat stat name.
# @param [Numeric] value distribution value.
# @param [Hash] opts the options to create the metric with
# @option opts [Numeric] :sample_rate sample rate, 1 for always
# @option opts [Array<String>] :tags An array of tags
# @example Report the time (in ms) taken to activate an account
# $statsd.distribution_time('account.activate') { @account.activate! }
def distribution_time(stat, opts = EMPTY_OPTIONS)
opts = { sample_rate: opts } if opts.is_a?(Numeric)
start = now
yield
ensure
distribution(stat, ((now - start) * 1000).round, opts)
end

# Sends a timing (in ms) for the given stat to the statsd server. The
# sample_rate determines what percentage of the time this report is sent. The
# statsd server then uses the sample_rate to correctly track the average
Expand Down
128 changes: 128 additions & 0 deletions spec/statsd_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,134 @@
end
end

describe '#distribution_time' do
let(:namespace) { nil }
let(:sample_rate) { nil }
let(:tags) { nil }

let(:before_date) do
DateTime.new(2020, 2, 25, 12, 12, 12)
end

let(:after_date) do
DateTime.new(2020, 2, 25, 12, 12, 13)
end

before do
Timecop.freeze(before_date)
allow(Process).to receive(:clock_gettime).and_return(0)
end

it_behaves_like 'a metrics method', 'foobar:1000|d' do
let(:basic_action) do
subject.distribution_time('foobar', tags: action_tags) do
Timecop.travel(after_date)
allow(Process).to receive(:clock_gettime).and_return(1)
end

subject.flush(sync: true)
end
end

context 'when actually testing time' do
it 'sends the timing' do
subject.distribution_time('foobar') do
Timecop.travel(after_date)
allow(Process).to receive(:clock_gettime).and_return(1)
end

subject.flush(sync: true)

expect(socket.recv[0]).to eq_with_telemetry 'foobar:1000|d'
end

it 'ensures the timing is sent' do
# rubocop:disable Lint/RescueWithoutErrorClass
subject.distribution_time('foobar') do
Timecop.travel(after_date)
allow(Process).to receive(:clock_gettime).and_return(1)
raise 'stop'
end rescue nil
# rubocop:enable Lint/RescueWithoutErrorClass

subject.flush(sync: true)

expect(socket.recv[0]).to eq_with_telemetry 'foobar:1000|d'
end
end

it 'returns the result of the block' do
expect(subject.distribution_time('foobar') { 'test' }).to eq 'test'
end

it 'does not catch errors if block is failing' do
expect do
subject.distribution_time('foobar') do
raise 'yolo'
end
end.to raise_error(StandardError, 'yolo')
end

it 'can run without "PROCESS_TIME_SUPPORTED"' do
stub_const('PROCESS_TIME_SUPPORTED', false)

expect do
subject.distribution_time('foobar') {}
end.not_to raise_error
end

context 'with a sample rate' do
before do
allow(subject).to receive(:rand).and_return(0)
end

it 'sends the timing with the sample rate' do
subject.distribution_time('foobar', sample_rate: 0.5) do
Timecop.travel(after_date)
allow(Process).to receive(:clock_gettime).and_return(1)
end

subject.flush(sync: true)

expect(socket.recv[0]).to eq_with_telemetry 'foobar:1000|d|@0.5'
end
end

context 'with a sample rate like statsd-ruby' do
before do
allow(subject).to receive(:rand).and_return(0)
end

it 'sends the timing with the sample rate' do
subject.distribution_time('foobar', 0.5) do
Timecop.travel(after_date)
allow(Process).to receive(:clock_gettime).and_return(1)
end

subject.flush(sync: true)

expect(socket.recv[0]).to eq_with_telemetry 'foobar:1000|d|@0.5'
end
end

context 'with pre sampling' do
before do
allow(subject).to receive(:rand).and_return(1)
end

it 'sends the sample rate without additional sampling' do
subject.distribution_time('foobar', sample_rate: 0.5, pre_sampled: true) do
Timecop.travel(after_date)
allow(Process).to receive(:clock_gettime).and_return(1)
end

subject.flush(sync: true)

expect(socket.recv[0]).to eq_with_telemetry 'foobar:1000|d|@0.5'
end
end
end

describe '#distribution' do
let(:namespace) { nil }
let(:sample_rate) { nil }
Expand Down

0 comments on commit 1870ba7

Please sign in to comment.