Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add metrics code locations #2263

Merged
merged 24 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
be9422d
metrics wip
sl0thentr0py Feb 16, 2024
ee59473
aggregator add impl
sl0thentr0py Feb 21, 2024
675292b
config and apis
sl0thentr0py Feb 21, 2024
73e5e04
encode statsd format and sanitization
sl0thentr0py Feb 22, 2024
9569bf9
capture envelope
sl0thentr0py Feb 22, 2024
8efde86
fix set
sl0thentr0py Feb 26, 2024
512408c
add transaction name to tags
sl0thentr0py Feb 26, 2024
bdac066
Specs
sl0thentr0py Feb 27, 2024
3f51c15
changelog
sl0thentr0py Feb 27, 2024
c1217cd
metric specs
sl0thentr0py Feb 27, 2024
8f9dabb
incr -> increment
sl0thentr0py Feb 29, 2024
ff3814f
Move config to separate metrics obj
sl0thentr0py Mar 5, 2024
f7163ef
Use scope name/source, not transaction
sl0thentr0py Mar 5, 2024
ff9792f
Remove is_json for envelope and use string check
sl0thentr0py Mar 11, 2024
9ba1a4e
trigger ci
sl0thentr0py Mar 11, 2024
478a78b
trigger ci
sl0thentr0py Mar 11, 2024
0e87bab
remove io-console pin
sl0thentr0py Mar 11, 2024
e6bbb90
Add Sentry::Metrics.timing API to measure blocks
sl0thentr0py Mar 4, 2024
7567daa
Merge remote-tracking branch 'origin/master' into neel/metrics/timing
sl0thentr0py Mar 12, 2024
116318b
Metric summaries on span
sl0thentr0py Mar 5, 2024
0df891d
Merge remote-tracking branch 'origin/master' into neel/metrics/span-a…
sl0thentr0py Mar 12, 2024
da14eaf
Add config.metrics.before_emit callback
sl0thentr0py Mar 8, 2024
088ccad
code locations
sl0thentr0py Mar 8, 2024
4107fd4
Merge remote-tracking branch 'origin/master' into neel/metrics/code-l…
sl0thentr0py Mar 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add Sentry::Metrics.timing API to measure blocks
  • Loading branch information
