yabeda 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cbfcaaa322333f0bf774953f4ef40081a43f3d8dc89a3aec426397c174f8e5ef
4
- data.tar.gz: 4f4dd1f0fc20cf59b72c26308861dfc7b439d271cdaebcd8441190a419e3b3e6
3
+ metadata.gz: e1c0a1298576c5e12a8c7acd4cda088fab78b155e846d6aa0c8865638488a5ab
4
+ data.tar.gz: ef0ba94b3fb39fd022e1aeb272104f46729f22aa10da6ba338cd2d91492883bd
5
5
  SHA512:
6
- metadata.gz: d43a6452648112c376b29dce66942ad6a491f4b0d54794cd42f5dd874d8db7c005f3e65d883707b53a6bbc4ffc8f961526a8c757acb877e89239d527d5205b4b
7
- data.tar.gz: c42902e5e1b7f410170f7dfec0594de246ec5aebaa989b33786cec835cfaa5e99308155b35f906e4bf495589c3d0b03c1cc40fcaa88ddb59b43b1abc5ce187e4
6
+ metadata.gz: 0effc951709196bbfb6f266211710704645502997c0ee8864d2514d204467a7206eb4550fde040a00d15dde811d85e0ed6eb237939417a6ce41bbd4c310fe6b8
7
+ data.tar.gz: f74760379893fade4bc37635dbbbd0f851a52635ebc50a210eed5531ca2e4b3a2a225cfdbd3d63e38af9dded14d6eaa518f853ff3b7ec63f7ae6ffb919969589
@@ -21,6 +21,7 @@ jobs:
21
21
  include:
22
22
  - ruby: head
23
23
  optional: true
24
+ - ruby: "4.0"
24
25
  - ruby: "3.4"
25
26
  - ruby: "3.3"
26
27
  - ruby: "3.2"
