yabeda 0.12.0 → 0.13.1
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 +4 -4
- data/.github/workflows/lint.yml +8 -3
- data/.github/workflows/{build-release.yml → release.yml} +9 -5
- data/.github/workflows/test.yml +7 -16
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +50 -1
- data/Gemfile +2 -2
- data/README.md +37 -10
- data/lib/yabeda/counter.rb +27 -2
- data/lib/yabeda/dsl/class_methods.rb +11 -1
- data/lib/yabeda/gauge.rb +13 -3
- data/lib/yabeda/group.rb +7 -0
- data/lib/yabeda/histogram.rb +2 -2
- data/lib/yabeda/metric.rb +30 -1
- data/lib/yabeda/rspec/base_matcher.rb +17 -6
- data/lib/yabeda/rspec/increment_yabeda_counter.rb +14 -3
- data/lib/yabeda/rspec/measure_yabeda_histogram.rb +16 -2
- data/lib/yabeda/rspec/observe_yabeda_summary.rb +16 -2
- data/lib/yabeda/rspec/update_yabeda_gauge.rb +16 -2
- data/lib/yabeda/summary.rb +2 -2
- data/lib/yabeda/version.rb +1 -1
- data/lib/yabeda.rb +12 -6
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e8a48fc237f75ffdb834af3b14fe7d991cb1f65a37d528a0139d2cc2895d3e6b
|
4
|
+
data.tar.gz: f3b316975ff26a048e9a8361c93b164591f3b3add12bea20a01b0f47316519f2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e9f252ca373fbf287c6ce674180e7cb58c35177951e903767722e8ad396bbee7cbebff73c6e9fcb7e2e43e30c6599c0aea5a53a7f4f546be28382a173ce5c856
|
7
|
+
data.tar.gz: 43fff38b19ef0fcde369eb4f43f78049543949d54d9f23d130a7e895ed7e10d62a6612f9328d1e9128cf1354578423f6992d56ee354063614c47f350cd960682
|
data/.github/workflows/lint.yml
CHANGED
@@ -3,7 +3,9 @@ name: Lint Ruby
|
|
3
3
|
on:
|
4
4
|
push:
|
5
5
|
branches:
|
6
|
-
-
|
6
|
+
- '**'
|
7
|
+
tags-ignore:
|
8
|
+
- 'v*'
|
7
9
|
paths:
|
8
10
|
- "gemfiles/*"
|
9
11
|
- "Gemfile"
|
@@ -20,12 +22,15 @@ on:
|
|
20
22
|
|
21
23
|
jobs:
|
22
24
|
rubocop:
|
25
|
+
# Skip running tests for local pull requests (use push event instead), run only for foreign ones
|
26
|
+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.owner.login != github.event.pull_request.base.repo.owner.login
|
27
|
+
name: RuboCop
|
23
28
|
runs-on: ubuntu-latest
|
24
29
|
steps:
|
25
|
-
- uses: actions/checkout@
|
30
|
+
- uses: actions/checkout@v4
|
26
31
|
- uses: ruby/setup-ruby@v1
|
27
32
|
with:
|
28
|
-
ruby-version: 3.
|
33
|
+
ruby-version: "3.3"
|
29
34
|
bundler-cache: true
|
30
35
|
- name: Lint Ruby code with RuboCop
|
31
36
|
run: |
|
@@ -1,4 +1,4 @@
|
|
1
|
-
name: Build and release gem
|
1
|
+
name: Build and release gem
|
2
2
|
|
3
3
|
on:
|
4
4
|
push:
|
@@ -8,13 +8,17 @@ on:
|
|
8
8
|
jobs:
|
9
9
|
release:
|
10
10
|
runs-on: ubuntu-latest
|
11
|
+
permissions:
|
12
|
+
contents: write
|
13
|
+
id-token: write
|
14
|
+
packages: write
|
11
15
|
steps:
|
12
|
-
- uses: actions/checkout@
|
16
|
+
- uses: actions/checkout@v4
|
13
17
|
with:
|
14
18
|
fetch-depth: 0 # Fetch current tag as annotated. See https://github.com/actions/checkout/issues/290
|
15
19
|
- uses: ruby/setup-ruby@v1
|
16
20
|
with:
|
17
|
-
ruby-version:
|
21
|
+
ruby-version: "3.3"
|
18
22
|
- name: "Extract data from tag: version, message, body"
|
19
23
|
id: tag
|
20
24
|
run: |
|
@@ -75,8 +79,8 @@ jobs:
|
|
75
79
|
GEM_HOST_API_KEY: Bearer ${{ secrets.GITHUB_TOKEN }}
|
76
80
|
run: |
|
77
81
|
gem push yabeda-${{ steps.tag.outputs.version }}.gem --host https://rubygems.pkg.github.com/${{ github.repository_owner }}
|
82
|
+
- name: Configure RubyGems Credentials
|
83
|
+
uses: rubygems/configure-rubygems-credentials@main
|
78
84
|
- name: Publish to RubyGems
|
79
|
-
env:
|
80
|
-
GEM_HOST_API_KEY: "${{ secrets.RUBYGEMS_API_KEY }}"
|
81
85
|
run: |
|
82
86
|
gem push yabeda-${{ steps.tag.outputs.version }}.gem
|
data/.github/workflows/test.yml
CHANGED
@@ -11,32 +11,23 @@ on:
|
|
11
11
|
jobs:
|
12
12
|
test:
|
13
13
|
name: "Ruby ${{ matrix.ruby }}"
|
14
|
+
# Skip running tests for local pull requests (use push event instead), run only for foreign ones
|
15
|
+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.owner.login != github.event.pull_request.base.repo.owner.login
|
14
16
|
runs-on: ubuntu-latest
|
15
17
|
strategy:
|
16
18
|
fail-fast: false
|
17
19
|
matrix:
|
18
20
|
include:
|
21
|
+
- ruby: "3.3"
|
19
22
|
- ruby: "3.2"
|
20
23
|
- ruby: "3.1"
|
21
24
|
- ruby: "3.0"
|
22
25
|
- ruby: "2.7"
|
23
|
-
container:
|
24
|
-
image: ruby:${{ matrix.ruby }}
|
25
|
-
env:
|
26
|
-
CI: true
|
27
26
|
steps:
|
28
|
-
- uses: actions/checkout@
|
29
|
-
- uses:
|
27
|
+
- uses: actions/checkout@v4
|
28
|
+
- uses: ruby/setup-ruby@v1
|
30
29
|
with:
|
31
|
-
|
32
|
-
|
33
|
-
restore-keys: |
|
34
|
-
bundle-${{ matrix.ruby }}-${{ hashFiles('**/*.gemspec') }}-${{ hashFiles('**/Gemfile') }}
|
35
|
-
bundle-${{ matrix.ruby }}-
|
36
|
-
- name: Bundle install
|
37
|
-
run: |
|
38
|
-
bundle config path vendor/bundle
|
39
|
-
bundle install
|
40
|
-
bundle update
|
30
|
+
ruby-version: ${{ matrix.ruby }}
|
31
|
+
bundler-cache: true
|
41
32
|
- name: Run RSpec
|
42
33
|
run: bundle exec rspec
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,53 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
7
7
|
|
8
8
|
## Unreleased
|
9
9
|
|
10
|
+
## 0.13.1 - 2024-10-11
|
11
|
+
|
12
|
+
### Fixed
|
13
|
+
|
14
|
+
- Compatibility with Ruby 2.x, broken in 0.13.0 due to differences of keywords handling in Ruby 2.x for case when method has arguments with default values. [@Envek]
|
15
|
+
|
16
|
+
## 0.13.0 - 2024-10-02
|
17
|
+
|
18
|
+
### Added
|
19
|
+
|
20
|
+
- Ability to limit some metrics to specific adapters. [#37](https://github.com/yabeda-rb/yabeda/pull/37) by [@Keallar] and [@Envek]
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
Yabeda.configure do
|
24
|
+
group :cloud do
|
25
|
+
adapter :newrelic, :datadog
|
26
|
+
|
27
|
+
counter :foo
|
28
|
+
end
|
29
|
+
|
30
|
+
counter :bar, adapter: :prometheus
|
31
|
+
end
|
32
|
+
```
|
33
|
+
|
34
|
+
- Multiple expectations in RSpec matchers. [@Envek]
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
expect { whatever }.to increment_yabeda_counter(:my_counter).with(
|
38
|
+
{ tag: "foo" } => 1,
|
39
|
+
{ tag: "bar" } => (be >= 42),
|
40
|
+
)
|
41
|
+
```
|
42
|
+
|
43
|
+
### Changed
|
44
|
+
|
45
|
+
- Don't require to provide tags for counters and histograms, use empty tags (`{}`) by default. See discussion at [#26](https://github.com/yabeda-rb/yabeda/issues/26). [@Envek]
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
Yabeda.foo.increment
|
49
|
+
# same as
|
50
|
+
Yabeda.foo.increment({}, by: 1)
|
51
|
+
```
|
52
|
+
|
53
|
+
### Fixed
|
54
|
+
|
55
|
+
- Railtie loading to prevent calling methods that have not yet been defined. [#38](https://github.com/yabeda-rb/yabeda/pull/38) by [@bibendi].
|
56
|
+
|
10
57
|
## 0.12.0 - 2023-07-28
|
11
58
|
|
12
59
|
### Added
|
@@ -37,7 +84,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
37
84
|
|
38
85
|
### Changed
|
39
86
|
|
40
|
-
- Adapters now should use method `Yabeda.collect!` instead of manual calling of every collector block.
|
87
|
+
- Adapters now should use method `Yabeda.collect!` instead of manual calling of every collector block.
|
41
88
|
|
42
89
|
## 0.9.0 - 2021-05-07
|
43
90
|
|
@@ -140,3 +187,5 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
140
187
|
[@dsalahutdinov]: https://github.com/dsalahutdinov "Dmitry Salahutdinov"
|
141
188
|
[@asusikov]: https://github.com/asusikov "Alexander Susikov"
|
142
189
|
[@liaden]: https://github.com/liaden "Joel Johnson"
|
190
|
+
[@bibendi]: https://github.com/bibendi "Misha Merkushin"
|
191
|
+
[@Keallar]: https://github.com/Keallar "Eugene Lysanskiy"
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -130,6 +130,7 @@ These are developed and maintained by other awesome folks:
|
|
130
130
|
|
131
131
|
- [Statsd](https://github.com/asusikov/yabeda-statsd)
|
132
132
|
- [AWS CloudWatch](https://github.com/retsef/yabeda-cloudwatch)
|
133
|
+
- [Honeybadger Insights](https://github.com/honeybadger-io/yabeda-honeybadger_insights)
|
133
134
|
- _…and more! You can write your own adapter and open a pull request to add it into this list._
|
134
135
|
|
135
136
|
## Available plugins to collect metrics
|
@@ -156,6 +157,7 @@ These are developed and maintained by other awesome folks:
|
|
156
157
|
- [yabeda-activejob](https://github.com/Fullscript/yabeda-activejob) — backend-agnostic metrics for background jobs.
|
157
158
|
- [yabeda-shoryuken](https://github.com/retsef/yabeda-shoryuken) — metrics for [Shoryuken](https://github.com/ruby-shoryuken/shoryuken) jobs execution message queues.
|
158
159
|
- [yabeda-rack-ratelimit](https://github.com/basecamp/yabeda-rack-ratelimit) — metrics for [Rack::Ratelimit](https://github.com/jeremy/rack-ratelimit)
|
160
|
+
- [yabeda-hanami](https://github.com/mlibrary/yabeda-hanami) — metrics for [Hanami](https://hanamirb.org/) The web, with simplicity.
|
159
161
|
- _…and more! You can write your own adapter and open a pull request to add it into this list._
|
160
162
|
|
161
163
|
## Configuration
|
@@ -210,6 +212,41 @@ expect { subject }.to \
|
|
210
212
|
with(be_between(0.005, 0.05))
|
211
213
|
```
|
212
214
|
|
215
|
+
You also can specify multiple tags and their expected values in `with`:
|
216
|
+
|
217
|
+
```ruby
|
218
|
+
expect { whatever }.to increment_yabeda_counter(:my_counter).with(
|
219
|
+
{ tag: "foo" } => 1,
|
220
|
+
{ tag: "bar" } => (be >= 42),
|
221
|
+
)
|
222
|
+
```
|
223
|
+
|
224
|
+
## Advanced usage
|
225
|
+
|
226
|
+
### Limiting metrics and groups to specific adapters
|
227
|
+
|
228
|
+
You can limit, which metrics and groups should be available for specific adapter:
|
229
|
+
|
230
|
+
```ruby
|
231
|
+
Yabeda.configure do
|
232
|
+
group :internal do
|
233
|
+
adapter :prometheus
|
234
|
+
|
235
|
+
counter :foo
|
236
|
+
gauge :bar
|
237
|
+
end
|
238
|
+
|
239
|
+
group :cloud do
|
240
|
+
adapter :newrelic
|
241
|
+
|
242
|
+
counter :baz
|
243
|
+
end
|
244
|
+
|
245
|
+
counter :qux, adapter: :prometheus
|
246
|
+
end
|
247
|
+
```
|
248
|
+
|
249
|
+
|
213
250
|
## Roadmap (aka TODO or Help wanted)
|
214
251
|
|
215
252
|
- Ability to change metric settings for individual adapters
|
@@ -222,16 +259,6 @@ expect { subject }.to \
|
|
222
259
|
end
|
223
260
|
```
|
224
261
|
|
225
|
-
- Ability to route some metrics only for given adapter:
|
226
|
-
|
227
|
-
```rb
|
228
|
-
adapter :prometheus do
|
229
|
-
include_group :sidekiq
|
230
|
-
end
|
231
|
-
```
|
232
|
-
|
233
|
-
|
234
|
-
|
235
262
|
## Development
|
236
263
|
|
237
264
|
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.
|
data/lib/yabeda/counter.rb
CHANGED
@@ -3,10 +3,15 @@
|
|
3
3
|
module Yabeda
|
4
4
|
# Growing-only counter
|
5
5
|
class Counter < Metric
|
6
|
-
|
6
|
+
# @overload increment(tags = {}, by: 1)
|
7
|
+
# Increment the counter for given set of tags by the given increment value
|
8
|
+
# @param tags Hash{Symbol=>#to_s} tags to identify the counter
|
9
|
+
# @param by [Integer] strictly positive increment value
|
10
|
+
def increment(*args)
|
11
|
+
tags, by = self.class.parse_args(*args)
|
7
12
|
all_tags = ::Yabeda::Tags.build(tags, group)
|
8
13
|
values[all_tags] += by
|
9
|
-
|
14
|
+
adapters.each_value do |adapter|
|
10
15
|
adapter.perform_counter_increment!(self, all_tags, by)
|
11
16
|
end
|
12
17
|
values[all_tags]
|
@@ -15,5 +20,25 @@ module Yabeda
|
|
15
20
|
def values
|
16
21
|
@values ||= Concurrent::Hash.new(0)
|
17
22
|
end
|
23
|
+
|
24
|
+
# @api private
|
25
|
+
# rubocop:disable Metrics/MethodLength
|
26
|
+
def self.parse_args(*args)
|
27
|
+
case args.size
|
28
|
+
when 0 # increment()
|
29
|
+
[EMPTY_TAGS, 1]
|
30
|
+
when 1 # increment(by: 5) or increment(tags)
|
31
|
+
if args[0].key?(:by)
|
32
|
+
[EMPTY_TAGS, args.fetch(:by)]
|
33
|
+
else
|
34
|
+
[args[0], 1]
|
35
|
+
end
|
36
|
+
when 2 # increment(tags, by: 5)
|
37
|
+
[args[0], args[1].fetch(:by, 1)]
|
38
|
+
else
|
39
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0..2)"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
# rubocop:enable Metrics/MethodLength
|
18
43
|
end
|
19
44
|
end
|
@@ -92,6 +92,16 @@ module Yabeda
|
|
92
92
|
Thread.current[:yabeda_temporary_tags] ||= {}
|
93
93
|
end
|
94
94
|
|
95
|
+
# Limit all group metrics to specific adapters only
|
96
|
+
#
|
97
|
+
# @param adapter_names [Array<Symbol>] Names of adapters to use
|
98
|
+
def adapter(*adapter_names, group: @group)
|
99
|
+
raise ConfigurationError, "Adapter limitation can't be defined outside of group" unless group
|
100
|
+
|
101
|
+
Yabeda.groups[group] ||= Yabeda::Group.new(group)
|
102
|
+
Yabeda.groups[group].adapter(*adapter_names)
|
103
|
+
end
|
104
|
+
|
95
105
|
private
|
96
106
|
|
97
107
|
def register_metric(metric)
|
@@ -99,7 +109,7 @@ module Yabeda
|
|
99
109
|
::Yabeda.define_singleton_method(name) { metric }
|
100
110
|
::Yabeda.metrics[name] = metric
|
101
111
|
register_group_for(metric) if metric.group
|
102
|
-
|
112
|
+
metric.adapters.each_value { |adapter| adapter.register!(metric) } if ::Yabeda.configured?
|
103
113
|
metric
|
104
114
|
end
|
105
115
|
|
data/lib/yabeda/gauge.rb
CHANGED
@@ -6,17 +6,27 @@ module Yabeda
|
|
6
6
|
def set(tags, value)
|
7
7
|
all_tags = ::Yabeda::Tags.build(tags, group)
|
8
8
|
values[all_tags] = value
|
9
|
-
|
9
|
+
adapters.each_value do |adapter|
|
10
10
|
adapter.perform_gauge_set!(self, all_tags, value)
|
11
11
|
end
|
12
12
|
value
|
13
13
|
end
|
14
14
|
|
15
|
-
|
15
|
+
# @overload increment(tags = {}, by: 1)
|
16
|
+
# Convenience method to increment current gauge value for given set of tags by the given increment value
|
17
|
+
# @param tags Hash{Symbol=>#to_s} tags to identify the gauge
|
18
|
+
# @param by [Integer] increment value
|
19
|
+
def increment(*args)
|
20
|
+
tags, by = Counter.parse_args(*args)
|
16
21
|
set(tags, get(tags).to_i + by)
|
17
22
|
end
|
18
23
|
|
19
|
-
|
24
|
+
# @overload decrement(tags = {}, by: 1)
|
25
|
+
# Convenience method to decrement current gauge value for given set of tags by the given decrement value
|
26
|
+
# @param tags Hash{Symbol=>#to_s} tags to identify the gauge
|
27
|
+
# @param by [Integer] decrement value
|
28
|
+
def decrement(*args)
|
29
|
+
tags, by = Counter.parse_args(*args)
|
20
30
|
set(tags, get(tags).to_i - by)
|
21
31
|
end
|
22
32
|
end
|
data/lib/yabeda/group.rb
CHANGED
@@ -19,6 +19,13 @@ module Yabeda
|
|
19
19
|
@default_tags[key] = value
|
20
20
|
end
|
21
21
|
|
22
|
+
def adapter(*adapter_names)
|
23
|
+
return @adapter if adapter_names.empty?
|
24
|
+
|
25
|
+
@adapter ||= Concurrent::Array.new
|
26
|
+
@adapter.push(*adapter_names)
|
27
|
+
end
|
28
|
+
|
22
29
|
def register_metric(metric)
|
23
30
|
define_singleton_method(metric.name) { metric }
|
24
31
|
end
|
data/lib/yabeda/histogram.rb
CHANGED
@@ -7,7 +7,7 @@ module Yabeda
|
|
7
7
|
option :buckets
|
8
8
|
|
9
9
|
# rubocop: disable Metrics/MethodLength
|
10
|
-
def measure(tags, value = nil)
|
10
|
+
def measure(tags = {}, value = nil)
|
11
11
|
if value.nil? ^ block_given?
|
12
12
|
raise ArgumentError, "You must provide either numeric value or block for Yabeda::Histogram#measure!"
|
13
13
|
end
|
@@ -20,7 +20,7 @@ module Yabeda
|
|
20
20
|
|
21
21
|
all_tags = ::Yabeda::Tags.build(tags, group)
|
22
22
|
values[all_tags] = value
|
23
|
-
|
23
|
+
adapters.each_value do |adapter|
|
24
24
|
adapter.perform_histogram_measure!(self, all_tags, value)
|
25
25
|
end
|
26
26
|
value
|
data/lib/yabeda/metric.rb
CHANGED
@@ -14,6 +14,9 @@ module Yabeda
|
|
14
14
|
option :per, optional: true, comment: "Per which unit is measured `unit`. E.g. `call` as in seconds per call"
|
15
15
|
option :group, optional: true, comment: "Category name for grouping metrics"
|
16
16
|
option :aggregation, optional: true, comment: "How adapters should aggregate values from different processes"
|
17
|
+
# rubocop:disable Layout/LineLength
|
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
|
+
# rubocop:enable Layout/LineLength
|
17
20
|
|
18
21
|
# Returns the value for the given label set
|
19
22
|
def get(labels = {})
|
@@ -25,7 +28,7 @@ module Yabeda
|
|
25
28
|
end
|
26
29
|
|
27
30
|
# Returns allowed tags for metric (with account for global and group-level +default_tags+)
|
28
|
-
# @return Array<Symbol>
|
31
|
+
# @return [Array<Symbol>]
|
29
32
|
def tags
|
30
33
|
(Yabeda.groups[group].default_tags.keys + Array(super)).uniq
|
31
34
|
end
|
@@ -33,5 +36,31 @@ module Yabeda
|
|
33
36
|
def inspect
|
34
37
|
"#<#{self.class.name}: #{[@group, @name].compact.join('.')}>"
|
35
38
|
end
|
39
|
+
|
40
|
+
# Returns the metric adapters
|
41
|
+
# @return [Hash<Symbol, Yabeda::BaseAdapter>]
|
42
|
+
def adapters
|
43
|
+
return ::Yabeda.adapters unless adapter
|
44
|
+
|
45
|
+
@adapters ||= begin
|
46
|
+
adapter_names = Array(adapter)
|
47
|
+
unknown_adapters = adapter_names - ::Yabeda.adapters.keys
|
48
|
+
|
49
|
+
if unknown_adapters.any?
|
50
|
+
raise ConfigurationError,
|
51
|
+
"invalid adapter option #{adapter.inspect} in metric #{inspect}"
|
52
|
+
end
|
53
|
+
|
54
|
+
::Yabeda.adapters.slice(*adapter_names)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Redefined option reader to get group-level adapter if not set on metric level
|
59
|
+
# @api private
|
60
|
+
def adapter
|
61
|
+
return ::Yabeda.groups[group]&.adapter if @adapter == Dry::Initializer::UNDEFINED
|
62
|
+
|
63
|
+
super
|
64
|
+
end
|
36
65
|
end
|
37
66
|
end
|
@@ -8,21 +8,30 @@ module Yabeda
|
|
8
8
|
# Example:
|
9
9
|
# expect { anything }.to do_whatever_with_yabeda_metric(Yabeda.something)
|
10
10
|
class BaseMatcher < ::RSpec::Matchers::BuiltIn::BaseMatcher
|
11
|
-
attr_reader :tags, :metric
|
11
|
+
attr_reader :tags, :metric, :expectations
|
12
12
|
|
13
13
|
# Specify a scope of labels (tags). Subset of tags can be specified.
|
14
14
|
def with_tags(tags)
|
15
|
+
raise ArgumentError, "Can't use `with_tags` with expectations hash provided" if !@tags && @expectations&.any?
|
16
|
+
|
15
17
|
@tags = tags
|
18
|
+
@expectations = { tags => nil }
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def with(expectations)
|
23
|
+
@expectations = expectations || {}
|
16
24
|
self
|
17
25
|
end
|
18
26
|
|
19
|
-
def initialize(
|
27
|
+
def initialize(metric)
|
20
28
|
super
|
21
|
-
@expected = @metric = resolve_metric(
|
29
|
+
@expected = @metric = resolve_metric(metric)
|
30
|
+
@expectations = {}
|
22
31
|
rescue KeyError
|
23
32
|
raise ArgumentError, <<~MSG
|
24
33
|
Pass metric name or metric instance to matcher (e.g. `increment_yabeda_counter(Yabeda.metric_name)` or \
|
25
|
-
increment_yabeda_counter('metric_name')). Got #{
|
34
|
+
increment_yabeda_counter('metric_name')). Got #{metric.inspect} instead
|
26
35
|
MSG
|
27
36
|
end
|
28
37
|
|
@@ -56,9 +65,11 @@ module Yabeda
|
|
56
65
|
# Filter metric changes by tags.
|
57
66
|
# If tags specified, treat them as subset of real tags (to avoid bothering with default tags in tests)
|
58
67
|
def filter_matching_changes(changes)
|
59
|
-
return changes
|
68
|
+
return changes.map { |tags, change| [tags, [nil, change]] }.to_h unless expectations&.any?
|
60
69
|
|
61
|
-
|
70
|
+
expectations.map do |tags, expected|
|
71
|
+
[tags, [expected, changes.find { |t, _v| t >= tags }&.[](1)]]
|
72
|
+
end.to_h
|
62
73
|
end
|
63
74
|
end
|
64
75
|
end
|
@@ -15,6 +15,7 @@ module Yabeda
|
|
15
15
|
class IncrementYabedaCounter < BaseMatcher
|
16
16
|
def by(increment)
|
17
17
|
@expected_increment = increment
|
18
|
+
@expectations = { tags => increment } if tags
|
18
19
|
self
|
19
20
|
end
|
20
21
|
|
@@ -32,8 +33,12 @@ module Yabeda
|
|
32
33
|
|
33
34
|
increments = filter_matching_changes(Yabeda::TestAdapter.instance.counters.fetch(metric))
|
34
35
|
|
35
|
-
increments.
|
36
|
-
|
36
|
+
return false if increments.empty?
|
37
|
+
|
38
|
+
increments.values.all? do |expected_increment, actual_increment|
|
39
|
+
next !actual_increment.nil? if expected_increment.nil?
|
40
|
+
|
41
|
+
values_match?(expected_increment, actual_increment)
|
37
42
|
end
|
38
43
|
end
|
39
44
|
|
@@ -49,13 +54,16 @@ module Yabeda
|
|
49
54
|
|
50
55
|
increments = filter_matching_changes(Yabeda::TestAdapter.instance.counters.fetch(metric))
|
51
56
|
|
52
|
-
increments.none?
|
57
|
+
increments.none? { |_tags, (_expected, actual)| !actual.nil? }
|
53
58
|
end
|
54
59
|
|
55
60
|
def failure_message
|
56
61
|
"expected #{expected_formatted} " \
|
57
62
|
"to be incremented #{"by #{description_of(expected_increment)} " unless expected_increment.nil?}" \
|
58
63
|
"#{"with tags #{::RSpec::Support::ObjectFormatter.format(tags)} " if tags}" \
|
64
|
+
"#{if !tags && expectations
|
65
|
+
"with following expectations: #{::RSpec::Support::ObjectFormatter.format(expectations)} "
|
66
|
+
end}" \
|
59
67
|
"but #{actual_increments_message}"
|
60
68
|
end
|
61
69
|
|
@@ -63,6 +71,9 @@ module Yabeda
|
|
63
71
|
"expected #{expected_formatted} " \
|
64
72
|
"not to be incremented " \
|
65
73
|
"#{"with tags #{::RSpec::Support::ObjectFormatter.format(tags)} " if tags}" \
|
74
|
+
"#{if !tags && expectations
|
75
|
+
"with following expectations: #{::RSpec::Support::ObjectFormatter.format(expectations)} "
|
76
|
+
end}" \
|
66
77
|
"but #{actual_increments_message}"
|
67
78
|
end
|
68
79
|
|
@@ -14,6 +14,8 @@ module Yabeda
|
|
14
14
|
# Custom matcher class with implementation for +measure_yabeda_histogram+
|
15
15
|
class MeasureYabedaHistogram < BaseMatcher
|
16
16
|
def with(value)
|
17
|
+
return super if value.is_a?(Hash)
|
18
|
+
|
17
19
|
@expected_value = value
|
18
20
|
self
|
19
21
|
end
|
@@ -32,7 +34,13 @@ module Yabeda
|
|
32
34
|
|
33
35
|
measures = filter_matching_changes(Yabeda::TestAdapter.instance.histograms.fetch(metric))
|
34
36
|
|
35
|
-
|
37
|
+
return false if measures.empty?
|
38
|
+
|
39
|
+
measures.values.all? do |expected_measure, actual_measure|
|
40
|
+
next !actual_measure.nil? if expected_measure.nil?
|
41
|
+
|
42
|
+
values_match?(expected_measure, actual_measure)
|
43
|
+
end
|
36
44
|
end
|
37
45
|
|
38
46
|
def match_when_negated(metric, block)
|
@@ -47,13 +55,16 @@ module Yabeda
|
|
47
55
|
|
48
56
|
measures = filter_matching_changes(Yabeda::TestAdapter.instance.histograms.fetch(metric))
|
49
57
|
|
50
|
-
measures.none?
|
58
|
+
measures.none? { |_tags, (_expected, actual)| !actual.nil? }
|
51
59
|
end
|
52
60
|
|
53
61
|
def failure_message
|
54
62
|
"expected #{expected_formatted} " \
|
55
63
|
"to be changed #{"to #{expected} " unless expected_value.nil?}" \
|
56
64
|
"#{"with tags #{::RSpec::Support::ObjectFormatter.format(tags)} " if tags}" \
|
65
|
+
"#{if !tags && expectations
|
66
|
+
"with following expectations: #{::RSpec::Support::ObjectFormatter.format(expectations)} "
|
67
|
+
end}" \
|
57
68
|
"but #{actual_changes_message}"
|
58
69
|
end
|
59
70
|
|
@@ -61,6 +72,9 @@ module Yabeda
|
|
61
72
|
"expected #{expected_formatted} " \
|
62
73
|
"not to be incremented " \
|
63
74
|
"#{"with tags #{::RSpec::Support::ObjectFormatter.format(tags)} " if tags}" \
|
75
|
+
"#{if !tags && expectations
|
76
|
+
"with following expectations: #{::RSpec::Support::ObjectFormatter.format(expectations)} "
|
77
|
+
end}" \
|
64
78
|
"but #{actual_changes_message}"
|
65
79
|
end
|
66
80
|
|
@@ -14,6 +14,8 @@ module Yabeda
|
|
14
14
|
# Custom matcher class with implementation for +observe_yabeda_summary+
|
15
15
|
class ObserveYabedaSummary < BaseMatcher
|
16
16
|
def with(value)
|
17
|
+
return super if value.is_a?(Hash)
|
18
|
+
|
17
19
|
@expected_value = value
|
18
20
|
self
|
19
21
|
end
|
@@ -32,7 +34,13 @@ module Yabeda
|
|
32
34
|
|
33
35
|
observations = filter_matching_changes(Yabeda::TestAdapter.instance.summaries.fetch(metric))
|
34
36
|
|
35
|
-
|
37
|
+
return false if observations.empty?
|
38
|
+
|
39
|
+
observations.values.all? do |expected_observation, actual_observation|
|
40
|
+
next !actual_observation.nil? if expected_observation.nil?
|
41
|
+
|
42
|
+
values_match?(expected_observation, actual_observation)
|
43
|
+
end
|
36
44
|
end
|
37
45
|
|
38
46
|
def match_when_negated(metric, block)
|
@@ -47,13 +55,16 @@ module Yabeda
|
|
47
55
|
|
48
56
|
observations = filter_matching_changes(Yabeda::TestAdapter.instance.summaries.fetch(metric))
|
49
57
|
|
50
|
-
observations.none?
|
58
|
+
observations.none? { |_tags, (_expected, actual)| !actual.nil? }
|
51
59
|
end
|
52
60
|
|
53
61
|
def failure_message
|
54
62
|
"expected #{expected_formatted} " \
|
55
63
|
"to be observed #{"with #{expected} " unless expected_value.nil?}" \
|
56
64
|
"#{"with tags #{::RSpec::Support::ObjectFormatter.format(tags)} " if tags}" \
|
65
|
+
"#{if !tags && expectations
|
66
|
+
"with following expectations: #{::RSpec::Support::ObjectFormatter.format(expectations)} "
|
67
|
+
end}" \
|
57
68
|
"but #{actual_changes_message}"
|
58
69
|
end
|
59
70
|
|
@@ -61,6 +72,9 @@ module Yabeda
|
|
61
72
|
"expected #{expected_formatted} " \
|
62
73
|
"not to be observed " \
|
63
74
|
"#{"with tags #{::RSpec::Support::ObjectFormatter.format(tags)} " if tags}" \
|
75
|
+
"#{if !tags && expectations
|
76
|
+
"with following expectations: #{::RSpec::Support::ObjectFormatter.format(expectations)} "
|
77
|
+
end}" \
|
64
78
|
"but #{actual_changes_message}"
|
65
79
|
end
|
66
80
|
|
@@ -14,6 +14,8 @@ module Yabeda
|
|
14
14
|
# Custom matcher class with implementation for +update_yabeda_gauge+
|
15
15
|
class UpdateYabedaGauge < BaseMatcher
|
16
16
|
def with(value)
|
17
|
+
return super if value.is_a?(Hash)
|
18
|
+
|
17
19
|
@expected_value = value
|
18
20
|
self
|
19
21
|
end
|
@@ -32,7 +34,13 @@ module Yabeda
|
|
32
34
|
|
33
35
|
updates = filter_matching_changes(Yabeda::TestAdapter.instance.gauges.fetch(metric))
|
34
36
|
|
35
|
-
|
37
|
+
return false if updates.empty?
|
38
|
+
|
39
|
+
updates.values.all? do |expected_update, actual_update|
|
40
|
+
next !actual_update.nil? if expected_update.nil?
|
41
|
+
|
42
|
+
expected_update.nil? || values_match?(expected_update, actual_update)
|
43
|
+
end
|
36
44
|
end
|
37
45
|
|
38
46
|
def match_when_negated(metric, block)
|
@@ -47,13 +55,16 @@ module Yabeda
|
|
47
55
|
|
48
56
|
updates = filter_matching_changes(Yabeda::TestAdapter.instance.gauges.fetch(metric))
|
49
57
|
|
50
|
-
updates.none?
|
58
|
+
updates.none? { |_tags, (_expected, actual)| !actual.nil? }
|
51
59
|
end
|
52
60
|
|
53
61
|
def failure_message
|
54
62
|
"expected #{expected_formatted} " \
|
55
63
|
"to be changed #{"to #{expected_value} " unless expected_value.nil?}" \
|
56
64
|
"#{"with tags #{::RSpec::Support::ObjectFormatter.format(tags)} " if tags}" \
|
65
|
+
"#{if !tags && expectations
|
66
|
+
"with following expectations: #{::RSpec::Support::ObjectFormatter.format(expectations)} "
|
67
|
+
end}" \
|
57
68
|
"but #{actual_changes_message}"
|
58
69
|
end
|
59
70
|
|
@@ -61,6 +72,9 @@ module Yabeda
|
|
61
72
|
"expected #{expected_formatted} " \
|
62
73
|
"not to be changed " \
|
63
74
|
"#{"with tags #{::RSpec::Support::ObjectFormatter.format(tags)} " if tags}" \
|
75
|
+
"#{if !tags && expectations
|
76
|
+
"with following expectations: #{::RSpec::Support::ObjectFormatter.format(expectations)} "
|
77
|
+
end}" \
|
64
78
|
"but #{actual_changes_message}"
|
65
79
|
end
|
66
80
|
|
data/lib/yabeda/summary.rb
CHANGED
@@ -5,7 +5,7 @@ module Yabeda
|
|
5
5
|
# calculate averages, percentiles, and so on.
|
6
6
|
class Summary < Metric
|
7
7
|
# rubocop: disable Metrics/MethodLength
|
8
|
-
def observe(tags, value = nil)
|
8
|
+
def observe(tags = {}, value = nil)
|
9
9
|
if value.nil? ^ block_given?
|
10
10
|
raise ArgumentError, "You must provide either numeric value or block for Yabeda::Summary#observe!"
|
11
11
|
end
|
@@ -18,7 +18,7 @@ module Yabeda
|
|
18
18
|
|
19
19
|
all_tags = ::Yabeda::Tags.build(tags, group)
|
20
20
|
values[all_tags] = value
|
21
|
-
|
21
|
+
adapters.each_value do |adapter|
|
22
22
|
adapter.perform_summary_observe!(self, all_tags, value)
|
23
23
|
end
|
24
24
|
value
|
data/lib/yabeda/version.rb
CHANGED
data/lib/yabeda.rb
CHANGED
@@ -8,12 +8,13 @@ require "yabeda/config"
|
|
8
8
|
require "yabeda/dsl"
|
9
9
|
require "yabeda/tags"
|
10
10
|
require "yabeda/errors"
|
11
|
-
require "yabeda/railtie" if defined?(Rails)
|
12
11
|
|
13
12
|
# Extendable framework for collecting and exporting metrics from Ruby apps
|
14
13
|
module Yabeda
|
15
14
|
include DSL
|
16
15
|
|
16
|
+
EMPTY_TAGS = {}.freeze
|
17
|
+
|
17
18
|
class << self
|
18
19
|
extend Forwardable
|
19
20
|
|
@@ -29,7 +30,7 @@ module Yabeda
|
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
32
|
-
# @return [Hash<
|
33
|
+
# @return [Hash<Symbol, Yabeda::BaseAdapter>] All loaded adapters
|
33
34
|
def adapters
|
34
35
|
@adapters ||= Concurrent::Hash.new
|
35
36
|
end
|
@@ -68,7 +69,9 @@ module Yabeda
|
|
68
69
|
def register_adapter(name, instance)
|
69
70
|
adapters[name] = instance
|
70
71
|
# NOTE: Pretty sure there is race condition
|
71
|
-
metrics.
|
72
|
+
metrics.each_value do |metric|
|
73
|
+
next unless metric.adapters.key?(name)
|
74
|
+
|
72
75
|
instance.register!(metric)
|
73
76
|
end
|
74
77
|
end
|
@@ -100,8 +103,8 @@ module Yabeda
|
|
100
103
|
|
101
104
|
# Register metrics in adapters after evaluating all configuration blocks
|
102
105
|
# to ensure that all global settings (like default tags) will be applied.
|
103
|
-
|
104
|
-
|
106
|
+
metrics.each_value do |metric|
|
107
|
+
metric.adapters.each_value do |adapter|
|
105
108
|
adapter.register!(metric)
|
106
109
|
end
|
107
110
|
end
|
@@ -129,7 +132,6 @@ module Yabeda
|
|
129
132
|
|
130
133
|
true
|
131
134
|
end
|
132
|
-
# rubocop: enable Metrics/MethodLength
|
133
135
|
|
134
136
|
# Forget all the configuration.
|
135
137
|
# For testing purposes as it doesn't rollback changes in adapters.
|
@@ -143,8 +145,12 @@ module Yabeda
|
|
143
145
|
@metrics = nil
|
144
146
|
collectors.clear
|
145
147
|
configurators.clear
|
148
|
+
@config = Config.new
|
146
149
|
instance_variable_set(:@configured_by, nil)
|
147
150
|
instance_variable_set(:@debug_was_enabled_by, nil)
|
148
151
|
end
|
152
|
+
# rubocop: enable Metrics/MethodLength
|
149
153
|
end
|
150
154
|
end
|
155
|
+
|
156
|
+
require "yabeda/railtie" if defined?(Rails)
|
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.
|
4
|
+
version: 0.13.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrey Novikov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-10-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: anyway_config
|
@@ -68,8 +68,8 @@ executables: []
|
|
68
68
|
extensions: []
|
69
69
|
extra_rdoc_files: []
|
70
70
|
files:
|
71
|
-
- ".github/workflows/build-release.yml"
|
72
71
|
- ".github/workflows/lint.yml"
|
72
|
+
- ".github/workflows/release.yml"
|
73
73
|
- ".github/workflows/test.yml"
|
74
74
|
- ".gitignore"
|
75
75
|
- ".rspec"
|
@@ -129,7 +129,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
129
129
|
- !ruby/object:Gem::Version
|
130
130
|
version: '0'
|
131
131
|
requirements: []
|
132
|
-
rubygems_version: 3.
|
132
|
+
rubygems_version: 3.5.16
|
133
133
|
signing_key:
|
134
134
|
specification_version: 4
|
135
135
|
summary: Extensible framework for collecting metric for your Ruby application
|