yabeda 0.11.0 → 0.13.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 +4 -4
- data/.github/workflows/lint.yml +37 -0
- data/.github/workflows/{build-release.yml → release.yml} +9 -5
- data/.github/workflows/test.yml +9 -24
- data/.rubocop.yml +9 -0
- data/CHANGELOG.md +50 -1
- data/Gemfile +7 -2
- data/README.md +52 -14
- data/lib/yabeda/base_adapter.rb +9 -0
- data/lib/yabeda/counter.rb +2 -2
- data/lib/yabeda/dsl/class_methods.rb +18 -1
- data/lib/yabeda/gauge.rb +3 -3
- data/lib/yabeda/global_group.rb +1 -1
- 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 +18 -6
- data/lib/yabeda/rspec/increment_yabeda_counter.rb +21 -10
- data/lib/yabeda/rspec/measure_yabeda_histogram.rb +23 -9
- data/lib/yabeda/rspec/observe_yabeda_summary.rb +94 -0
- data/lib/yabeda/rspec/update_yabeda_gauge.rb +23 -9
- data/lib/yabeda/rspec.rb +6 -5
- data/lib/yabeda/summary.rb +28 -0
- data/lib/yabeda/test_adapter.rb +15 -3
- data/lib/yabeda/testing.rb +1 -1
- data/lib/yabeda/version.rb +1 -1
- data/lib/yabeda.rb +11 -9
- data/yabeda.gemspec +1 -5
- metadata +8 -75
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8d2581bd55cdd41639a1c6f01d456c5e09e1ad7636253e92720d35242fde6fab
|
4
|
+
data.tar.gz: 02f88db1ee3d892e387129c8775ab1b31dbafe2ec97ea8a67c34ff6abf67dc1d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 57c4e7b716b077e37b7191cf4bfcc4f7090943eab1922439d485e02bc925408f21db656e239545bd7836413fc219282e71553b4cbd88e02b80f272dc236bacfb
|
7
|
+
data.tar.gz: 63707a843e531b01e685e17f7772dcf718ab730d401396d936ade23a5f8e0a7699ddbf7799caca797afcb4ef0fb5c07cacfd362ff98f46949846f166de636b97
|
@@ -0,0 +1,37 @@
|
|
1
|
+
name: Lint Ruby
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- '**'
|
7
|
+
tags-ignore:
|
8
|
+
- 'v*'
|
9
|
+
paths:
|
10
|
+
- "gemfiles/*"
|
11
|
+
- "Gemfile"
|
12
|
+
- "**/*.rb"
|
13
|
+
- "**/*.gemspec"
|
14
|
+
- ".github/workflows/lint.yml"
|
15
|
+
pull_request:
|
16
|
+
paths:
|
17
|
+
- "gemfiles/*"
|
18
|
+
- "Gemfile"
|
19
|
+
- "**/*.rb"
|
20
|
+
- "**/*.gemspec"
|
21
|
+
- ".github/workflows/lint.yml"
|
22
|
+
|
23
|
+
jobs:
|
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
|
28
|
+
runs-on: ubuntu-latest
|
29
|
+
steps:
|
30
|
+
- uses: actions/checkout@v4
|
31
|
+
- uses: ruby/setup-ruby@v1
|
32
|
+
with:
|
33
|
+
ruby-version: "3.3"
|
34
|
+
bundler-cache: true
|
35
|
+
- name: Lint Ruby code with RuboCop
|
36
|
+
run: |
|
37
|
+
bundle exec rubocop
|
@@ -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,38 +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"
|
22
|
+
- ruby: "3.2"
|
23
|
+
- ruby: "3.1"
|
19
24
|
- ruby: "3.0"
|
20
25
|
- ruby: "2.7"
|
21
|
-
- ruby: "2.6"
|
22
|
-
- ruby: "2.5"
|
23
|
-
- ruby: "2.4"
|
24
|
-
- ruby: "2.3"
|
25
|
-
container:
|
26
|
-
image: ruby:${{ matrix.ruby }}
|
27
|
-
env:
|
28
|
-
CI: true
|
29
26
|
steps:
|
30
|
-
- uses: actions/checkout@
|
31
|
-
- uses:
|
27
|
+
- uses: actions/checkout@v4
|
28
|
+
- uses: ruby/setup-ruby@v1
|
32
29
|
with:
|
33
|
-
|
34
|
-
|
35
|
-
restore-keys: |
|
36
|
-
bundle-${{ matrix.ruby }}-${{ hashFiles('**/*.gemspec') }}-${{ hashFiles('**/Gemfile') }}
|
37
|
-
bundle-${{ matrix.ruby }}-
|
38
|
-
- name: Upgrade Bundler to 2.0 (for older Rubies)
|
39
|
-
run: gem install bundler -v '~> 2.0'
|
40
|
-
- name: Bundle install
|
41
|
-
run: |
|
42
|
-
bundle config path vendor/bundle
|
43
|
-
bundle install
|
44
|
-
bundle update
|
45
|
-
- name: Run Rubocop
|
46
|
-
run: bundle exec rubocop
|
30
|
+
ruby-version: ${{ matrix.ruby }}
|
31
|
+
bundler-cache: true
|
47
32
|
- name: Run RSpec
|
48
33
|
run: bundle exec rspec
|
data/.rubocop.yml
CHANGED
@@ -10,6 +10,9 @@ Metrics/BlockLength:
|
|
10
10
|
- "Gemfile"
|
11
11
|
- "spec/**/*"
|
12
12
|
|
13
|
+
Metrics/AbcSize:
|
14
|
+
Max: 25
|
15
|
+
|
13
16
|
Layout/LineLength:
|
14
17
|
Max: 120
|
15
18
|
|
@@ -29,6 +32,12 @@ RSpec/DescribeClass:
|
|
29
32
|
RSpec/NestedGroups:
|
30
33
|
Max: 4
|
31
34
|
|
35
|
+
RSpec/MultipleMemoizedHelpers:
|
36
|
+
Enabled: false
|
37
|
+
|
38
|
+
RSpec/ExampleLength:
|
39
|
+
Max: 10
|
40
|
+
|
32
41
|
Bundler/OrderedGems:
|
33
42
|
Enabled: false
|
34
43
|
|
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.0 - 2024-10-02
|
11
|
+
|
12
|
+
### Added
|
13
|
+
|
14
|
+
- Ability to limit some metrics to specific adapters. [#37](https://github.com/yabeda-rb/yabeda/pull/37) by [@Keallar] and [@Envek]
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
Yabeda.configure do
|
18
|
+
group :cloud do
|
19
|
+
adapter :newrelic, :datadog
|
20
|
+
|
21
|
+
counter :foo
|
22
|
+
end
|
23
|
+
|
24
|
+
counter :bar, adapter: :prometheus
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
- Multiple expectations in RSpec matchers. [@Envek]
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
expect { whatever }.to increment_yabeda_counter(:my_counter).with(
|
32
|
+
{ tag: "foo" } => 1,
|
33
|
+
{ tag: "bar" } => (be >= 42),
|
34
|
+
)
|
35
|
+
```
|
36
|
+
|
37
|
+
### Changed
|
38
|
+
|
39
|
+
- 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]
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
Yabeda.foo.increment
|
43
|
+
# same as
|
44
|
+
Yabeda.foo.increment({}, by: 1)
|
45
|
+
```
|
46
|
+
|
47
|
+
### Fixed
|
48
|
+
|
49
|
+
- Railtie loading to prevent calling methods that have not yet been defined. [#38](https://github.com/yabeda-rb/yabeda/pull/38) by [@bibendi].
|
50
|
+
|
51
|
+
## 0.12.0 - 2023-07-28
|
52
|
+
|
53
|
+
### Added
|
54
|
+
|
55
|
+
- Summary metric type (mostly for Prometheus adapter).
|
56
|
+
|
10
57
|
## 0.11.0 - 2021-09-25
|
11
58
|
|
12
59
|
### Added
|
@@ -31,7 +78,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
31
78
|
|
32
79
|
### Changed
|
33
80
|
|
34
|
-
- Adapters now should use method `Yabeda.collect!` instead of manual calling of every collector block.
|
81
|
+
- Adapters now should use method `Yabeda.collect!` instead of manual calling of every collector block.
|
35
82
|
|
36
83
|
## 0.9.0 - 2021-05-07
|
37
84
|
|
@@ -134,3 +181,5 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
134
181
|
[@dsalahutdinov]: https://github.com/dsalahutdinov "Dmitry Salahutdinov"
|
135
182
|
[@asusikov]: https://github.com/asusikov "Alexander Susikov"
|
136
183
|
[@liaden]: https://github.com/liaden "Joel Johnson"
|
184
|
+
[@bibendi]: https://github.com/bibendi "Misha Merkushin"
|
185
|
+
[@Keallar]: https://github.com/Keallar "Eugene Lysanskiy"
|
data/Gemfile
CHANGED
@@ -8,9 +8,14 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
|
8
8
|
gemspec
|
9
9
|
|
10
10
|
group :development, :test do
|
11
|
+
gem "rake", "~> 12.0"
|
12
|
+
gem "rspec", "~> 3.0"
|
13
|
+
gem "yard"
|
14
|
+
gem "yard-dry-initializer"
|
15
|
+
|
11
16
|
gem "pry"
|
12
17
|
gem "pry-byebug", platform: :mri
|
13
18
|
|
14
|
-
gem "rubocop", "~>
|
15
|
-
gem "rubocop-rspec"
|
19
|
+
gem "rubocop", "~> 1.0", require: false
|
20
|
+
gem "rubocop-rspec", require: false
|
16
21
|
end
|
data/README.md
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# 
|
2
2
|
|
3
3
|
[](https://rubygems.org/gems/yabeda)
|
4
|
-
|
5
|
-
**This software is Work in Progress: features will appear and disappear, API will be changed, your feedback is always welcome!**
|
4
|
+
[](https://github.com/yabeda-rb/yabeda/actions/workflows/test.yml)
|
6
5
|
|
7
6
|
Extendable solution for easy setup of monitoring in your Ruby apps.
|
8
7
|
|
@@ -18,6 +17,11 @@ Most of the time you don't need to add this gem to your Gemfile directly (unless
|
|
18
17
|
|
19
18
|
```ruby
|
20
19
|
gem 'yabeda'
|
20
|
+
|
21
|
+
# Add some plugins to quickly start collecting some essential metrics:
|
22
|
+
# gem 'yabeda-rails'
|
23
|
+
# gem 'yabeda-sidekiq'
|
24
|
+
|
21
25
|
# Then add monitoring system adapter, e.g.:
|
22
26
|
# gem 'yabeda-prometheus'
|
23
27
|
```
|
@@ -39,6 +43,7 @@ And then execute:
|
|
39
43
|
comment "How long whistles are being active"
|
40
44
|
unit :seconds
|
41
45
|
end
|
46
|
+
summary :bells_ringing_duration, unit: :seconds, comment: "How long bells are ringing"
|
42
47
|
end
|
43
48
|
end
|
44
49
|
```
|
@@ -124,6 +129,8 @@ And then execute:
|
|
124
129
|
These are developed and maintained by other awesome folks:
|
125
130
|
|
126
131
|
- [Statsd](https://github.com/asusikov/yabeda-statsd)
|
132
|
+
- [AWS CloudWatch](https://github.com/retsef/yabeda-cloudwatch)
|
133
|
+
- [Honeybadger Insights](https://github.com/honeybadger-io/yabeda-honeybadger_insights)
|
127
134
|
- _…and more! You can write your own adapter and open a pull request to add it into this list._
|
128
135
|
|
129
136
|
## Available plugins to collect metrics
|
@@ -131,6 +138,7 @@ These are developed and maintained by other awesome folks:
|
|
131
138
|
### Maintained by Yabeda
|
132
139
|
|
133
140
|
- [yabeda-rails] — basic request metrics for [Ruby on Rails](https://rubyonrails.org/) applications.
|
141
|
+
- [yabeda-activerecord] — query performance and connection pool stats for apps using ActiveRecord to query databases.
|
134
142
|
- [yabeda-sidekiq] — comprehensive set of metrics for monitoring [Sidekiq](https://sidekiq.org/) jobs execution and queues.
|
135
143
|
- [yabeda-faktory] — metrics for monitoring jobs execution by Ruby workers of [Faktory](https://contribsys.com/faktory/).
|
136
144
|
- [yabeda-graphql] — metrics to query and field-level monitoring for apps using [GraphQL-Ruby](https://graphql-ruby.org/).
|
@@ -146,6 +154,10 @@ These are developed and maintained by other awesome folks:
|
|
146
154
|
- [yabeda-grape](https://github.com/efigence/yabeda-grape) — metrics for [Grape](https://github.com/ruby-grape/grape) framework.
|
147
155
|
- [yabeda-gruf](https://github.com/Placewise/yabeda-gruf) — metrics for [gRPC Ruby Framework](https://github.com/bigcommerce/gruf)
|
148
156
|
- [yabeda-gc](https://github.com/ianks/yabeda-gc) — metrics for Ruby garbage collection.
|
157
|
+
- [yabeda-activejob](https://github.com/Fullscript/yabeda-activejob) — backend-agnostic metrics for background jobs.
|
158
|
+
- [yabeda-shoryuken](https://github.com/retsef/yabeda-shoryuken) — metrics for [Shoryuken](https://github.com/ruby-shoryuken/shoryuken) jobs execution message queues.
|
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.
|
149
161
|
- _…and more! You can write your own adapter and open a pull request to add it into this list._
|
150
162
|
|
151
163
|
## Configuration
|
@@ -172,7 +184,7 @@ Add the following to your `rails_helper.rb` (or `spec_helper.rb`):
|
|
172
184
|
require "yabeda/rspec"
|
173
185
|
```
|
174
186
|
|
175
|
-
Now you can use `increment_yabeda_counter`, `update_yabeda_gauge`, and `
|
187
|
+
Now you can use `increment_yabeda_counter`, `update_yabeda_gauge`, `measure_yabeda_histogram`, and `observe_yabeda_summary` matchers:
|
176
188
|
|
177
189
|
```ruby
|
178
190
|
it "increments counters" do
|
@@ -192,7 +204,7 @@ end
|
|
192
204
|
|
193
205
|
Note that tags you specified doesn't need to be exact, but can be a subset of tags used on metric update. In this example updates with following sets of tags `{ method: "command", command: "subscribe", status: "SUCCESS" }` and `{ method: "command", command: "subscribe", status: "FAILURE" }` will make test example to pass.
|
194
206
|
|
195
|
-
And check for values with `by` for counters, `to` for gauges, and `with` for
|
207
|
+
And check for values with `by` for counters, `to` for gauges, and `with` for histograms and summaries (and you [can use other matchers here](https://relishapp.com/rspec/rspec-expectations/v/3-10/docs/composing-matchers)):
|
196
208
|
|
197
209
|
```ruby
|
198
210
|
expect { subject }.to \
|
@@ -200,6 +212,41 @@ expect { subject }.to \
|
|
200
212
|
with(be_between(0.005, 0.05))
|
201
213
|
```
|
202
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
|
+
|
203
250
|
## Roadmap (aka TODO or Help wanted)
|
204
251
|
|
205
252
|
- Ability to change metric settings for individual adapters
|
@@ -212,16 +259,6 @@ expect { subject }.to \
|
|
212
259
|
end
|
213
260
|
```
|
214
261
|
|
215
|
-
- Ability to route some metrics only for given adapter:
|
216
|
-
|
217
|
-
```rb
|
218
|
-
adapter :prometheus do
|
219
|
-
include_group :sidekiq
|
220
|
-
end
|
221
|
-
```
|
222
|
-
|
223
|
-
|
224
|
-
|
225
262
|
## Development
|
226
263
|
|
227
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.
|
@@ -267,6 +304,7 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/yabeda
|
|
267
304
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
268
305
|
|
269
306
|
[yabeda-rails]: https://github.com/yabeda-rb/yabeda-rails/ "Yabeda plugin for collecting and exporting basic metrics for Rails applications"
|
307
|
+
[yabeda-activerecord]: https://github.com/yabeda-rb/yabeda-activerecord/ "Yabeda plugin to collect query performance metrics and connection pool statistics"
|
270
308
|
[yabeda-sidekiq]: https://github.com/yabeda-rb/yabeda-sidekiq/ "Yabeda plugin for complete monitoring of Sidekiq metrics"
|
271
309
|
[yabeda-faktory]: https://github.com/yabeda-rb/yabeda-faktory/ "Yabeda plugin for complete monitoring of Faktory Ruby Workers"
|
272
310
|
[yabeda-graphql]: https://github.com/yabeda-rb/yabeda-graphql/ "Measure and understand how good your GraphQL-Ruby application works"
|
data/lib/yabeda/base_adapter.rb
CHANGED
@@ -8,6 +8,7 @@ module Yabeda
|
|
8
8
|
when Counter then register_counter!(metric)
|
9
9
|
when Gauge then register_gauge!(metric)
|
10
10
|
when Histogram then register_histogram!(metric)
|
11
|
+
when Summary then register_summary!(metric)
|
11
12
|
else raise "#{metric.class} is unknown metric type"
|
12
13
|
end
|
13
14
|
end
|
@@ -36,6 +37,14 @@ module Yabeda
|
|
36
37
|
raise NotImplementedError, "#{self.class} doesn't support measuring histograms"
|
37
38
|
end
|
38
39
|
|
40
|
+
def register_summary!(_metric)
|
41
|
+
raise NotImplementedError, "#{self.class} doesn't support summaries as metric type!"
|
42
|
+
end
|
43
|
+
|
44
|
+
def perform_summary_observe!(_metric, _tags, _value)
|
45
|
+
raise NotImplementedError, "#{self.class} doesn't support observing summaries"
|
46
|
+
end
|
47
|
+
|
39
48
|
# Hook to enable debug mode in adapters when it is enabled in Yabeda itself
|
40
49
|
def debug!; end
|
41
50
|
end
|
data/lib/yabeda/counter.rb
CHANGED
@@ -3,10 +3,10 @@
|
|
3
3
|
module Yabeda
|
4
4
|
# Growing-only counter
|
5
5
|
class Counter < Metric
|
6
|
-
def increment(tags, by: 1)
|
6
|
+
def increment(tags = {}, by: 1)
|
7
7
|
all_tags = ::Yabeda::Tags.build(tags, group)
|
8
8
|
values[all_tags] += by
|
9
|
-
|
9
|
+
adapters.each_value do |adapter|
|
10
10
|
adapter.perform_counter_increment!(self, all_tags, by)
|
11
11
|
end
|
12
12
|
values[all_tags]
|
@@ -4,6 +4,7 @@ require "yabeda/metric"
|
|
4
4
|
require "yabeda/counter"
|
5
5
|
require "yabeda/gauge"
|
6
6
|
require "yabeda/histogram"
|
7
|
+
require "yabeda/summary"
|
7
8
|
require "yabeda/group"
|
8
9
|
require "yabeda/global_group"
|
9
10
|
require "yabeda/dsl/metric_builder"
|
@@ -55,6 +56,12 @@ module Yabeda
|
|
55
56
|
register_metric(metric)
|
56
57
|
end
|
57
58
|
|
59
|
+
# Register a summary
|
60
|
+
def summary(*args, **kwargs, &block)
|
61
|
+
metric = MetricBuilder.new(Summary).build(args, kwargs, @group, &block)
|
62
|
+
register_metric(metric)
|
63
|
+
end
|
64
|
+
|
58
65
|
# Add default tag for all metric
|
59
66
|
#
|
60
67
|
# @param name [Symbol] Name of default tag
|
@@ -85,6 +92,16 @@ module Yabeda
|
|
85
92
|
Thread.current[:yabeda_temporary_tags] ||= {}
|
86
93
|
end
|
87
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
|
+
|
88
105
|
private
|
89
106
|
|
90
107
|
def register_metric(metric)
|
@@ -92,7 +109,7 @@ module Yabeda
|
|
92
109
|
::Yabeda.define_singleton_method(name) { metric }
|
93
110
|
::Yabeda.metrics[name] = metric
|
94
111
|
register_group_for(metric) if metric.group
|
95
|
-
|
112
|
+
metric.adapters.each_value { |adapter| adapter.register!(metric) } if ::Yabeda.configured?
|
96
113
|
metric
|
97
114
|
end
|
98
115
|
|
data/lib/yabeda/gauge.rb
CHANGED
@@ -6,17 +6,17 @@ 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
|
-
def increment(tags, by: 1)
|
15
|
+
def increment(tags = {}, by: 1)
|
16
16
|
set(tags, get(tags).to_i + by)
|
17
17
|
end
|
18
18
|
|
19
|
-
def decrement(tags, by: 1)
|
19
|
+
def decrement(tags = {}, by: 1)
|
20
20
|
set(tags, get(tags).to_i - by)
|
21
21
|
end
|
22
22
|
end
|
data/lib/yabeda/global_group.rb
CHANGED
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,20 +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(
|
20
|
-
|
27
|
+
def initialize(metric)
|
28
|
+
super
|
29
|
+
@expected = @metric = resolve_metric(metric)
|
30
|
+
@expectations = {}
|
21
31
|
rescue KeyError
|
22
32
|
raise ArgumentError, <<~MSG
|
23
33
|
Pass metric name or metric instance to matcher (e.g. `increment_yabeda_counter(Yabeda.metric_name)` or \
|
24
|
-
increment_yabeda_counter('metric_name')). Got #{
|
34
|
+
increment_yabeda_counter('metric_name')). Got #{metric.inspect} instead
|
25
35
|
MSG
|
26
36
|
end
|
27
37
|
|
@@ -55,9 +65,11 @@ module Yabeda
|
|
55
65
|
# Filter metric changes by tags.
|
56
66
|
# If tags specified, treat them as subset of real tags (to avoid bothering with default tags in tests)
|
57
67
|
def filter_matching_changes(changes)
|
58
|
-
return changes
|
68
|
+
return changes.map { |tags, change| [tags, [nil, change]] }.to_h unless expectations&.any?
|
59
69
|
|
60
|
-
|
70
|
+
expectations.map do |tags, expected|
|
71
|
+
[tags, [expected, changes.find { |t, _v| t >= tags }&.[](1)]]
|
72
|
+
end.to_h
|
61
73
|
end
|
62
74
|
end
|
63
75
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
3
|
+
require_relative "base_matcher"
|
4
4
|
|
5
5
|
module Yabeda
|
6
6
|
module RSpec
|
@@ -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,21 +54,27 @@ 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
|
-
|
58
|
-
|
59
|
-
|
62
|
+
"to be incremented #{"by #{description_of(expected_increment)} " unless expected_increment.nil?}" \
|
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}" \
|
67
|
+
"but #{actual_increments_message}"
|
60
68
|
end
|
61
69
|
|
62
70
|
def failure_message_when_negated
|
63
71
|
"expected #{expected_formatted} " \
|
64
|
-
|
65
|
-
|
66
|
-
|
72
|
+
"not to be incremented " \
|
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}" \
|
77
|
+
"but #{actual_increments_message}"
|
67
78
|
end
|
68
79
|
|
69
80
|
def actual_increments_message
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
3
|
+
require_relative "base_matcher"
|
4
4
|
|
5
5
|
module Yabeda
|
6
6
|
module RSpec
|
@@ -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,21 +55,27 @@ 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
|
-
|
56
|
-
|
57
|
-
|
63
|
+
"to be changed #{"to #{expected} " unless expected_value.nil?}" \
|
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}" \
|
68
|
+
"but #{actual_changes_message}"
|
58
69
|
end
|
59
70
|
|
60
71
|
def failure_message_when_negated
|
61
72
|
"expected #{expected_formatted} " \
|
62
|
-
|
63
|
-
|
64
|
-
|
73
|
+
"not to be incremented " \
|
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}" \
|
78
|
+
"but #{actual_changes_message}"
|
65
79
|
end
|
66
80
|
|
67
81
|
def actual_changes_message
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base_matcher"
|
4
|
+
|
5
|
+
module Yabeda
|
6
|
+
module RSpec
|
7
|
+
# Checks whether Yabeda summary was observed during test run or not
|
8
|
+
# @param metric [Yabeda::Summary,String,Symbol] metric instance or name
|
9
|
+
# @return [Yabeda::RSpec::ObserveYabedaSummary]
|
10
|
+
def observe_yabeda_summary(metric)
|
11
|
+
ObserveYabedaSummary.new(metric)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Custom matcher class with implementation for +observe_yabeda_summary+
|
15
|
+
class ObserveYabedaSummary < BaseMatcher
|
16
|
+
def with(value)
|
17
|
+
return super if value.is_a?(Hash)
|
18
|
+
|
19
|
+
@expected_value = value
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :expected_value
|
24
|
+
|
25
|
+
def initialize(*)
|
26
|
+
super
|
27
|
+
return if metric.is_a? Yabeda::Summary
|
28
|
+
|
29
|
+
raise ArgumentError, "Pass summary instance/name to `observe_yabeda_summary`. Got #{metric.inspect} instead"
|
30
|
+
end
|
31
|
+
|
32
|
+
def match(metric, block)
|
33
|
+
block.call
|
34
|
+
|
35
|
+
observations = filter_matching_changes(Yabeda::TestAdapter.instance.summaries.fetch(metric))
|
36
|
+
|
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
|
44
|
+
end
|
45
|
+
|
46
|
+
def match_when_negated(metric, block)
|
47
|
+
unless expected_value.nil?
|
48
|
+
raise NotImplementedError, <<~MSG
|
49
|
+
`expect {}.not_to observe_yabeda_summary` doesn't support specifying values with `.with`
|
50
|
+
as it can lead to false positives.
|
51
|
+
MSG
|
52
|
+
end
|
53
|
+
|
54
|
+
block.call
|
55
|
+
|
56
|
+
observations = filter_matching_changes(Yabeda::TestAdapter.instance.summaries.fetch(metric))
|
57
|
+
|
58
|
+
observations.none? { |_tags, (_expected, actual)| !actual.nil? }
|
59
|
+
end
|
60
|
+
|
61
|
+
def failure_message
|
62
|
+
"expected #{expected_formatted} " \
|
63
|
+
"to be observed #{"with #{expected} " unless expected_value.nil?}" \
|
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}" \
|
68
|
+
"but #{actual_changes_message}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def failure_message_when_negated
|
72
|
+
"expected #{expected_formatted} " \
|
73
|
+
"not to be observed " \
|
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}" \
|
78
|
+
"but #{actual_changes_message}"
|
79
|
+
end
|
80
|
+
|
81
|
+
def actual_changes_message
|
82
|
+
observations = Yabeda::TestAdapter.instance.summaries.fetch(metric)
|
83
|
+
if observations.empty?
|
84
|
+
"no observations of this summary have been made"
|
85
|
+
elsif tags && observations.key?(tags)
|
86
|
+
formatted_tags = ::RSpec::Support::ObjectFormatter.format(tags)
|
87
|
+
"has been observed with #{observations.fetch(tags)} with tags #{formatted_tags}"
|
88
|
+
else
|
89
|
+
"following observations have been made: #{::RSpec::Support::ObjectFormatter.format(observations)}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
3
|
+
require_relative "base_matcher"
|
4
4
|
|
5
5
|
module Yabeda
|
6
6
|
module RSpec
|
@@ -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,21 +55,27 @@ 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
|
-
|
56
|
-
|
57
|
-
|
63
|
+
"to be changed #{"to #{expected_value} " unless expected_value.nil?}" \
|
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}" \
|
68
|
+
"but #{actual_changes_message}"
|
58
69
|
end
|
59
70
|
|
60
71
|
def failure_message_when_negated
|
61
72
|
"expected #{expected_formatted} " \
|
62
|
-
|
63
|
-
|
64
|
-
|
73
|
+
"not to be changed " \
|
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}" \
|
78
|
+
"but #{actual_changes_message}"
|
65
79
|
end
|
66
80
|
|
67
81
|
def actual_changes_message
|
data/lib/yabeda/rspec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
3
|
+
require_relative "testing"
|
4
4
|
|
5
5
|
module Yabeda
|
6
6
|
# RSpec integration for Yabeda: custom matchers, etc
|
@@ -8,11 +8,12 @@ module Yabeda
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
require_relative "
|
12
|
-
require_relative "
|
13
|
-
require_relative "
|
11
|
+
require_relative "rspec/increment_yabeda_counter"
|
12
|
+
require_relative "rspec/update_yabeda_gauge"
|
13
|
+
require_relative "rspec/measure_yabeda_histogram"
|
14
|
+
require_relative "rspec/observe_yabeda_summary"
|
14
15
|
|
15
|
-
|
16
|
+
RSpec.configure do |config|
|
16
17
|
config.before(:suite) do
|
17
18
|
Yabeda.configure! unless Yabeda.already_configured?
|
18
19
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Yabeda
|
4
|
+
# Base class for complex metric for measuring time values that allow to
|
5
|
+
# calculate averages, percentiles, and so on.
|
6
|
+
class Summary < Metric
|
7
|
+
# rubocop: disable Metrics/MethodLength
|
8
|
+
def observe(tags = {}, value = nil)
|
9
|
+
if value.nil? ^ block_given?
|
10
|
+
raise ArgumentError, "You must provide either numeric value or block for Yabeda::Summary#observe!"
|
11
|
+
end
|
12
|
+
|
13
|
+
if block_given?
|
14
|
+
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
15
|
+
yield
|
16
|
+
value = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - starting)
|
17
|
+
end
|
18
|
+
|
19
|
+
all_tags = ::Yabeda::Tags.build(tags, group)
|
20
|
+
values[all_tags] = value
|
21
|
+
adapters.each_value do |adapter|
|
22
|
+
adapter.perform_summary_observe!(self, all_tags, value)
|
23
|
+
end
|
24
|
+
value
|
25
|
+
end
|
26
|
+
# rubocop: enable Metrics/MethodLength
|
27
|
+
end
|
28
|
+
end
|
data/lib/yabeda/test_adapter.rb
CHANGED
@@ -2,24 +2,28 @@
|
|
2
2
|
|
3
3
|
require "singleton"
|
4
4
|
|
5
|
-
require_relative "
|
5
|
+
require_relative "base_adapter"
|
6
6
|
|
7
7
|
module Yabeda
|
8
8
|
# Fake monitoring system adapter that collects latest metric values for later inspection
|
9
9
|
class TestAdapter < BaseAdapter
|
10
10
|
include Singleton
|
11
11
|
|
12
|
-
attr_reader :counters, :gauges, :histograms
|
12
|
+
attr_reader :counters, :gauges, :histograms, :summaries
|
13
13
|
|
14
|
+
# rubocop:disable Metrics/AbcSize
|
14
15
|
def initialize
|
16
|
+
super
|
15
17
|
@counters = Hash.new { |ch, ck| ch[ck] = Hash.new { |th, tk| th[tk] = 0 } }
|
16
18
|
@gauges = Hash.new { |gh, gk| gh[gk] = Hash.new { |th, tk| th[tk] = nil } }
|
17
19
|
@histograms = Hash.new { |hh, hk| hh[hk] = Hash.new { |th, tk| th[tk] = nil } }
|
20
|
+
@summaries = Hash.new { |sh, sk| sh[sk] = Hash.new { |th, tk| th[tk] = nil } }
|
18
21
|
end
|
22
|
+
# rubocop:enable Metrics/AbcSize
|
19
23
|
|
20
24
|
# Call this method after every test example to quickly get blank state for the next test example
|
21
25
|
def reset!
|
22
|
-
[@counters, @gauges, @histograms].each do |collection|
|
26
|
+
[@counters, @gauges, @histograms, @summaries].each do |collection|
|
23
27
|
collection.each_value(&:clear) # Reset tag-values hash to be empty
|
24
28
|
end
|
25
29
|
end
|
@@ -36,6 +40,10 @@ module Yabeda
|
|
36
40
|
@histograms[metric]
|
37
41
|
end
|
38
42
|
|
43
|
+
def register_summary!(metric)
|
44
|
+
@summaries[metric]
|
45
|
+
end
|
46
|
+
|
39
47
|
def perform_counter_increment!(counter, tags, increment)
|
40
48
|
@counters[counter][tags] += increment
|
41
49
|
end
|
@@ -47,5 +55,9 @@ module Yabeda
|
|
47
55
|
def perform_histogram_measure!(histogram, tags, value)
|
48
56
|
@histograms[histogram][tags] = value
|
49
57
|
end
|
58
|
+
|
59
|
+
def perform_summary_observe!(summary, tags, value)
|
60
|
+
@summaries[summary][tags] = value
|
61
|
+
end
|
50
62
|
end
|
51
63
|
end
|
data/lib/yabeda/testing.rb
CHANGED
data/lib/yabeda/version.rb
CHANGED
data/lib/yabeda.rb
CHANGED
@@ -8,7 +8,6 @@ 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
|
@@ -29,7 +28,7 @@ module Yabeda
|
|
29
28
|
end
|
30
29
|
end
|
31
30
|
|
32
|
-
# @return [Hash<
|
31
|
+
# @return [Hash<Symbol, Yabeda::BaseAdapter>] All loaded adapters
|
33
32
|
def adapters
|
34
33
|
@adapters ||= Concurrent::Hash.new
|
35
34
|
end
|
@@ -68,7 +67,9 @@ module Yabeda
|
|
68
67
|
def register_adapter(name, instance)
|
69
68
|
adapters[name] = instance
|
70
69
|
# NOTE: Pretty sure there is race condition
|
71
|
-
metrics.
|
70
|
+
metrics.each_value do |metric|
|
71
|
+
next unless metric.adapters.key?(name)
|
72
|
+
|
72
73
|
instance.register!(metric)
|
73
74
|
end
|
74
75
|
end
|
@@ -86,7 +87,7 @@ module Yabeda
|
|
86
87
|
|
87
88
|
# Perform configuration: registration of metrics and collector blocks
|
88
89
|
# @return [void]
|
89
|
-
# rubocop: disable Metrics/MethodLength
|
90
|
+
# rubocop: disable Metrics/MethodLength
|
90
91
|
def configure!
|
91
92
|
raise(AlreadyConfiguredError, @configured_by) if already_configured?
|
92
93
|
|
@@ -100,8 +101,8 @@ module Yabeda
|
|
100
101
|
|
101
102
|
# Register metrics in adapters after evaluating all configuration blocks
|
102
103
|
# to ensure that all global settings (like default tags) will be applied.
|
103
|
-
|
104
|
-
|
104
|
+
metrics.each_value do |metric|
|
105
|
+
metric.adapters.each_value do |adapter|
|
105
106
|
adapter.register!(metric)
|
106
107
|
end
|
107
108
|
end
|
@@ -129,12 +130,10 @@ module Yabeda
|
|
129
130
|
|
130
131
|
true
|
131
132
|
end
|
132
|
-
# rubocop: enable Metrics/MethodLength, Metrics/AbcSize
|
133
133
|
|
134
134
|
# Forget all the configuration.
|
135
135
|
# For testing purposes as it doesn't rollback changes in adapters.
|
136
136
|
# @api private
|
137
|
-
# rubocop: disable Metrics/AbcSize
|
138
137
|
def reset!
|
139
138
|
default_tags.clear
|
140
139
|
adapters.clear
|
@@ -144,9 +143,12 @@ module Yabeda
|
|
144
143
|
@metrics = nil
|
145
144
|
collectors.clear
|
146
145
|
configurators.clear
|
146
|
+
@config = Config.new
|
147
147
|
instance_variable_set(:@configured_by, nil)
|
148
148
|
instance_variable_set(:@debug_was_enabled_by, nil)
|
149
149
|
end
|
150
|
-
# rubocop: enable Metrics/
|
150
|
+
# rubocop: enable Metrics/MethodLength
|
151
151
|
end
|
152
152
|
end
|
153
|
+
|
154
|
+
require "yabeda/railtie" if defined?(Rails)
|
data/yabeda.gemspec
CHANGED
@@ -29,9 +29,5 @@ Gem::Specification.new do |spec|
|
|
29
29
|
spec.add_dependency "concurrent-ruby"
|
30
30
|
spec.add_dependency "dry-initializer"
|
31
31
|
|
32
|
-
spec.
|
33
|
-
spec.add_development_dependency "rake", "~> 12.0"
|
34
|
-
spec.add_development_dependency "rspec", "~> 3.0"
|
35
|
-
spec.add_development_dependency "yard"
|
36
|
-
spec.add_development_dependency "yard-dry-initializer"
|
32
|
+
spec.required_ruby_version = ">= 2.3"
|
37
33
|
end
|
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.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:
|
11
|
+
date: 2024-10-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: anyway_config
|
@@ -58,76 +58,6 @@ dependencies:
|
|
58
58
|
- - ">="
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: '0'
|
61
|
-
- !ruby/object:Gem::Dependency
|
62
|
-
name: bundler
|
63
|
-
requirement: !ruby/object:Gem::Requirement
|
64
|
-
requirements:
|
65
|
-
- - "~>"
|
66
|
-
- !ruby/object:Gem::Version
|
67
|
-
version: '2.0'
|
68
|
-
type: :development
|
69
|
-
prerelease: false
|
70
|
-
version_requirements: !ruby/object:Gem::Requirement
|
71
|
-
requirements:
|
72
|
-
- - "~>"
|
73
|
-
- !ruby/object:Gem::Version
|
74
|
-
version: '2.0'
|
75
|
-
- !ruby/object:Gem::Dependency
|
76
|
-
name: rake
|
77
|
-
requirement: !ruby/object:Gem::Requirement
|
78
|
-
requirements:
|
79
|
-
- - "~>"
|
80
|
-
- !ruby/object:Gem::Version
|
81
|
-
version: '12.0'
|
82
|
-
type: :development
|
83
|
-
prerelease: false
|
84
|
-
version_requirements: !ruby/object:Gem::Requirement
|
85
|
-
requirements:
|
86
|
-
- - "~>"
|
87
|
-
- !ruby/object:Gem::Version
|
88
|
-
version: '12.0'
|
89
|
-
- !ruby/object:Gem::Dependency
|
90
|
-
name: rspec
|
91
|
-
requirement: !ruby/object:Gem::Requirement
|
92
|
-
requirements:
|
93
|
-
- - "~>"
|
94
|
-
- !ruby/object:Gem::Version
|
95
|
-
version: '3.0'
|
96
|
-
type: :development
|
97
|
-
prerelease: false
|
98
|
-
version_requirements: !ruby/object:Gem::Requirement
|
99
|
-
requirements:
|
100
|
-
- - "~>"
|
101
|
-
- !ruby/object:Gem::Version
|
102
|
-
version: '3.0'
|
103
|
-
- !ruby/object:Gem::Dependency
|
104
|
-
name: yard
|
105
|
-
requirement: !ruby/object:Gem::Requirement
|
106
|
-
requirements:
|
107
|
-
- - ">="
|
108
|
-
- !ruby/object:Gem::Version
|
109
|
-
version: '0'
|
110
|
-
type: :development
|
111
|
-
prerelease: false
|
112
|
-
version_requirements: !ruby/object:Gem::Requirement
|
113
|
-
requirements:
|
114
|
-
- - ">="
|
115
|
-
- !ruby/object:Gem::Version
|
116
|
-
version: '0'
|
117
|
-
- !ruby/object:Gem::Dependency
|
118
|
-
name: yard-dry-initializer
|
119
|
-
requirement: !ruby/object:Gem::Requirement
|
120
|
-
requirements:
|
121
|
-
- - ">="
|
122
|
-
- !ruby/object:Gem::Version
|
123
|
-
version: '0'
|
124
|
-
type: :development
|
125
|
-
prerelease: false
|
126
|
-
version_requirements: !ruby/object:Gem::Requirement
|
127
|
-
requirements:
|
128
|
-
- - ">="
|
129
|
-
- !ruby/object:Gem::Version
|
130
|
-
version: '0'
|
131
61
|
description: 'Collect statistics about how your application is performing with ease.
|
132
62
|
Export metrics to various monitoring systems.
|
133
63
|
|
@@ -138,7 +68,8 @@ executables: []
|
|
138
68
|
extensions: []
|
139
69
|
extra_rdoc_files: []
|
140
70
|
files:
|
141
|
-
- ".github/workflows/
|
71
|
+
- ".github/workflows/lint.yml"
|
72
|
+
- ".github/workflows/release.yml"
|
142
73
|
- ".github/workflows/test.yml"
|
143
74
|
- ".gitignore"
|
144
75
|
- ".rspec"
|
@@ -170,7 +101,9 @@ files:
|
|
170
101
|
- lib/yabeda/rspec/base_matcher.rb
|
171
102
|
- lib/yabeda/rspec/increment_yabeda_counter.rb
|
172
103
|
- lib/yabeda/rspec/measure_yabeda_histogram.rb
|
104
|
+
- lib/yabeda/rspec/observe_yabeda_summary.rb
|
173
105
|
- lib/yabeda/rspec/update_yabeda_gauge.rb
|
106
|
+
- lib/yabeda/summary.rb
|
174
107
|
- lib/yabeda/tags.rb
|
175
108
|
- lib/yabeda/test_adapter.rb
|
176
109
|
- lib/yabeda/testing.rb
|
@@ -189,14 +122,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
189
122
|
requirements:
|
190
123
|
- - ">="
|
191
124
|
- !ruby/object:Gem::Version
|
192
|
-
version: '
|
125
|
+
version: '2.3'
|
193
126
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
194
127
|
requirements:
|
195
128
|
- - ">="
|
196
129
|
- !ruby/object:Gem::Version
|
197
130
|
version: '0'
|
198
131
|
requirements: []
|
199
|
-
rubygems_version: 3.
|
132
|
+
rubygems_version: 3.5.16
|
200
133
|
signing_key:
|
201
134
|
specification_version: 4
|
202
135
|
summary: Extensible framework for collecting metric for your Ruby application
|