Skip to content
Open
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
38 changes: 34 additions & 4 deletions lib/datadog/statsd.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ def initialize(
raise ArgumentError, 'tags must be an array of string tags or a Hash'
end

@logger = logger
@namespace = namespace
@prefix = @namespace ? "#{@namespace}.".freeze : nil
@serializer = Serialization::Serializer.new(prefix: @prefix, global_tags: tags)
Expand Down Expand Up @@ -187,6 +188,10 @@ def decrement(stat, opts = EMPTY_OPTIONS)
# @option opts [Array<String>] :tags An array of tags
def count(stat, count, opts = EMPTY_OPTIONS)
opts = { sample_rate: opts } if opts.is_a?(Numeric)
unless count.is_a?(Numeric)
@logger.error("count: count value should be numeric") if @logger
return
end
send_stats(stat, count, COUNTER_TYPE, opts)
end

Expand All @@ -206,6 +211,10 @@ def count(stat, count, opts = EMPTY_OPTIONS)
# $statsd.gauge('user.count', User.count)
def gauge(stat, value, opts = EMPTY_OPTIONS)
opts = { sample_rate: opts } if opts.is_a?(Numeric)
unless value.is_a?(Numeric)
@logger.error("gauge: value should be numeric") if @logger
return
end
send_stats(stat, value, GAUGE_TYPE, opts)
end

Expand All @@ -220,6 +229,10 @@ def gauge(stat, value, opts = EMPTY_OPTIONS)
# @example Report the current user count:
# $statsd.histogram('user.count', User.count)
def histogram(stat, value, opts = EMPTY_OPTIONS)
unless value.is_a?(Numeric)
@logger.error("histogram: value should be numeric") if @logger
return
end
send_stats(stat, value, HISTOGRAM_TYPE, opts)
end

Expand All @@ -234,6 +247,10 @@ def histogram(stat, value, opts = EMPTY_OPTIONS)
# @example Report the current user count:
# $statsd.distribution('user.count', User.count)
def distribution(stat, value, opts = EMPTY_OPTIONS)
unless value.is_a?(Numeric)
@logger.error("distribution: value should be numeric") if @logger
return
end
send_stats(stat, value, DISTRIBUTION_TYPE, opts)
end

Expand Down Expand Up @@ -270,6 +287,10 @@ def distribution_time(stat, opts = EMPTY_OPTIONS)
# @option opts [Array<String>] :tags An array of tags
def timing(stat, ms, opts = EMPTY_OPTIONS)
opts = { sample_rate: opts } if opts.is_a?(Numeric)
unless ms.is_a?(Numeric)
@logger.error("timing: ms should be numeric") if @logger
return
end
send_stats(stat, ms, TIMING_TYPE, opts)
end

Expand Down Expand Up @@ -307,6 +328,9 @@ def time(stat, opts = EMPTY_OPTIONS)
# $statsd.set('visitors.uniques', User.id)
def set(stat, value, opts = EMPTY_OPTIONS)
opts = { sample_rate: opts } if opts.is_a?(Numeric)
if value.is_a?(String)
value = escape(value)
end
send_stats(stat, value, SET_TYPE, opts)
end

Expand All @@ -315,10 +339,10 @@ def set(stat, value, opts = EMPTY_OPTIONS)
# @param [String] name Service check name
# @param [String] status Service check status.
# @param [Hash] opts the additional data about the service check
# @option opts [Integer, String, nil] :timestamp (nil) Assign a timestamp to the service check. Default is now when none
# @option opts [String, nil] :hostname (nil) Assign a hostname to the service check.
# @option opts [Array<String>, nil] :tags (nil) An array of tags
# @option opts [String, nil] :message (nil) A message to associate with this service check status
# @option opts [Integer, String, nil] :timestamp (nil) Assign a timestamp to the service check. Default is now when none
# @option opts [String, nil] :hostname (nil) Assign a hostname to the service check.
# @option opts [Array<String>, nil] :tags (nil) An array of tags
# @option opts [String, nil] :message (nil) A message to associate with this service check status
# @example Report a critical service check status
# $statsd.service_check('my.service.check', Statsd::CRITICAL, :tags=>['urgent'])
def service_check(name, status, opts = EMPTY_OPTIONS)
Expand Down Expand Up @@ -419,6 +443,12 @@ def now
Process.clock_gettime(Process::CLOCK_MONOTONIC)
end

def escape(text)
text.delete('|').tap do |t|
t.gsub!("\n", '\n')
end
end

def send_stats(stat, delta, type, opts = EMPTY_OPTIONS)
telemetry.sent(metrics: 1) if telemetry

Expand Down
49 changes: 49 additions & 0 deletions spec/statsd_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,55 @@
expect(socket.recv[0]).to eq_with_telemetry('foobar:1|c')
end

it 'does not send count when the value isnt numeric' do
subject.count('metric_name', 'hello')
subject.flush(sync: true)

expect(socket.recv).to be nil
end

it 'does not send gauge when the value isnt numeric' do
subject.gauge('metric_name', 'hello')
subject.flush(sync: true)

expect(socket.recv).to be nil
end

it 'escape set when the value is a string' do
subject.set('metric_name', "hel\nl|o")
subject.flush(sync: true)

expect(socket.recv[0]).to eq_with_telemetry("metric_name:hel\\nlo|s")
end

it 'does not escape set when the value is numeric' do
subject.set('metric_name', 5)
subject.flush(sync: true)

expect(socket.recv[0]).to eq_with_telemetry("metric_name:5|s")
end

it 'does not send distribution when the value isnt numeric' do
subject.distribution('metric_name', 'hello')
subject.flush(sync: true)

expect(socket.recv).to be nil
end

it 'does not send histogram when the value isnt numeric' do
subject.histogram('metric_name', 'hello')
subject.flush(sync: true)

expect(socket.recv).to be nil
end

it 'does not send timing when the value isnt numeric' do
subject.timing('metric_name', 'hello')
subject.flush(sync: true)

expect(socket.recv).to be nil
end

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