Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@

- Unify Logs and Metrics implementations ([#2826](https://github.com/getsentry/sentry-ruby/pull/2826))
- Unify LogEventBuffer and MetricEventBuffer logic ([#2830](https://github.com/getsentry/sentry-ruby/pull/2830))
- Add maximum limits on LogEventBuffer (1k) and MetricEventBuffer (10k) for protection from memory blowup ([#2831](https://github.com/getsentry/sentry-ruby/pull/2831))

## 6.2.0

Expand Down
2 changes: 2 additions & 0 deletions sentry-ruby/lib/sentry/log_event_buffer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ module Sentry
# @!visibility private
class LogEventBuffer < TelemetryEventBuffer
DEFAULT_MAX_EVENTS = 100
MAX_EVENTS_BEFORE_DROP = 1000

def initialize(configuration, client)
super(
configuration,
client,
event_class: LogEvent,
max_items: configuration.max_log_events || DEFAULT_MAX_EVENTS,
max_items_before_drop: MAX_EVENTS_BEFORE_DROP,
envelope_type: "log",
envelope_content_type: "application/vnd.sentry.items.log+json",
before_send: configuration.before_send_log
Expand Down
4 changes: 3 additions & 1 deletion sentry-ruby/lib/sentry/metric_event_buffer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ module Sentry
#
# @!visibility private
class MetricEventBuffer < TelemetryEventBuffer
DEFAULT_MAX_METRICS = 100
DEFAULT_MAX_METRICS = 1000
MAX_METRICS_BEFORE_DROP = 10_000

def initialize(configuration, client)
super(
configuration,
client,
event_class: MetricEvent,
max_items: configuration.max_metric_events || DEFAULT_MAX_METRICS,
max_items_before_drop: MAX_METRICS_BEFORE_DROP,
envelope_type: "trace_metric",
envelope_content_type: "application/vnd.sentry.items.trace-metric+json",
before_send: configuration.before_send_metric
Expand Down
20 changes: 15 additions & 5 deletions sentry-ruby/lib/sentry/telemetry_event_buffer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,19 @@ class TelemetryEventBuffer < ThreadedPeriodicWorker
FLUSH_INTERVAL = 5 # seconds

# @!visibility private
attr_reader :pending_items
attr_reader :pending_items, :envelope_type, :data_category

def initialize(configuration, client, event_class:, max_items:, envelope_type:, envelope_content_type:, before_send:)
def initialize(configuration, client, event_class:, max_items:, max_items_before_drop:, envelope_type:, envelope_content_type:, before_send:)
super(configuration.sdk_logger, FLUSH_INTERVAL)

@client = client
@dsn = configuration.dsn
@debug = configuration.debug
@event_class = event_class
@max_items = max_items
@max_items_before_drop = max_items_before_drop
@envelope_type = envelope_type
@data_category = Sentry::Envelope::Item.data_category(@envelope_type)
@envelope_content_type = envelope_content_type
@before_send = before_send

Expand Down Expand Up @@ -53,10 +55,19 @@ def flush
alias_method :run, :flush

def add_item(item)
raise ArgumentError, "expected a #{@event_class}, got #{item.class}" unless item.is_a?(@event_class)
unless item.is_a?(@event_class)
log_debug("[#{self.class}] expected a #{@event_class}, got #{item.class}")
return
end

@mutex.synchronize do
@pending_items << item
if size >= @max_items_before_drop
log_debug("[#{self.class}] exceeded max capacity, dropping event")
@client.transport.record_lost_event(:queue_overflow, @data_category)
else
@pending_items << item
end

send_items if size >= @max_items
end

Expand Down Expand Up @@ -103,7 +114,6 @@ def send_items
end

unless discarded_count.zero?
@data_category = Sentry::Envelope::Item.data_category(@envelope_type)
@client.transport.record_lost_event(:before_send, @data_category, num: discarded_count)
end

Expand Down
4 changes: 2 additions & 2 deletions sentry-ruby/spec/sentry/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -770,8 +770,8 @@ class SentryConfigurationSample < Sentry::Configuration
end

describe "#max_metric_events" do
it "returns 100 by default" do
expect(subject.max_metric_events).to eq(100)
it "returns 1000 by default" do
expect(subject.max_metric_events).to eq(1000)
end

it "can be set to an integer value" do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

RSpec.shared_examples "telemetry event buffer" do |event_factory:, max_items_config:, enable_config:|
let(:string_io) { StringIO.new }
let(:logger) { ::Logger.new(string_io) }
let(:client) { double(Sentry::Client) }
let(:sdk_logger) { ::Logger.new(string_io) }
let(:client) { Sentry.get_current_client }
let(:event) { event_factory.call }

before do
perform_basic_setup do |config|
config.sdk_logger = logger
config.sdk_logger = sdk_logger
config.background_worker_threads = 0
config.public_send(:"#{max_items_config}=", max_items)
config.public_send(:"#{enable_config}=", true)
Expand Down Expand Up @@ -67,6 +67,49 @@
end
end

describe "max capacity and dropping events" do
let(:max_items) { 3 }
let(:max_items_before_drop) { 10 }

before do
subject.instance_variable_set(:@max_items_before_drop, max_items_before_drop)

# don't clear pending items to allow buffer to grow
allow(subject).to receive(:clear!)
end

it "adds items up to max_items_before_drop capacity" do
expect {
max_items_before_drop.times { subject.add_item(event) }
}.to change { subject.size }.from(0).to(max_items_before_drop)
end

it "drops events when buffer reaches max_items_before_drop" do
max_items_before_drop.times { subject.add_item(event) }

expect {
subject.add_item(event)
}.not_to change { subject.size }

expect(subject.size).to eq(max_items_before_drop)
end

it "records lost event when dropping due to queue overflow" do
max_items_before_drop.times { subject.add_item(event) }

expect(client.transport).to receive(:record_lost_event).with(:queue_overflow, subject.data_category)

subject.add_item(event)
end

it "logs debug message when dropping events" do
max_items_before_drop.times { subject.add_item(event) }
subject.add_item(event)

expect(string_io.string).to include("exceeded max capacity, dropping event")
end
end

describe "error handling" do
let(:max_items) { 3 }

Expand Down
Loading