Skip to content

Commit

Permalink
0.9.0: Ability to add default tags only for a specific group (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
liaden authored May 7, 2021
1 parent 56a1d82 commit d15c7c5
Show file tree
Hide file tree
Showing 18 changed files with 192 additions and 43 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## 0.9.0 - 2021-05-07

### Added

- Ability to set global metric tags only for a specific group [#19](https://github.com/yabeda-rb/yabeda/pull/19) by [@liaden]

## 0.8.0 - 2020-08-21

### Added
Expand Down Expand Up @@ -99,3 +105,4 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
[@Envek]: https://github.com/Envek "Andrey Novikov"
[@dsalahutdinov]: https://github.com/dsalahutdinov "Dmitry Salahutdinov"
[@asusikov]: https://github.com/asusikov "Alexander Susikov"
[@liaden]: https://github.com/liaden "Joel Johnson"
21 changes: 18 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,14 @@ And then execute:
end
```

5. _Optionally_ setup default tags that will be added to all metrics
5. _Optionally_ setup default tags for all appropriate metrics
```ruby
Yabeda.configure do
# matches all metrics in all groups
default_tag :rails_environment, 'production'
# matches all metrics in the :your_app group
default_tag :tag_name, 'override', group: :your_app
end
# You can redefine them for limited amount of time
Expand All @@ -91,8 +95,19 @@ And then execute:
end
```

6. See the docs for the adapter you're using
7. Enjoy!
**Note**: any usage of `with_tags` **must** have all those tags defined on all metrics that are generated in the block.

6. _Optionally_ override default tags using precedence:

The tag precedence from high to low is:

* Manually specified tags
* Thread local tags (specified by `Yabeda.with_tags`)
* Group specific tags
* Global tags

7. See the docs for the adapter you're using
8. Enjoy!
## Available monitoring system adapters
Expand Down
14 changes: 10 additions & 4 deletions lib/yabeda.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ def metrics

# @return [Hash<String, Yabeda::Group>] All registered metrics
def groups
@groups ||= Concurrent::Hash.new
@groups ||= Concurrent::Hash.new.tap do |hash|
hash[nil] = Yabeda::GlobalGroup.new(nil)
end
end

# @return [Hash<String, Yabeda::BaseAdapter>] All loaded adapters
Expand All @@ -33,7 +35,7 @@ def collectors
@collectors ||= Concurrent::Array.new
end

# @return [Hash<Symbol, Symbol>] All added default tags
# @return [Hash<Symbol, Symbol>] All added global default tags
def default_tags
@default_tags ||= Concurrent::Hash.new
end
Expand Down Expand Up @@ -86,14 +88,18 @@ def configure!
# Forget all the configuration.
# For testing purposes as it doesn't rollback changes in adapters.
# @api private
# rubocop: disable Metrics/AbcSize
def reset!
default_tags.clear
adapters.clear
groups.clear
metrics.clear
groups.each_key { |group| singleton_class.send(:remove_method, group) if group && respond_to?(group) }
@groups = nil
metrics.each_key { |metric| singleton_class.send(:remove_method, metric) if respond_to?(metric) }
@metrics = nil
collectors.clear
configurators.clear
instance_variable_set(:@configured_by, nil)
end
# rubocop: enable Metrics/AbcSize
end
end
2 changes: 1 addition & 1 deletion lib/yabeda/counter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module Yabeda
# Growing-only counter
class Counter < Metric
def increment(tags, by: 1)
all_tags = ::Yabeda::Tags.build(tags)
all_tags = ::Yabeda::Tags.build(tags, group)
values[all_tags] += by
::Yabeda.adapters.each do |_, adapter|
adapter.perform_counter_increment!(self, all_tags, by)
Expand Down
21 changes: 15 additions & 6 deletions lib/yabeda/dsl/class_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
require "yabeda/gauge"
require "yabeda/histogram"
require "yabeda/group"
require "yabeda/global_group"
require "yabeda/dsl/metric_builder"

module Yabeda
Expand All @@ -30,6 +31,7 @@ def collect(&block)
# (like NewRelic) it is treated individually and has a special meaning.
def group(group_name)
@group = group_name
Yabeda.groups[@group] ||= Yabeda::Group.new(@group)
return unless block_given?

yield
Expand Down Expand Up @@ -58,24 +60,30 @@ def histogram(*args, **kwargs, &block)
#
# @param name [Symbol] Name of default tag
# @param value [String] Value of default tag
def default_tag(name, value)
::Yabeda.default_tags[name] = value
def default_tag(name, value, group: @group)
if group
Yabeda.groups[group] ||= Yabeda::Group.new(group)
Yabeda.groups[group].default_tag(name, value)
else
Yabeda.default_tags[name] = value
end
end

# Redefine default tags for a limited amount of time
# @param tags Hash{Symbol=>#to_s}
def with_tags(**tags)
Thread.current[:yabeda_temporary_tags] = tags
previous_temp_tags = temporary_tags
Thread.current[:yabeda_temporary_tags] = Thread.current[:yabeda_temporary_tags].merge(tags)
yield
ensure
Thread.current[:yabeda_temporary_tags] = {}
Thread.current[:yabeda_temporary_tags] = previous_temp_tags
end

# Get tags set by +with_tags+
# @api private
# @return Hash
def temporary_tags
Thread.current[:yabeda_temporary_tags] || {}
Thread.current[:yabeda_temporary_tags] ||= {}
end

private
Expand All @@ -95,9 +103,10 @@ def register_group_for(metric)
if group.nil?
group = Group.new(metric.group)
::Yabeda.groups[metric.group] = group
::Yabeda.define_singleton_method(metric.group) { group }
end

::Yabeda.define_singleton_method(metric.group) { group } unless ::Yabeda.respond_to?(metric.group)

group.register_metric(metric)
end
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 @@ -4,7 +4,7 @@ module Yabeda
# Arbitrary value, can be changed in both sides
class Gauge < Metric
def set(tags, value)
all_tags = ::Yabeda::Tags.build(tags)
all_tags = ::Yabeda::Tags.build(tags, group)
values[all_tags] = value
::Yabeda.adapters.each do |_, adapter|
adapter.perform_gauge_set!(self, all_tags, value)
Expand Down
13 changes: 13 additions & 0 deletions lib/yabeda/global_group.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

require "forwardable"
require_relative "./group"

module Yabeda
# Represents implicit global group
class GlobalGroup < Group
extend Forwardable

def_delegators ::Yabeda, :default_tags, :default_tag
end
end
10 changes: 10 additions & 0 deletions lib/yabeda/group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ class Group

param :name

def default_tags
@default_tags ||= Concurrent::Hash.new
::Yabeda.default_tags.merge(@default_tags)
end

def default_tag(key, value)
@default_tags ||= Concurrent::Hash.new
@default_tags[key] = value
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 @@ -7,7 +7,7 @@ class Histogram < Metric
option :buckets

def measure(tags, value)
all_tags = ::Yabeda::Tags.build(tags)
all_tags = ::Yabeda::Tags.build(tags, group)
values[all_tags] = value
::Yabeda.adapters.each do |_, adapter|
adapter.perform_histogram_measure!(self, all_tags, value)
Expand Down
6 changes: 4 additions & 2 deletions lib/yabeda/metric.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@ class Metric

# Returns the value for the given label set
def get(labels = {})
values[::Yabeda::Tags.build(labels)]
values[::Yabeda::Tags.build(labels, group)]
end

def values
@values ||= Concurrent::Hash.new
end

# Returns allowed tags for metric (with account for global and group-level +default_tags+)
# @return Array<Symbol>
def tags
(Yabeda.default_tags.keys + Array(super)).uniq
(Yabeda.groups[group].default_tags.keys + Array(super)).uniq
end
end
end
8 changes: 6 additions & 2 deletions lib/yabeda/tags.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
module Yabeda
# Class to merge tags
class Tags
def self.build(tags)
::Yabeda.default_tags.merge(Yabeda.temporary_tags).merge(tags)
def self.build(tags, group_name = nil)
Yabeda.default_tags.dup.tap do |result|
result.merge!(Yabeda.groups[group_name].default_tags) if group_name
result.merge!(Yabeda.temporary_tags)
result.merge!(tags)
end
end
end
end
2 changes: 1 addition & 1 deletion lib/yabeda/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Yabeda
VERSION = "0.8.0"
VERSION = "0.9.0"
end
2 changes: 1 addition & 1 deletion spec/yabeda/counter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
counter :test_counter
end
Yabeda.configure!
allow(Yabeda::Tags).to receive(:build).with(tags).and_return(built_tags)
allow(Yabeda::Tags).to receive(:build).with(tags, anything).and_return(built_tags)
::Yabeda.register_adapter(:test_adapter, adapter)
end

Expand Down
53 changes: 35 additions & 18 deletions spec/yabeda/dsl/class_methods_spec.rb
Original file line number Diff line number Diff line change
@@ -1,24 +1,6 @@
# frozen_string_literal: true

RSpec.describe Yabeda::DSL::ClassMethods do
after do
if Yabeda.instance_variable_defined?(:@groups)
Yabeda.instance_variable_get(:@groups).each_key do |group|
Yabeda.singleton_class.send(:remove_method, group)
end
Yabeda.remove_instance_variable(:@groups)
end

if Yabeda.instance_variable_defined?(:@metrics)
Yabeda.instance_variable_get(:@metrics).each_key do |metric|
Yabeda.singleton_class.send(:remove_method, metric)
end
Yabeda.remove_instance_variable(:@metrics)
end

::Yabeda.default_tags.clear
end

describe ".group" do
context "without block" do
before do
Expand Down Expand Up @@ -125,5 +107,40 @@

it { is_expected.to eq(environment: "test") }
end

context "with a specified group that does not exist" do
before do
Yabeda.configure { default_tag :environment, "test", group: :missing_group }
Yabeda.configure!
end

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

it "defines the default tag" do
expect(Yabeda.groups[:missing_group].default_tags).to eq(environment: "test")
end
end

context "when specified group is defined after default_tag" do
before do
Yabeda.configure { default_tag :environment, "test", group: :missing_group }
Yabeda.configure do
group :missing_group
default_tag :key, "value"
gauge :test_gauge, comment: "..."
end
Yabeda.configure!
end

it "defines all the tags" do
expect(Yabeda.groups[:missing_group].default_tags).to eq(environment: "test", key: "value")
end

it "test_gauge has all the tags defined" do
expect(Yabeda.missing_group.test_gauge.tags).to eq(%i[environment key])
end
end
end
end
2 changes: 1 addition & 1 deletion spec/yabeda/gauge_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
gauge :test_gauge
end
Yabeda.configure!
allow(Yabeda::Tags).to receive(:build).with(tags).and_return(built_tags)
allow(Yabeda::Tags).to receive(:build).with(tags, anything).and_return(built_tags)
::Yabeda.register_adapter(:test_adapter, adapter)
end

Expand Down
37 changes: 37 additions & 0 deletions spec/yabeda/group_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# frozen_string_literal: true

RSpec.describe Yabeda::Group do
let(:name) { nil }

let(:group) { described_class.new(name) }

before do
Yabeda.groups[name] = group
end

after { Yabeda.reset! }

describe "default tags" do
context "when on the top level group" do
it "is an empty by default" do
expect(group.default_tags).to be_empty
end
end

context "when within a named group" do
let(:name) { :group1 }

it "includes top level default_tags" do
Yabeda.default_tag :tag, "default"
expect(group.default_tags).to eq(tag: "default")
end

it "overrides top level default_tags" do
Yabeda.default_tag :tag, "default"
group.default_tag :tag, "overridden"
expect(Yabeda.groups[nil].default_tags).to eq(tag: "default")
expect(group.default_tags).to eq(tag: "overridden")
end
end
end
end
2 changes: 1 addition & 1 deletion spec/yabeda/histogram_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
histogram :test_histogram, buckets: [1, 10, 100]
end
Yabeda.configure!
allow(Yabeda::Tags).to receive(:build).with(tags).and_return(built_tags)
allow(Yabeda::Tags).to receive(:build).with(tags, anything).and_return(built_tags)
::Yabeda.register_adapter(:test_adapter, adapter)
end

Expand Down
Loading

0 comments on commit d15c7c5

Please sign in to comment.