data/CHANGELOG.md CHANGED
@@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ## 0.16.0 - 2026-03-12
11
+
12
+ ### Added
13
+
14
+ - Ability to include/exclude only certain metrics in a group for export via adapter. [@sobrinho][] in [#49](https://github.com/yabeda-rb/yabeda/pull/49)
15
+
16
+ ```ruby
17
+ Yabeda.configure do
18
+ group :internal do
19
+ only :foo
20
+ end
21
+ end
22
+ ```
23
+
24
+ To exclude certain metrics from a group, use the `except` option:
25
+
26
+ ```ruby
27
+ Yabeda.configure do
28
+ group :internal do
29
+ adapter :prometheus
30
+ except :bar, :baz
31
+ end
32
+ end
33
+ ```
34
+
35
+ ## 0.15.0 - 2026-03-04
36
+
37
+ ### Fixed
38
+
39
+ - Concurrency issues with metrics' increment/decrement helper methods. [@cconstantine-onesignal][] in [#45](https://github.com/yabeda-rb/yabeda/pull/45)
40
+
10
41
  ## 0.14.0 - 2025-09-10
11
42
 
12
43
  ### Added
@@ -215,3 +246,5 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
215
246
  [@Keallar]: https://github.com/Keallar "Eugene Lysanskiy"
216
247
  [@jbockler]: https://github.com/jbockler "Josch Bockler"
217
248
  [@killondark]: https://github.com/killondark "Alexander Marychev"
249
+ [@cconstantine-onesignal]: https://github.com/cconstantine-onesignal "Chris Constantine"
250
+ [@sobrinho]: https://github.com/sobrinho "Gabriel Sobrinho"
data/Gemfile CHANGED
@@ -13,8 +13,7 @@ group :development, :test do
13
13
  gem "yard"
14
14
  gem "yard-dry-initializer"
15
15
 
16
- gem "pry"
17
- gem "pry-byebug", platform: :mri
16
+ gem "debug"
18
17
 
19
18
  gem "rubocop", "~> 1.0", require: false
20
19
  gem "rubocop-rspec", require: false
data/README.md CHANGED
@@ -158,6 +158,8 @@ These are developed and maintained by other awesome folks:
158
158
  - [yabeda-shoryuken](https://github.com/retsef/yabeda-shoryuken) — metrics for [Shoryuken](https://github.com/ruby-shoryuken/shoryuken) jobs execution message queues.
159
159
  - [yabeda-rack-ratelimit](https://github.com/basecamp/yabeda-rack-ratelimit) — metrics for [Rack::Ratelimit](https://github.com/jeremy/rack-ratelimit)
160
160
  - [yabeda-hanami](https://github.com/mlibrary/yabeda-hanami) — metrics for [Hanami](https://hanamirb.org/) The web, with simplicity.
161
+ - [yabeda-jemalloc](http://github.com/jondavidschober/yabeda-jemalloc) - metrics for [Jemalloc](https://jemalloc.net/) via the `stats` FFI
162
+ - [yabeda-rack-queue](https://github.com/speedshop/yabeda-rack-queue) - metrics for reporting queue times of Rack applications.
161
163
  - _…and more! You can write your own adapter and open a pull request to add it into this list._
162
164
 
163
165
  ## Configuration
@@ -260,6 +262,53 @@ Yabeda.configure do
260
262
  end
261
263
  ```
262
264
 
265
+ ## Filtering metrics
266
+
267
+ You can control which metrics and groups are exposed for a given group using either an allow-list (`only`) or a deny-list (`except`).
268
+
269
+ To permit only specific metrics for a group, use the `only` option:
270
+
271
+ ```ruby
272
+ Yabeda.configure do
273
+ group :internal do
274
+ only :foo
275
+
276
+ counter :foo
277
+ gauge :bar
278
+ end
279
+ end
280
+ ```
281
+
282
+ To exclude certain metrics from a group, use the `except` option:
283
+
284
+ ```ruby
285
+ Yabeda.configure do
286
+ group :internal do
287
+ adapter :prometheus
288
+
289
+ except :bar
290
+
291
+ counter :foo
292
+ gauge :bar
293
+ end
294
+ end
295
+ ```
296
+
297
+ This mechanism is especially useful for disabling unwanted metrics from third-party plugins.
298
+
299
+ For example, [yabeda-activerecord](https://github.com/yabeda-rb/yabeda-activerecord) doesn't provide its own configuration to turn off metrics with high cardinality, such as `queries_total` and `query_duration`. You can disable them like this:
300
+
301
+ ```ruby
302
+ require 'yabeda'
303
+ require 'yabeda-activerecord'
304
+
305
+ Yabeda.configure do
306
+ group :activerecord do
307
+ except :queries_total, :query_duration
308
+ end
309
+ end
310
+ ```
311
+
263
312
  ## Roadmap (aka TODO or Help wanted)
264
313
 
265
314
  - Ability to change metric settings for individual adapters
@@ -29,6 +29,13 @@ module Yabeda
29
29
  raise NotImplementedError, "#{self.class} doesn't support setting gauges"
30
30
  end
31
31
 
32
+ # Optional method that adapters can implement for native gauge increment/decrement
33
+ def perform_gauge_increment!(_gauge, _tags, _increment)
34
+ # used to indicate adapter does not implement this method. return a non-nil value
35
+ # in your adapter to indicate it is supported
36
+ nil
37
+ end
38
+
32
39
  def register_histogram!(_metric)
33
40
  raise NotImplementedError, "#{self.class} doesn't support histograms as metric type!"
34
41
  end
@@ -10,15 +10,11 @@ module Yabeda
10
10
  def increment(*args)
11
11
  tags, by = self.class.parse_args(*args)
12
12
  all_tags = ::Yabeda::Tags.build(tags, group)
13
- values[all_tags] += by
13
+ value = increment_value(all_tags, by: by)
14
14
  adapters.each_value do |adapter|
15
15
  adapter.perform_counter_increment!(self, all_tags, by)
16
16
  end
17
- values[all_tags]
18
- end
19
-
20
- def values
21
- @values ||= Concurrent::Hash.new(0)
17
+ value
22
18
  end
23
19
 
24
20
  # @api private
@@ -12,6 +12,7 @@ require "yabeda/dsl/metric_builder"
12
12
  module Yabeda
13
13
  # DSL for ease of work with Yabeda
14
14
  module DSL
15
+ # rubocop:disable Metrics/ModuleLength
15
16
  module ClassMethods
16
17
  # Block for grouping and simplifying configuration of related metrics
17
18
  def configure(&block)
@@ -110,6 +111,26 @@ module Yabeda
110
111
  ensure @adapter_names = nil
111
112
  end
112
113
 
114
+ def only(*metric_names, group: @group)
115
+ if group
116
+ Yabeda.groups[group] ||= Yabeda::Group.new(group)
117
+ Yabeda.groups[group].only(*metric_names)
118
+ else
119
+ raise ConfigurationError, "Yabeda.only should be called either inside group declaration " \
120
+ "or should have group name provided. No metric group provided."
121
+ end
122
+ end
123
+
124
+ def except(*metric_names, group: @group)
125
+ if group
126
+ Yabeda.groups[group] ||= Yabeda::Group.new(group)
127
+ Yabeda.groups[group].except(*metric_names)
128
+ else
129
+ raise ConfigurationError, "Yabeda.except should be called either inside group declaration " \
130
+ "or should have group name provided. No metric group provided."
131
+ end
132
+ end
133
+
113
134
  def include_group(group)
114
135
  raise ConfigurationError, "Adapter limitation can't be defined without of group name" unless group
115
136
 
@@ -142,4 +163,5 @@ module Yabeda
142
163
  end
143
164
  end
144
165
  end
166
+ # rubocop:enable Metrics/ModuleLength
145
167
  end
data/lib/yabeda/gauge.rb CHANGED
@@ -5,7 +5,8 @@ module Yabeda
5
5
  class Gauge < Metric
6
6
  def set(tags, value)
7
7
  all_tags = ::Yabeda::Tags.build(tags, group)
8
- values[all_tags] = value
8
+ atomic_value = values[all_tags] ||= Concurrent::Atom.new(0)
9
+ atomic_value.swap { |_| value }
9
10
  adapters.each_value do |adapter|
10
11
  adapter.perform_gauge_set!(self, all_tags, value)
11
12
  end
@@ -18,7 +19,14 @@ module Yabeda
18
19
  # @param by [Integer] increment value
19
20
  def increment(*args)
20
21
  tags, by = Counter.parse_args(*args)
21
- set(tags, get(tags).to_i + by)
22
+ all_tags = ::Yabeda::Tags.build(tags, group)
23
+ next_value = increment_value(all_tags, by: by)
24
+ adapters.each_value do |adapter|
25
+ if adapter.perform_gauge_increment!(self, all_tags, by).nil?
26
+ adapter.perform_gauge_set!(self, all_tags, next_value)
27
+ end
28
+ end
29
+ next_value
22
30
  end
23
31
 
24
32
  # @overload decrement(tags = {}, by: 1)
@@ -27,7 +35,14 @@ module Yabeda
27
35
  # @param by [Integer] decrement value
28
36
  def decrement(*args)
29
37
  tags, by = Counter.parse_args(*args)
30
- set(tags, get(tags).to_i - by)
38
+ all_tags = ::Yabeda::Tags.build(tags, group)
39
+ next_value = increment_value(all_tags, by: -by)
40
+ adapters.each_value do |adapter|
41
+ if adapter.perform_gauge_increment!(self, all_tags, -by).nil?
42
+ adapter.perform_gauge_set!(self, all_tags, next_value)
43
+ end
44
+ end
45
+ next_value
31
46
  end
32
47
  end
33
48
  end
data/lib/yabeda/group.rb CHANGED
@@ -26,6 +26,20 @@ module Yabeda
26
26
  @adapter.push(*adapter_names)
27
27
  end
28
28
 
29
+ def only(*metric_names)
30
+ return @only_list if metric_names.empty?
31
+
32
+ @only_list ||= Concurrent::Array.new
33
+ @only_list.push(*metric_names.map(&:to_sym))
34
+ end
35
+
36
+ def except(*metric_names)
37
+ return @except_list if metric_names.empty?
38
+
39
+ @except_list ||= Concurrent::Array.new
40
+ @except_list.push(*metric_names.map(&:to_sym))
41
+ end
42
+
29
43
  def register_metric(metric)
30
44
  define_singleton_method(metric.name) { metric }
31
45
  end
data/lib/yabeda/metric.rb CHANGED
@@ -17,14 +17,11 @@ module Yabeda
17
17
  # rubocop:disable Layout/LineLength
18
18
  option :adapter, optional: true, comment: "Monitoring system adapter to register metric in and report metric values to (other adapters won't be used)"
19
19
  # rubocop:enable Layout/LineLength
20
+ option :values, optional: true, default: proc { Concurrent::Hash.new }
20
21
 
21
22
  # Returns the value for the given label set
22
23
  def get(labels = {})
23
- values[::Yabeda::Tags.build(labels, group)]
24
- end
25
-
26
- def values
27
- @values ||= Concurrent::Hash.new
24
+ @values[::Yabeda::Tags.build(labels, group)]&.value
28
25
  end
29
26
 
30
27
  # Returns allowed tags for metric (with account for global and group-level +default_tags+)
@@ -55,12 +52,25 @@ module Yabeda
55
52
  end
56
53
  end
57
54
 
58
- # Redefined option reader to get group-level adapter if not set on metric level
59
- # @api private
55
+ # Redefined option reader to get null adapter if metric is excluded from the group or from the only list in group,
56
+ # group-level adapter if not set on metric level, and adapter on metric level if set
57
+ # @api privatee
60
58
  def adapter
61
- return ::Yabeda.groups[group]&.adapter if @adapter == Dry::Initializer::UNDEFINED
59
+ group = ::Yabeda.groups[self.group]
60
+
61
+ return :null_adapter if group&.except&.include?(name.to_sym) ||
62
+ (group&.only && !group.only.include?(name.to_sym))
63
+
64
+ return group&.adapter if @adapter == Dry::Initializer::UNDEFINED
62
65
 
63
66
  super
64
67
  end
68
+
69
+ # Atomically increment the stored value, assumed to be given all labels, including group labels
70
+ # @api private
71
+ def increment_value(labels = {}, by: 1)
72
+ atomic_value = values[labels] ||= Concurrent::Atom.new(0)
73
+ atomic_value.swap { |prev| prev + by }
74
+ end
65
75
  end
66
76
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_adapter"
4
+
5
+ module Yabeda
6
+ # Adapter that discards all metrics. Use when you want to disable metric export
7
+ # (e.g. in development or when no monitoring backend is configured).
8
+ class NullAdapter < BaseAdapter
9
+ def register_counter!(_metric); end
10
+
11
+ def perform_counter_increment!(_counter, _tags, _increment); end
12
+
13
+ def register_gauge!(_metric); end
14
+
15
+ def perform_gauge_set!(_gauge, _tags, _value); end
16
+
17
+ def register_histogram!(_metric); end
18
+
19
+ def perform_histogram_measure!(_histogram, _tags, _value); end
20
+
21
+ def register_summary!(_metric); end
22
+
23
+ def perform_summary_observe!(_summary, _tags, _value); end
24
+ end
25
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Yabeda
4
- VERSION = "0.14.0"
4
+ VERSION = "0.16.0"
5
5
  end
data/lib/yabeda.rb CHANGED
@@ -8,6 +8,7 @@ require "yabeda/config"
8
8
  require "yabeda/dsl"
9
9
  require "yabeda/tags"
10
10
  require "yabeda/errors"
11
+ require "yabeda/null_adapter"
11
12
 
12
13
  # Extendable framework for collecting and exporting metrics from Ruby apps
13
14
  module Yabeda
@@ -32,7 +33,9 @@ module Yabeda
32
33
 
33
34
  # @return [Hash<Symbol, Yabeda::BaseAdapter>] All loaded adapters
34
35
  def adapters
35
- @adapters ||= Concurrent::Hash.new
36
+ @adapters ||= Concurrent::Hash.new.tap do |hash|
37
+ hash[:null_adapter] = Yabeda::NullAdapter.new
38
+ end
36
39
  end
37
40
 
38
41
  # @return [Array<Proc>] All collectors for periodical retrieving of metrics
@@ -138,7 +141,7 @@ module Yabeda
138
141
  # @api private
139
142
  def reset!
140
143
  default_tags.clear
141
- adapters.clear
144
+ @adapters = nil
142
145
  groups.each_key { |group| singleton_class.send(:remove_method, group) if group && respond_to?(group) }
143
146
  @groups = nil
144
147
  metrics.each_key { |metric| singleton_class.send(:remove_method, metric) if respond_to?(metric) }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yabeda
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.0
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey Novikov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-09-10 00:00:00.000000000 Z
11
+ date: 2026-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: anyway_config
@@ -96,6 +96,7 @@ files:
96
96
  - lib/yabeda/group.rb
97
97
  - lib/yabeda/histogram.rb
98
98
  - lib/yabeda/metric.rb
99
+ - lib/yabeda/null_adapter.rb
99
100
  - lib/yabeda/railtie.rb
100
101
  - lib/yabeda/rspec.rb
101
102
  - lib/yabeda/rspec/base_matcher.rb