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

Ability to change metrics setting for individual adapter #37

Merged
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
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,20 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

### Added

- Ability to limit some metrics to specific adapters. [#37](https://github.com/yabeda-rb/yabeda/pull/37) by [@Keallar] and [@Envek]

```ruby
Yabeda.configure do
group :cloud do
adapter :newrelic, :datadog

counter :foo
end

counter :bar, adapter: :prometheus
end
```

- Multiple expectations in RSpec matchers:

```ruby
Expand Down Expand Up @@ -155,3 +169,4 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
[@dsalahutdinov]: https://github.com/dsalahutdinov "Dmitry Salahutdinov"
[@asusikov]: https://github.com/asusikov "Alexander Susikov"
[@liaden]: https://github.com/liaden "Joel Johnson"
[@Keallar]: https://github.com/Keallar "Eugene Lysanskiy"
36 changes: 26 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,32 @@ expect { whatever }.to increment_yabeda_counter(:my_counter).with(
)
```

## Advanced usage

### Limiting metrics and groups to specific adapters

You can limit, which metrics and groups should be available for specific adapter:

```ruby
Yabeda.configure do
group :internal do
adapter :prometheus

counter :foo
gauge :bar
end

group :cloud do
adapter :newrelic

counter :baz
end

counter :qux, adapter: :prometheus
end
```


## Roadmap (aka TODO or Help wanted)

- Ability to change metric settings for individual adapters
Expand All @@ -233,16 +259,6 @@ expect { whatever }.to increment_yabeda_counter(:my_counter).with(
end
```

- Ability to route some metrics only for given adapter:

```rb
adapter :prometheus do
include_group :sidekiq
end
```



## Development

After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
Expand Down
8 changes: 5 additions & 3 deletions lib/yabeda.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def groups
end
end

# @return [Hash<String, Yabeda::BaseAdapter>] All loaded adapters
# @return [Hash<Symbol, Yabeda::BaseAdapter>] All loaded adapters
def adapters
@adapters ||= Concurrent::Hash.new
end
Expand Down Expand Up @@ -68,6 +68,8 @@ def register_adapter(name, instance)
adapters[name] = instance
# NOTE: Pretty sure there is race condition
metrics.each_value do |metric|
next unless metric.adapters.key?(name)

instance.register!(metric)
end
end
Expand Down Expand Up @@ -99,8 +101,8 @@ def configure!

# Register metrics in adapters after evaluating all configuration blocks
# to ensure that all global settings (like default tags) will be applied.
adapters.each_value do |adapter|
metrics.each_value do |metric|
metrics.each_value do |metric|
metric.adapters.each_value do |adapter|
adapter.register!(metric)
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/yabeda/counter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class Counter < Metric
def increment(tags, by: 1)
all_tags = ::Yabeda::Tags.build(tags, group)
values[all_tags] += by
::Yabeda.adapters.each_value do |adapter|
adapters.each_value do |adapter|
adapter.perform_counter_increment!(self, all_tags, by)
end
values[all_tags]
Expand Down
12 changes: 11 additions & 1 deletion lib/yabeda/dsl/class_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,24 @@ def temporary_tags
Thread.current[:yabeda_temporary_tags] ||= {}
end

# Limit all group metrics to specific adapters only
#
# @param adapter_names [Array<Symbol>] Names of adapters to use
def adapter(*adapter_names, group: @group)
raise ConfigurationError, "Adapter limitation can't be defined outside of group" unless group

Yabeda.groups[group] ||= Yabeda::Group.new(group)
Yabeda.groups[group].adapter(*adapter_names)
end

private

def register_metric(metric)
name = [metric.group, metric.name].compact.join("_")
::Yabeda.define_singleton_method(name) { metric }
::Yabeda.metrics[name] = metric
register_group_for(metric) if metric.group
::Yabeda.adapters.each_value { |adapter| adapter.register!(metric) } if ::Yabeda.configured?
metric.adapters.each_value { |adapter| adapter.register!(metric) } if ::Yabeda.configured?
metric
end

Expand Down
2 changes: 1 addition & 1 deletion lib/yabeda/gauge.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class Gauge < Metric
def set(tags, value)
all_tags = ::Yabeda::Tags.build(tags, group)
values[all_tags] = value
::Yabeda.adapters.each_value do |adapter|
adapters.each_value do |adapter|
adapter.perform_gauge_set!(self, all_tags, value)
end
value
Expand Down
7 changes: 7 additions & 0 deletions lib/yabeda/group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ def default_tag(key, value)
@default_tags[key] = value
end

def adapter(*adapter_names)
return @adapter if adapter_names.empty?

@adapter ||= Concurrent::Array.new
@adapter.push(*adapter_names)
end

def register_metric(metric)
define_singleton_method(metric.name) { metric }
end
Expand Down
2 changes: 1 addition & 1 deletion lib/yabeda/histogram.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def measure(tags, value = nil)

all_tags = ::Yabeda::Tags.build(tags, group)
values[all_tags] = value
::Yabeda.adapters.each_value do |adapter|
adapters.each_value do |adapter|
adapter.perform_histogram_measure!(self, all_tags, value)
end
value
Expand Down
31 changes: 30 additions & 1 deletion lib/yabeda/metric.rb
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe metric itself should have adapters method (that will default to all registered adapters unless limiting option specified)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try to add it

Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ class Metric
option :per, optional: true, comment: "Per which unit is measured `unit`. E.g. `call` as in seconds per call"
option :group, optional: true, comment: "Category name for grouping metrics"
option :aggregation, optional: true, comment: "How adapters should aggregate values from different processes"
# rubocop:disable Layout/LineLength
option :adapter, optional: true, comment: "Monitoring system adapter to register metric in and report metric values to (other adapters won't be used)"
# rubocop:enable Layout/LineLength

# Returns the value for the given label set
def get(labels = {})
Expand All @@ -25,13 +28,39 @@ def values
end

# Returns allowed tags for metric (with account for global and group-level +default_tags+)
# @return Array<Symbol>
# @return [Array<Symbol>]
def tags
(Yabeda.groups[group].default_tags.keys + Array(super)).uniq
end

def inspect
"#<#{self.class.name}: #{[@group, @name].compact.join('.')}>"
end

# Returns the metric adapters
# @return [Hash<Symbol, Yabeda::BaseAdapter>]
def adapters
return ::Yabeda.adapters unless adapter

@adapters ||= begin
adapter_names = Array(adapter)
unknown_adapters = adapter_names - ::Yabeda.adapters.keys

if unknown_adapters.any?
raise ConfigurationError,
"invalid adapter option #{adapter.inspect} in metric #{inspect}"
end

::Yabeda.adapters.slice(*adapter_names)
end
end

# Redefined option reader to get group-level adapter if not set on metric level
# @api private
def adapter
return ::Yabeda.groups[group]&.adapter if @adapter == Dry::Initializer::UNDEFINED

super
end
end
end
2 changes: 1 addition & 1 deletion lib/yabeda/summary.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def observe(tags, value = nil)

all_tags = ::Yabeda::Tags.build(tags, group)
values[all_tags] = value
::Yabeda.adapters.each_value do |adapter|
adapters.each_value do |adapter|
adapter.perform_summary_observe!(self, all_tags, value)
end
value
Expand Down
1 change: 1 addition & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require "bundler/setup"
require "yabeda"
require "yabeda/base_adapter"
require "pry"

RSpec.configure do |config|
Expand Down
22 changes: 22 additions & 0 deletions spec/yabeda/counter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,26 @@
increment_counter
expect(adapter).to have_received(:perform_counter_increment!).with(counter, built_tags, metric_value)
end

context "with adapter option" do
let(:counter) { Yabeda.counter_with_adapter }
let(:another_adapter) { instance_double(Yabeda::BaseAdapter, perform_counter_increment!: true, register!: true) }

before do
Yabeda.register_adapter(:another_adapter, another_adapter)
Yabeda.configure do
counter :counter_with_adapter, adapter: :test_adapter
end
Yabeda.configure! unless Yabeda.already_configured?
end

it "execute perform_counter_increment! method of adapter with name :test_adapter" do
increment_counter

aggregate_failures do
expect(adapter).to have_received(:perform_counter_increment!).with(counter, built_tags, metric_value)
expect(another_adapter).not_to have_received(:perform_counter_increment!)
end
end
end
end
55 changes: 55 additions & 0 deletions spec/yabeda/dsl/class_methods_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,59 @@
end
end
end

describe ".configure" do
subject(:configure) { Yabeda.configure(&block) }

let(:block) { proc { histogram :test_histogram, buckets: [42] } }

before do
Yabeda.register_adapter(:another_adapter, Yabeda::TestAdapter.instance)
Yabeda.configure! unless Yabeda.configured?
end

it "register metric" do
configure

expect(Yabeda.test_histogram).to be_a(Yabeda::Histogram)
end

context "when got metric with adapter option" do
let(:block) { proc { histogram :invalid_test, buckets: [42], adapter: :another_adapter } }

it { expect { configure }.not_to raise_error }

context "when option is invalid" do
let(:block) { proc { histogram :invalid_test, buckets: [42], adapter: :invalid } }

it { expect { configure }.to raise_error(Yabeda::ConfigurationError, /invalid adapter option/) }
end
end
end

describe ".adapter" do
context "when group is not defined" do
it "raises an error" do
expect do
Yabeda.configure { adapter :test }
Yabeda.configure! unless Yabeda.already_configured?
end.to raise_error(Yabeda::ConfigurationError, /can't be defined outside of group/)
end
end

context "with a specified group that does not exist" do
before do
Yabeda.configure { adapter :test, group: :adapter_group }
Yabeda.configure! unless Yabeda.already_configured?
end

it "creates the group" do
expect(Yabeda.groups[:adapter_group]).to be_a(Yabeda::Group)
end

it "defines the default tag" do
expect(Yabeda.groups[:adapter_group].adapter).to eq(%i[test])
end
end
end
end
22 changes: 22 additions & 0 deletions spec/yabeda/gauge_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,26 @@
end
end
end

context "with adapter option" do
let(:gauge) { Yabeda.gauge_with_adapter }
let(:another_adapter) { instance_double(Yabeda::BaseAdapter, perform_gauge_set!: true, register!: true) }

before do
Yabeda.register_adapter(:another_adapter, another_adapter)
Yabeda.configure do
gauge :gauge_with_adapter, adapter: :test_adapter
end
Yabeda.configure! unless Yabeda.already_configured?
end

it "execute perform_counter_increment! method of adapter with name :test_adapter" do
set_gauge

aggregate_failures do
expect(adapter).to have_received(:perform_gauge_set!).with(gauge, built_tags, metric_value)
expect(another_adapter).not_to have_received(:perform_gauge_set!)
end
end
end
end
22 changes: 22 additions & 0 deletions spec/yabeda/histogram_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,26 @@
expect { measure_histogram }.to raise_error(ArgumentError)
end
end

context "with adapter option" do
let(:histogram) { Yabeda.histogram_with_adapter }
let(:another_adapter) { instance_double(Yabeda::BaseAdapter, perform_histogram_measure!: true, register!: true) }

before do
Yabeda.register_adapter(:another_adapter, another_adapter)
Yabeda.configure do
histogram :histogram_with_adapter, adapter: :test_adapter, buckets: [1, 10, 100]
end
Yabeda.configure! unless Yabeda.already_configured?
end

it "execute perform_counter_increment! method of adapter with name :test_adapter" do
measure_histogram

aggregate_failures do
expect(adapter).to have_received(:perform_histogram_measure!).with(histogram, built_tags, metric_value)
expect(another_adapter).not_to have_received(:perform_histogram_measure!)
end
end
end
end
Loading