Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added statsd instrumentation support.

  • Loading branch information...
commit 984ed4a927f5d8756fe9cdf32c6fe056c6f3a4ec 1 parent 8618b75
@jnunemaker authored
View
1  Gemfile
@@ -3,6 +3,7 @@ gemspec
gem 'rake'
gem 'metriks', :require => false
+gem 'statsd-ruby', :require => false
gem 'rspec'
gem 'rack-test'
gem 'activesupport', :require => false
View
84 lib/flipper/instrumentation/metriks_subscriber.rb
@@ -2,90 +2,18 @@
# ActiveSupport::Notifications. Instead, you should require the metriks file
# that lives in the same directory as this file. The benefit is that it
# subscribes to the correct events and does everything for your.
+require 'flipper/instrumentation/subscriber'
require 'metriks'
module Flipper
module Instrumentation
- class MetriksSubscriber
- # Public: Use this as the subscribed block.
- def self.call(name, start, ending, transaction_id, payload)
- new(name, start, ending, transaction_id, payload).update
+ class MetriksSubscriber < Subscriber
+ def update_timer(metric)
+ Metriks.timer(metric).update(@duration)
end
- # Private: Initializes a new event processing instance.
- def initialize(name, start, ending, transaction_id, payload)
- @name = name
- @start = start
- @ending = ending
- @payload = payload
- @duration = ending - start
- @transaction_id = transaction_id
- end
-
- def update
- operation_type = @name.split('.').first
- method_name = "update_#{operation_type}_metrics"
-
- if respond_to?(method_name)
- send(method_name)
- else
- puts "Could not update #{operation_type} metrics as MetriksSubscriber did not respond to `#{method_name}`"
- end
- end
-
- def update_feature_operation_metrics
- feature_name = @payload[:feature_name]
- gate_name = @payload[:gate_name]
- operation = strip_trailing_question_mark(@payload[:operation])
- result = @payload[:result]
- thing = @payload[:thing]
-
- Metriks.timer("flipper.feature_operation.#{operation}").update(@duration)
-
- if @payload[:operation] == :enabled?
- metric_name = if result
- "flipper.feature.#{feature_name}.enabled"
- else
- "flipper.feature.#{feature_name}.disabled"
- end
-
- Metriks.meter(metric_name).mark
- end
- end
-
- def update_adapter_operation_metrics
- adapter_name = @payload[:adapter_name]
- operation = @payload[:operation]
- result = @payload[:result]
- value = @payload[:value]
- key = @payload[:key]
-
- Metriks.timer("flipper.adapter.#{adapter_name}.#{operation}").update(@duration)
- end
-
- def update_gate_operation_metrics
- feature_name = @payload[:feature_name]
- gate_name = @payload[:gate_name]
- operation = strip_trailing_question_mark(@payload[:operation])
- result = @payload[:result]
- thing = @payload[:thing]
-
- Metriks.timer("flipper.gate_operation.#{gate_name}.#{operation}").update(@duration)
- Metriks.timer("flipper.feature.#{feature_name}.gate_operation.#{gate_name}.#{operation}").update(@duration)
-
- if @payload[:operation] == :open?
- metric_name = if result
- "flipper.feature.#{feature_name}.gate.#{gate_name}.open"
- else
- "flipper.feature.#{feature_name}.gate.#{gate_name}.closed"
- end
-
- Metriks.meter(metric_name).mark
- end
- end
-
- def strip_trailing_question_mark(operation)
- operation.to_s.gsub(/\?$/, '')
+ def update_counter(metric)
+ Metriks.meter(metric).mark
end
end
end
View
6 lib/flipper/instrumentation/statsd.rb
@@ -0,0 +1,6 @@
+require 'securerandom'
+require 'active_support/notifications'
+require 'flipper/instrumentation/statsd_subscriber'
+
+ActiveSupport::Notifications.subscribe /\.flipper$/,
+ Flipper::Instrumentation::StatsdSubscriber
View
28 lib/flipper/instrumentation/statsd_subscriber.rb
@@ -0,0 +1,28 @@
+# Note: You should never need to require this file directly if you are using
+# ActiveSupport::Notifications. Instead, you should require the metriks file
+# that lives in the same directory as this file. The benefit is that it
+# subscribes to the correct events and does everything for your.
+require 'flipper/instrumentation/subscriber'
+require 'statsd'
+
+module Flipper
+ module Instrumentation
+ class StatsdSubscriber < Subscriber
+ class << self
+ attr_accessor :client
+ end
+
+ def update_timer(metric)
+ if self.class.client
+ self.class.client.timing metric, (@duration * 1_000).round
+ end
+ end
+
+ def update_counter(metric)
+ if self.class.client
+ self.class.client.increment metric
+ end
+ end
+ end
+ end
+end
View
102 lib/flipper/instrumentation/subscriber.rb
@@ -0,0 +1,102 @@
+module Flipper
+ module Instrumentation
+ class Subscriber
+ # Public: Use this as the subscribed block.
+ def self.call(name, start, ending, transaction_id, payload)
+ new(name, start, ending, transaction_id, payload).update
+ end
+
+ # Private: Initializes a new event processing instance.
+ def initialize(name, start, ending, transaction_id, payload)
+ @name = name
+ @start = start
+ @ending = ending
+ @payload = payload
+ @duration = ending - start
+ @transaction_id = transaction_id
+ end
+
+ # Internal: Override in subclass.
+ def update_timer(metric)
+ raise 'not implemented'
+ end
+
+ # Internal: Override in subclass.
+ def update_counter(metric)
+ raise 'not implemented'
+ end
+
+ # Private
+ def update
+ operation_type = @name.split('.').first
+ method_name = "update_#{operation_type}_metrics"
+
+ if respond_to?(method_name)
+ send(method_name)
+ else
+ puts "Could not update #{operation_type} metrics as #{self.class} did not respond to `#{method_name}`"
+ end
+ end
+
+ # Private
+ def update_feature_operation_metrics
+ feature_name = @payload[:feature_name]
+ gate_name = @payload[:gate_name]
+ operation = strip_trailing_question_mark(@payload[:operation])
+ result = @payload[:result]
+ thing = @payload[:thing]
+
+ update_timer "flipper.feature_operation.#{operation}"
+
+ if @payload[:operation] == :enabled?
+ metric_name = if result
+ "flipper.feature.#{feature_name}.enabled"
+ else
+ "flipper.feature.#{feature_name}.disabled"
+ end
+
+ update_counter metric_name
+ end
+ end
+
+ # Private
+ def update_adapter_operation_metrics
+ adapter_name = @payload[:adapter_name]
+ operation = @payload[:operation]
+ result = @payload[:result]
+ value = @payload[:value]
+ key = @payload[:key]
+
+
+ update_timer "flipper.adapter.#{adapter_name}.#{operation}"
+ end
+
+ # Private
+ def update_gate_operation_metrics
+ feature_name = @payload[:feature_name]
+ gate_name = @payload[:gate_name]
+ operation = strip_trailing_question_mark(@payload[:operation])
+ result = @payload[:result]
+ thing = @payload[:thing]
+
+ update_timer "flipper.gate_operation.#{gate_name}.#{operation}"
+ update_timer "flipper.feature.#{feature_name}.gate_operation.#{gate_name}.#{operation}"
+
+ if @payload[:operation] == :open?
+ metric_name = if result
+ "flipper.feature.#{feature_name}.gate.#{gate_name}.open"
+ else
+ "flipper.feature.#{feature_name}.gate.#{gate_name}.closed"
+ end
+
+ update_counter metric_name
+ end
+ end
+
+ # Private
+ def strip_trailing_question_mark(operation)
+ operation.to_s.gsub(/\?$/, '')
+ end
+ end
+ end
+end
View
7 spec/flipper/instrumentation/metriks_subscriber_spec.rb
@@ -56,11 +56,4 @@
Metriks.meter("flipper.feature.stats.gate.actor.open").count.should be(1)
Metriks.meter("flipper.feature.stats.gate.boolean.closed").count.should be(1)
end
-
- # Helper for seeing what is in the metriks registry
- def print_registry_names
- Metriks::Registry.default.each do |name, metric|
- puts name
- end
- end
end
View
76 spec/flipper/instrumentation/statsd_subscriber_spec.rb
@@ -0,0 +1,76 @@
+require 'helper'
+require 'flipper/adapters/memory'
+require 'flipper/instrumentation/statsd'
+
+describe Flipper::Instrumentation::StatsdSubscriber do
+ let(:statsd_client) { Statsd.new }
+ let(:socket) { FakeUDPSocket.new }
+ let(:adapter) { Flipper::Adapters::Memory.new }
+ let(:flipper) {
+ Flipper.new(adapter, :instrumenter => ActiveSupport::Notifications)
+ }
+
+ let(:user) { user = Struct.new(:flipper_id).new('1') }
+
+ before do
+ described_class.client = statsd_client
+ Thread.current[:statsd_socket] = socket
+ end
+
+ after do
+ described_class.client = nil
+ Thread.current[:statsd_socket] = nil
+ end
+
+ def assert_timer(metric)
+ regex = /#{Regexp.escape metric}\:\d+\|ms/
+ socket.buffer.detect { |op| op.first =~ regex }.should_not be_nil
+ end
+
+ def assert_counter(metric)
+ socket.buffer.detect { |op| op.first == "#{metric}:1|c" }.should_not be_nil
+ end
+
+ context "for enabled feature" do
+ it "updates feature metrics when calls happen" do
+ flipper[:stats].enable(user)
+ assert_timer 'flipper.feature_operation.enable'
+
+ flipper[:stats].enabled?(user)
+ assert_timer 'flipper.feature_operation.enabled'
+ assert_counter 'flipper.feature.stats.enabled'
+ end
+ end
+
+ context "for disabled feature" do
+ it "updates feature metrics when calls happen" do
+ flipper[:stats].disable(user)
+ assert_timer 'flipper.feature_operation.disable'
+
+ flipper[:stats].enabled?(user)
+ assert_timer 'flipper.feature_operation.enabled'
+ assert_counter 'flipper.feature.stats.disabled'
+ end
+ end
+
+ it "updates adapter metrics when calls happen" do
+ flipper[:stats].enable(user)
+ assert_timer 'flipper.adapter.memory.enable'
+
+ flipper[:stats].enabled?(user)
+ assert_timer 'flipper.adapter.memory.get'
+
+ flipper[:stats].disable(user)
+ assert_timer 'flipper.adapter.memory.disable'
+ end
+
+ it "updates gate metrics when calls happen" do
+ flipper[:stats].enable(user)
+ flipper[:stats].enabled?(user)
+
+ assert_timer 'flipper.gate_operation.boolean.open'
+ assert_timer 'flipper.feature.stats.gate_operation.boolean.open'
+ assert_counter 'flipper.feature.stats.gate.actor.open'
+ assert_counter 'flipper.feature.stats.gate.boolean.closed'
+ end
+end
View
2  spec/helper.rb
@@ -15,6 +15,8 @@
require 'flipper'
+Dir[root_path.join("spec/support/**/*.rb")].each { |f| require f }
+
RSpec.configure do |config|
config.fail_fast = true
View
27 spec/support/fake_udp_socket.rb
@@ -0,0 +1,27 @@
+class FakeUDPSocket
+ attr_reader :buffer
+
+ def initialize
+ @buffer = []
+ end
+
+ def send(message, *rest)
+ @buffer.push [message]
+ end
+
+ def recv
+ @buffer.shift
+ end
+
+ def clear
+ @buffer = []
+ end
+
+ def to_s
+ inspect
+ end
+
+ def inspect
+ "<FakeUDPSocket: #{@buffer.inspect}>"
+ end
+end

0 comments on commit 984ed4a

Please sign in to comment.
Something went wrong with that request. Please try again.