sl0thentr0py committed Mar 12, 2024
commit e6bbb90f93967b68702e493ecb8b6dd4881a9307
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Add `hint:` support to `Sentry::Rails::ErrorSubscriber` [#2235](https://github.com/getsentry/sentry-ruby/pull/2235)
- Add [Metrics](https://docs.sentry.io/product/metrics/) support
- Add main APIs and `Aggregator` thread [#2247](https://github.com/getsentry/sentry-ruby/pull/2247)
- Add `Sentry::Metrics.timing` API for measuring block duration [#2254](https://github.com/getsentry/sentry-ruby/pull/2254)

The SDK now supports recording and aggregating metrics. A new thread will be started
for aggregation and will flush the pending data to Sentry every 5 seconds.
Expand Down Expand Up @@ -36,6 +37,11 @@

# set - get unique counts of elements
Sentry::Metrics.set('user_view', 'jane')

# timing - measure duration of code block, defaults to seconds
Sentry::Metrics.timing('how_long') { sleep(1) }
# timing - measure duration of code block in other duraton units
Sentry::Metrics.timing('how_long_ms', unit: 'millisecond') { sleep(0.5) }
```

### Bug Fixes
Expand Down
17 changes: 17 additions & 0 deletions sentry-ruby/lib/sentry/metrics.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@
require 'sentry/metrics/distribution_metric'
require 'sentry/metrics/gauge_metric'
require 'sentry/metrics/set_metric'
require 'sentry/metrics/timing'
require 'sentry/metrics/aggregator'

module Sentry
module Metrics
DURATION_UNITS = %w[nanosecond microsecond millisecond second minute hour day week]
INFORMATION_UNITS = %w[bit byte kilobyte kibibyte megabyte mebibyte gigabyte gibibyte terabyte tebibyte petabyte pebibyte exabyte exbibyte]
FRACTIONAL_UNITS = %w[ratio percent]

class << self
def increment(key, value = 1.0, unit: 'none', tags: {}, timestamp: nil)
Sentry.metrics_aggregator&.add(:c, key, value, unit: unit, tags: tags, timestamp: timestamp)
Expand All @@ -25,6 +30,18 @@ def set(key, value, unit: 'none', tags: {}, timestamp: nil)
def gauge(key, value, unit: 'none', tags: {}, timestamp: nil)
Sentry.metrics_aggregator&.add(:g, key, value, unit: unit, tags: tags, timestamp: timestamp)
end

def timing(key, unit: 'second', tags: {}, timestamp: nil, &block)
return unless Sentry.metrics_aggregator
return unless block_given?
return unless DURATION_UNITS.include?(unit)

start = Timing.send(unit.to_sym)
yield
value = Timing.send(unit.to_sym) - start

Sentry.metrics_aggregator.add(:d, key, value, unit: unit, tags: tags, timestamp: timestamp)
end
end
end
end
43 changes: 43 additions & 0 deletions sentry-ruby/lib/sentry/metrics/timing.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# frozen_string_literal: true

module Sentry
module Metrics
module Timing
class << self
def nanosecond
time = Sentry.utc_now
time.to_i * (10 ** 9) + time.nsec
end

def microsecond
time = Sentry.utc_now
time.to_i * (10 ** 6) + time.usec
end

def millisecond
Sentry.utc_now.to_i * (10 ** 3)
end

def second
Sentry.utc_now.to_i
end

def minute
Sentry.utc_now.to_i / 60.0
end

def hour
Sentry.utc_now.to_i / 3600.0
end

def day
Sentry.utc_now.to_i / (3600.0 * 24.0)
end

def week
Sentry.utc_now.to_i / (3600.0 * 24.0 * 7.0)
end
end
end
end
end
54 changes: 54 additions & 0 deletions sentry-ruby/spec/sentry/metrics/timing_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
require 'spec_helper'

RSpec.describe Sentry::Metrics::Timing do
let(:fake_time) { Time.new(2024, 1, 2, 3, 4, 5) }
before { allow(Time).to receive(:now).and_return(fake_time) }

describe '.nanosecond' do
it 'returns nanoseconds' do
expect(described_class.nanosecond).to eq(fake_time.to_i * 10 ** 9)
end
end

describe '.microsecond' do
it 'returns microseconds' do
expect(described_class.microsecond).to eq(fake_time.to_i * 10 ** 6)
end
end

describe '.millisecond' do
it 'returns milliseconds' do
expect(described_class.millisecond).to eq(fake_time.to_i * 10 ** 3)
end
end

describe '.second' do
it 'returns seconds' do
expect(described_class.second).to eq(fake_time.to_i)
end
end

describe '.minute' do
it 'returns minutes' do
expect(described_class.minute).to eq(fake_time.to_i / 60.0)
end
end

describe '.hour' do
it 'returns hours' do
expect(described_class.hour).to eq(fake_time.to_i / 3600.0)
end
end

describe '.day' do
it 'returns days' do
expect(described_class.day).to eq(fake_time.to_i / (3600.0 * 24.0))
end
end

describe '.week' do
it 'returns weeks' do
expect(described_class.week).to eq(fake_time.to_i / (3600.0 * 24.0 * 7.0))
end
end
end
25 changes: 25 additions & 0 deletions sentry-ruby/spec/sentry/metrics_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,29 @@
described_class.gauge('foo', 5.0, unit: 'second', tags: { fortytwo: 42 }, timestamp: fake_time)
end
end

describe '.timing' do
it 'does nothing without a block' do
expect(aggregator).not_to receive(:add)
described_class.timing('foo')
end

it 'does nothing with a non-duration unit' do
expect(aggregator).not_to receive(:add)
described_class.timing('foo', unit: 'ratio') { }
end

it 'measures time taken as distribution and passes through args to aggregator' do
expect(aggregator).to receive(:add).with(
:d,
'foo',
an_instance_of(Integer),
unit: 'millisecond',
tags: { fortytwo: 42 },
timestamp: fake_time
)

described_class.timing('foo', unit: 'millisecond', tags: { fortytwo: 42 }, timestamp: fake_time) { sleep(0.1) }
end
end
end