Skip to content

Commit d15c7c5

Browse files
authored
0.9.0: Ability to add default tags only for a specific group (#19)
1 parent 56a1d82 commit d15c7c5

18 files changed

+192
-43
lines changed

CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
66
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
77

8+
## 0.9.0 - 2021-05-07
9+
10+
### Added
11+
12+
- Ability to set global metric tags only for a specific group [#19](https://github.com/yabeda-rb/yabeda/pull/19) by [@liaden]
13+
814
## 0.8.0 - 2020-08-21
915

1016
### Added
@@ -99,3 +105,4 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
99105
[@Envek]: https://github.com/Envek "Andrey Novikov"
100106
[@dsalahutdinov]: https://github.com/dsalahutdinov "Dmitry Salahutdinov"
101107
[@asusikov]: https://github.com/asusikov "Alexander Susikov"
108+
[@liaden]: https://github.com/liaden "Joel Johnson"

README.md

+18-3
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,14 @@ And then execute:
7979
end
8080
```
8181

82-
5. _Optionally_ setup default tags that will be added to all metrics
82+
5. _Optionally_ setup default tags for all appropriate metrics
8383
```ruby
8484
Yabeda.configure do
85+
# matches all metrics in all groups
8586
default_tag :rails_environment, 'production'
87+
88+
# matches all metrics in the :your_app group
89+
default_tag :tag_name, 'override', group: :your_app
8690
end
8791
8892
# You can redefine them for limited amount of time
@@ -91,8 +95,19 @@ And then execute:
9195
end
9296
```
9397

94-
6. See the docs for the adapter you're using
95-
7. Enjoy!
98+
**Note**: any usage of `with_tags` **must** have all those tags defined on all metrics that are generated in the block.
99+
100+
6. _Optionally_ override default tags using precedence:
101+
102+
The tag precedence from high to low is:
103+
104+
* Manually specified tags
105+
* Thread local tags (specified by `Yabeda.with_tags`)
106+
* Group specific tags
107+
* Global tags
108+
109+
7. See the docs for the adapter you're using
110+
8. Enjoy!
96111
97112
## Available monitoring system adapters
98113

lib/yabeda.rb

+10-4
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ def metrics
2020

2121
# @return [Hash<String, Yabeda::Group>] All registered metrics
2222
def groups
23-
@groups ||= Concurrent::Hash.new
23+
@groups ||= Concurrent::Hash.new.tap do |hash|
24+
hash[nil] = Yabeda::GlobalGroup.new(nil)
25+
end
2426
end
2527

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

36-
# @return [Hash<Symbol, Symbol>] All added default tags
38+
# @return [Hash<Symbol, Symbol>] All added global default tags
3739
def default_tags
3840
@default_tags ||= Concurrent::Hash.new
3941
end
@@ -86,14 +88,18 @@ def configure!
8688
# Forget all the configuration.
8789
# For testing purposes as it doesn't rollback changes in adapters.
8890
# @api private
91+
# rubocop: disable Metrics/AbcSize
8992
def reset!
9093
default_tags.clear
9194
adapters.clear
92-
groups.clear
93-
metrics.clear
95+
groups.each_key { |group| singleton_class.send(:remove_method, group) if group && respond_to?(group) }
96+
@groups = nil
97+
metrics.each_key { |metric| singleton_class.send(:remove_method, metric) if respond_to?(metric) }
98+
@metrics = nil
9499
collectors.clear
95100
configurators.clear
96101
instance_variable_set(:@configured_by, nil)
97102
end
103+
# rubocop: enable Metrics/AbcSize
98104
end
99105
end

lib/yabeda/counter.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module Yabeda
44
# Growing-only counter
55
class Counter < Metric
66
def increment(tags, by: 1)
7-
all_tags = ::Yabeda::Tags.build(tags)
7+
all_tags = ::Yabeda::Tags.build(tags, group)
88
values[all_tags] += by
99
::Yabeda.adapters.each do |_, adapter|
1010
adapter.perform_counter_increment!(self, all_tags, by)

lib/yabeda/dsl/class_methods.rb

+15-6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
require "yabeda/gauge"
66
require "yabeda/histogram"
77
require "yabeda/group"
8+
require "yabeda/global_group"
89
require "yabeda/dsl/metric_builder"
910

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

3537
yield
@@ -58,24 +60,30 @@ def histogram(*args, **kwargs, &block)
5860
#
5961
# @param name [Symbol] Name of default tag
6062
# @param value [String] Value of default tag
61-
def default_tag(name, value)
62-
::Yabeda.default_tags[name] = value
63+
def default_tag(name, value, group: @group)
64+
if group
65+
Yabeda.groups[group] ||= Yabeda::Group.new(group)
66+
Yabeda.groups[group].default_tag(name, value)
67+
else
68+
Yabeda.default_tags[name] = value
69+
end
6370
end
6471

6572
# Redefine default tags for a limited amount of time
6673
# @param tags Hash{Symbol=>#to_s}
6774
def with_tags(**tags)
68-
Thread.current[:yabeda_temporary_tags] = tags
75+
previous_temp_tags = temporary_tags
76+
Thread.current[:yabeda_temporary_tags] = Thread.current[:yabeda_temporary_tags].merge(tags)
6977
yield
7078
ensure
71-
Thread.current[:yabeda_temporary_tags] = {}
79+
Thread.current[:yabeda_temporary_tags] = previous_temp_tags
7280
end
7381

7482
# Get tags set by +with_tags+
7583
# @api private
7684
# @return Hash
7785
def temporary_tags
78-
Thread.current[:yabeda_temporary_tags] || {}
86+
Thread.current[:yabeda_temporary_tags] ||= {}
7987
end
8088

8189
private
@@ -95,9 +103,10 @@ def register_group_for(metric)
95103
if group.nil?
96104
group = Group.new(metric.group)
97105
::Yabeda.groups[metric.group] = group
98-
::Yabeda.define_singleton_method(metric.group) { group }
99106
end
100107

108+
::Yabeda.define_singleton_method(metric.group) { group } unless ::Yabeda.respond_to?(metric.group)
109+
101110
group.register_metric(metric)
102111
end
103112
end

lib/yabeda/gauge.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module Yabeda
44
# Arbitrary value, can be changed in both sides
55
class Gauge < Metric
66
def set(tags, value)
7-
all_tags = ::Yabeda::Tags.build(tags)
7+
all_tags = ::Yabeda::Tags.build(tags, group)
88
values[all_tags] = value
99
::Yabeda.adapters.each do |_, adapter|
1010
adapter.perform_gauge_set!(self, all_tags, value)

lib/yabeda/global_group.rb

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# frozen_string_literal: true
2+
3+
require "forwardable"
4+
require_relative "./group"
5+
6+
module Yabeda
7+
# Represents implicit global group
8+
class GlobalGroup < Group
9+
extend Forwardable
10+
11+
def_delegators ::Yabeda, :default_tags, :default_tag
12+
end
13+
end

lib/yabeda/group.rb

+10
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@ class Group
99

1010
param :name
1111

12+
def default_tags
13+
@default_tags ||= Concurrent::Hash.new
14+
::Yabeda.default_tags.merge(@default_tags)
15+
end
16+
17+
def default_tag(key, value)
18+
@default_tags ||= Concurrent::Hash.new
19+
@default_tags[key] = value
20+
end
21+
1222
def register_metric(metric)
1323
define_singleton_method(metric.name) { metric }
1424
end

lib/yabeda/histogram.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ class Histogram < Metric
77
option :buckets
88

99
def measure(tags, value)
10-
all_tags = ::Yabeda::Tags.build(tags)
10+
all_tags = ::Yabeda::Tags.build(tags, group)
1111
values[all_tags] = value
1212
::Yabeda.adapters.each do |_, adapter|
1313
adapter.perform_histogram_measure!(self, all_tags, value)

lib/yabeda/metric.rb

+4-2
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,17 @@ class Metric
1717

1818
# Returns the value for the given label set
1919
def get(labels = {})
20-
values[::Yabeda::Tags.build(labels)]
20+
values[::Yabeda::Tags.build(labels, group)]
2121
end
2222

2323
def values
2424
@values ||= Concurrent::Hash.new
2525
end
2626

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

lib/yabeda/tags.rb

+6-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@
33
module Yabeda
44
# Class to merge tags
55
class Tags
6-
def self.build(tags)
7-
::Yabeda.default_tags.merge(Yabeda.temporary_tags).merge(tags)
6+
def self.build(tags, group_name = nil)
7+
Yabeda.default_tags.dup.tap do |result|
8+
result.merge!(Yabeda.groups[group_name].default_tags) if group_name
9+
result.merge!(Yabeda.temporary_tags)
10+
result.merge!(tags)
11+
end
812
end
913
end
1014
end

lib/yabeda/version.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# frozen_string_literal: true
22

33
module Yabeda
4-
VERSION = "0.8.0"
4+
VERSION = "0.9.0"
55
end

spec/yabeda/counter_spec.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
counter :test_counter
1515
end
1616
Yabeda.configure!
17-
allow(Yabeda::Tags).to receive(:build).with(tags).and_return(built_tags)
17+
allow(Yabeda::Tags).to receive(:build).with(tags, anything).and_return(built_tags)
1818
::Yabeda.register_adapter(:test_adapter, adapter)
1919
end
2020

spec/yabeda/dsl/class_methods_spec.rb

+35-18
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,6 @@
11
# frozen_string_literal: true
22

33
RSpec.describe Yabeda::DSL::ClassMethods do
4-
after do
5-
if Yabeda.instance_variable_defined?(:@groups)
6-
Yabeda.instance_variable_get(:@groups).each_key do |group|
7-
Yabeda.singleton_class.send(:remove_method, group)
8-
end
9-
Yabeda.remove_instance_variable(:@groups)
10-
end
11-
12-
if Yabeda.instance_variable_defined?(:@metrics)
13-
Yabeda.instance_variable_get(:@metrics).each_key do |metric|
14-
Yabeda.singleton_class.send(:remove_method, metric)
15-
end
16-
Yabeda.remove_instance_variable(:@metrics)
17-
end
18-
19-
::Yabeda.default_tags.clear
20-
end
21-
224
describe ".group" do
235
context "without block" do
246
before do
@@ -125,5 +107,40 @@
125107

126108
it { is_expected.to eq(environment: "test") }
127109
end
110+
111+
context "with a specified group that does not exist" do
112+
before do
113+
Yabeda.configure { default_tag :environment, "test", group: :missing_group }
114+
Yabeda.configure!
115+
end
116+
117+
it "creates the group" do
118+
expect(Yabeda.groups[:missing_group]).to be_a(Yabeda::Group)
119+
end
120+
121+
it "defines the default tag" do
122+
expect(Yabeda.groups[:missing_group].default_tags).to eq(environment: "test")
123+
end
124+
end
125+
126+
context "when specified group is defined after default_tag" do
127+
before do
128+
Yabeda.configure { default_tag :environment, "test", group: :missing_group }
129+
Yabeda.configure do
130+
group :missing_group
131+
default_tag :key, "value"
132+
gauge :test_gauge, comment: "..."
133+
end
134+
Yabeda.configure!
135+
end
136+
137+
it "defines all the tags" do
138+
expect(Yabeda.groups[:missing_group].default_tags).to eq(environment: "test", key: "value")
139+
end
140+
141+
it "test_gauge has all the tags defined" do
142+
expect(Yabeda.missing_group.test_gauge.tags).to eq(%i[environment key])
143+
end
144+
end
128145
end
129146
end

spec/yabeda/gauge_spec.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
gauge :test_gauge
1515
end
1616
Yabeda.configure!
17-
allow(Yabeda::Tags).to receive(:build).with(tags).and_return(built_tags)
17+
allow(Yabeda::Tags).to receive(:build).with(tags, anything).and_return(built_tags)
1818
::Yabeda.register_adapter(:test_adapter, adapter)
1919
end
2020

spec/yabeda/group_spec.rb

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe Yabeda::Group do
4+
let(:name) { nil }
5+
6+
let(:group) { described_class.new(name) }
7+
8+
before do
9+
Yabeda.groups[name] = group
10+
end
11+
12+
after { Yabeda.reset! }
13+
14+
describe "default tags" do
15+
context "when on the top level group" do
16+
it "is an empty by default" do
17+
expect(group.default_tags).to be_empty
18+
end
19+
end
20+
21+
context "when within a named group" do
22+
let(:name) { :group1 }
23+
24+
it "includes top level default_tags" do
25+
Yabeda.default_tag :tag, "default"
26+
expect(group.default_tags).to eq(tag: "default")
27+
end
28+
29+
it "overrides top level default_tags" do
30+
Yabeda.default_tag :tag, "default"
31+
group.default_tag :tag, "overridden"
32+
expect(Yabeda.groups[nil].default_tags).to eq(tag: "default")
33+
expect(group.default_tags).to eq(tag: "overridden")
34+
end
35+
end
36+
end
37+
end

spec/yabeda/histogram_spec.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
histogram :test_histogram, buckets: [1, 10, 100]
1515
end
1616
Yabeda.configure!
17-
allow(Yabeda::Tags).to receive(:build).with(tags).and_return(built_tags)
17+
allow(Yabeda::Tags).to receive(:build).with(tags, anything).and_return(built_tags)
1818
::Yabeda.register_adapter(:test_adapter, adapter)
1919
end
2020

0 commit comments

Comments
 (0)