Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added statsd instrumentation support.
- Loading branch information
1 parent
8618b75
commit 984ed4a
Showing
9 changed files
with
248 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
require 'securerandom' | ||
require 'active_support/notifications' | ||
require 'flipper/instrumentation/statsd_subscriber' | ||
|
||
ActiveSupport::Notifications.subscribe /\.flipper$/, | ||
Flipper::Instrumentation::StatsdSubscriber |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |