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 config.metrics.before_emit callback
  • Loading branch information
sl0thentr0py committed Mar 12, 2024
commit da14eafe630064b89c4fa845d32cd349c60580ee
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- 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)
- Add metric summaries on spans [#2255](https://github.com/getsentry/sentry-ruby/pull/2255)
- Add `config.metrics.before_emit` callback [#2258](https://github.com/getsentry/sentry-ruby/pull/2258)

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 @@ -44,6 +45,18 @@
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) }

# add a before_emit callback to filter keys or update tags
Sentry.init do |config|
# ...
config.metrics.enabled = true
config.metrics.before_emit = lambda do |key, tags|
return nil if key == 'foo'
tags[:bar] = 42
tags.delete(:baz)
true
end
end
```

### Bug Fixes
Expand Down
6 changes: 0 additions & 6 deletions sentry-ruby/lib/sentry/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -576,12 +576,6 @@ def error_messages

private

def check_callable!(name, value)
unless value == nil || value.respond_to?(:call)
raise ArgumentError, "#{name} must be callable (or nil to disable)"
end
end

def init_dsn(dsn_string)
return if dsn_string.nil? || dsn_string.empty?

Expand Down
6 changes: 5 additions & 1 deletion sentry-ruby/lib/sentry/metrics/aggregator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class Aggregator
def initialize(configuration, client)
@client = client
@logger = configuration.logger
@before_emit = configuration.metrics.before_emit

@default_tags = {}
@default_tags['release'] = configuration.release if configuration.release
Expand Down Expand Up @@ -55,8 +56,11 @@ def add(type,
# this is integer division and thus takes the floor of the division
# and buckets into 10 second intervals
bucket_timestamp = (timestamp / ROLLUP_IN_SECONDS) * ROLLUP_IN_SECONDS
updated_tags = get_updated_tags(tags)

serialized_tags = serialize_tags(get_updated_tags(tags))
return if @before_emit && !@before_emit.call(key, updated_tags)

serialized_tags = serialize_tags(updated_tags)
bucket_key = [type, key, unit, serialized_tags]

added = @mutex.synchronize do
Expand Down
23 changes: 23 additions & 0 deletions sentry-ruby/lib/sentry/metrics/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,38 @@
module Sentry
module Metrics
class Configuration
include ArgumentCheckingHelper

# Enable metrics usage
# Starts a new {Sentry::Metrics::Aggregator} instance to aggregate metrics
# and a thread to aggregate flush every 5 seconds.
# @return [Boolean]
attr_accessor :enabled

# Optional Proc, called before emitting a metric to the aggregator.
# Use it to filter keys (return false/nil) or update tags.
# Make sure to return true at the end.
#
# @example
# config.metrics.before_emit = lambda do |key, tags|
# return nil if key == 'foo'
# tags[:bar] = 42
# tags.delete(:baz)
# true
# end
#
# @return [Proc, nil]
attr_reader :before_emit

def initialize
@enabled = false
end

def before_emit=(value)
check_callable!("metrics.before_emit", value)

@before_emit = value
end
end
end
end
6 changes: 6 additions & 0 deletions sentry-ruby/lib/sentry/utils/argument_checking_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,11 @@ def check_argument_includes!(argument, values)
raise ArgumentError, "expect the argument to be one of #{values.map(&:inspect).join(' or ')}, got #{argument.inspect}"
end
end

def check_callable!(name, value)
unless value == nil || value.respond_to?(:call)
raise ArgumentError, "#{name} must be callable (or nil to disable)"
end
end
end
end
32 changes: 32 additions & 0 deletions sentry-ruby/spec/sentry/metrics/aggregator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,38 @@
expect(metric.value).to eq(Set[1])
end

describe 'with before_emit callback' do
before do
perform_basic_setup do |config|
config.metrics.enabled = true
config.enable_tracing = true
config.release = 'test-release'
config.environment = 'test'
config.logger = Logger.new(string_io)

config.metrics.before_emit = lambda do |key, tags|
return nil if key == 'foo'
tags[:add_tag] = 42
tags.delete(:remove_tag)
true
end
end
end

it 'does not emit metric with filtered key' do
expect(Sentry::Metrics::CounterMetric).not_to receive(:new)
subject.add(:c, 'foo', 1)
expect(subject.buckets).to eq({})
end

it 'updates the tags according to the callback' do
subject.add(:c, 'bar', 1, tags: { remove_tag: 99 })
_, _, _, tags = subject.buckets.values.first.keys.first
expect(tags).not_to include(['remove_tag', '99'])
expect(tags).to include(['add_tag', '42'])
end
end

describe 'local aggregation for span metric summaries' do
it 'does nothing without an active scope span' do
expect_any_instance_of(Sentry::Metrics::LocalAggregator).not_to receive(:add)
Expand Down
11 changes: 11 additions & 0 deletions sentry-ruby/spec/sentry/metrics/configuration_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require 'spec_helper'

RSpec.describe Sentry::Metrics::Configuration do
describe '#before_emit=' do
it 'raises error when setting before_emit to anything other than callable or nil' do
subject.before_emit = -> { }
subject.before_emit = nil
expect { subject.before_emit = true }.to raise_error(ArgumentError, 'metrics.before_emit must be callable (or nil to disable)')
end
end
end
Loading