checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
---
2
2
SHA256:
3
- metadata.gz: 518d9350ed026801311bdc695857fdd057856f6b7fe40715bf8072c990e8e5b8
4
- data.tar.gz: 7d36fbd3e63994789ce8ac04212fb44c30041fd33f7173fbfdf1a6257beea99a
3
+ metadata.gz: 0affbdfcda841ffe4eec9105ded5526b13d209ccf0aa564e1b84c91651079d0e
4
+ data.tar.gz: 8c0f3879ee0f2cedeba7f6b0526f679ef9962e2270c7639837821f226a0f3580
5
5
SHA512:
6
- metadata.gz: 42e990916d474ed52f08330bd7210b4c6f464a0199354cfc1fe3710817b9d57790d445d0d1f88c8fccd6daeb627638a80dd8b578c685544f6a4f9c7765522a5f
7
- data.tar.gz: 5b49eec021acaef39af2c9401681fa85f02d368f36231a60a64faf595e5b0c1432ce07a67fb86ce9c8b3041835fd4480da86ea5d91f7ca0939ef5b40592e6a49
6
+ metadata.gz: 28d6883722e140005b24071ab637104d6d0e16e22a75fffac734b242fafbb88c1ecdbfd4d4d44a11439a8cbac5d0ad5dcfba705ed26a706f6abb83f55f3f5513
7
+ data.tar.gz: b02ddcabf2669f9458a21dd37e0495218eedff6f15047c923b81d6fbcd311ce34ed2d27c7ea664a97aebaf806eac653dd4ec09fb3ee526607da796ec4c3a54f2
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
3
3
[![Gem Version](https://badge.fury.io/rb/ezmetrics.svg)](https://badge.fury.io/rb/ezmetrics)
4
4
5
- A simple tool for capturing and displaying Rails metrics.
5
+ Simple, lightweight and fast metrics aggregation for Rails.
6
6
7
7
## Installation
8
8
@@ -28,12 +28,24 @@ You can change the timeframe according to your needs and save the metrics by cal
28
28
29
29
```ruby
30
30
# Store the metrics for 60 seconds (default behaviour)
31
- EZmetrics.new.log(duration: 100.5, views: 40.7, db: 59.8, queries: 4, status: 200)
31
+ EZmetrics.new.log(
32
+ duration: 100.5,
33
+ views: 40.7,
34
+ db: 59.8,
35
+ queries: 4,
36
+ status: 200
37
+ )
32
38
```
33
39
34
40
```ruby
35
41
# Store the metrics for 10 minutes
36
- EZmetrics.new(10.minutes).log(duration: 100.5, views: 40.7, db: 59.8, queries: 4, status: 200)
42
+ EZmetrics.new(10.minutes).log(
43
+ duration: 100.5,
44
+ views: 40.7,
45
+ db: 59.8,
46
+ queries: 4,
47
+ status: 200
48
+ )
37
49
```
38
50
39
51
---
@@ -50,7 +62,7 @@ For displaying metrics you need to call `show` method:
50
62
EZmetrics.new(10.minutes).show
51
63
```
52
64
53
- > Please note that you can combine these timeframes, for example - store for 10 minutes, display for 5 minutes.
65
+ You can combine these timeframes, for example - store for 10 minutes, display for 5 minutes.
54
66
55
67
### Capture metrics
56
68
@@ -119,7 +131,7 @@ This will return a hash with the following structure:
119
131
}
120
132
```
121
133
122
- ### Aggregation options
134
+ ### Aggregation
123
135
124
136
The aggregation can be easily configured by specifying aggregation options as in the following examples:
125
137
@@ -205,6 +217,59 @@ EZmetrics.new.show(views: :avg, :db: [:avg, :max], requests: true)
205
217
}
206
218
```
207
219
220
+ ### Partitioning
221
+
222
+ To aggregate metrics, partitioned by a unit of time you need to call `partition_by({time_unit})` before calling `show`
223
+
224
+ ```ruby
225
+ # Aggregate metrics for last hour, partition by minute
226
+ EZmetrics.new(1.hour).partition_by(:minute).show(duration: [:avg, :max], db: :avg)
227
+ ```
228
+
229
+ This will return an array of objects with the following structure:
230
+
231
+ ```ruby
232
+ [
233
+ {
234
+ timestamp: # UNIX timestamp
235
+ data: # a hash with aggregated metrics
236
+ }
237
+ ]
238
+ ```
239
+
240
+ like in the example below:
241
+
242
+ ```ruby
243
+ [
244
+ {
245
+ timestamp: 1575242880,
246
+ data: {
247
+ duration: {
248
+ avg: 477,
249
+ max: 8566
250
+ },
251
+ db: {
252
+ avg: 387
253
+ }
254
+ }
255
+ },
256
+ {
257
+ timestamp: 1575242940,
258
+ data: {
259
+ duration: {
260
+ avg: 234,
261
+ max: 3675
262
+ },
263
+ db: {
264
+ avg: 123
265
+ }
266
+ }
267
+ }
268
+ ]
269
+ ```
270
+
271
+ Available time units for partitioning: `second`, `minute`, `hour`, `day`. Default: `minute`.
272
+
208
273
### Performance
209
274
210
275
The aggregation speed relies on the performance of **Redis** (data storage) and **Oj** (json serialization/parsing).
@@ -215,8 +280,6 @@ You can check the **aggregation** time by running:
215
280
EZmetrics::Benchmark.new.measure_aggregation
216
281
```
217
282
218
- The result of running this benchmark on a _2017 Macbook Pro 2.9 GHz Intel Core i7 with 16 GB of RAM_:
219
-
220
283
| Interval | Duration (seconds) |
221
284
| :------: | :----------------: |
222
285
| 1 minute | 0.0 |
@@ -224,3 +287,21 @@ The result of running this benchmark on a _2017 Macbook Pro 2.9 GHz Intel Core i
224
287
| 12 hours | 0.49 |
225
288
| 24 hours | 1.51 |
226
289
| 48 hours | 3.48 |
290
+
291
+ ---
292
+
293
+ To check the **partitioned aggregation** time you need to run:
294
+
295
+ ```ruby
296
+ EZmetrics::Benchmark.new.measure_aggregation(:minute)
297
+ ```
298
+
299
+ | Interval | Duration (seconds) |
300
+ | :------: | :----------------: |
301
+ | 1 minute | 0.0 |
302
+ | 1 hour | 0.05 |
303
+ | 12 hours | 0.74 |
304
+ | 24 hours | 2.12 |
305
+ | 48 hours | 4.85 |
306
+
307
+ The benchmarks above were run on a _2017 Macbook Pro 2.9 GHz Intel Core i7 with 16 GB of RAM_
data/lib/ezmetrics.rb CHANGED
@@ -5,6 +5,7 @@ require "oj"
5
5
class EZmetrics
6
6
METRICS = [:duration, :views, :db, :queries].freeze
7
7
AGGREGATION_FUNCTIONS = [:max, :avg].freeze
8
+ PARTITION_UNITS = [:second, :minute, :hour, :day].freeze
8
9
9
10
def initialize(interval_seconds=60)
10
11
@interval_seconds = interval_seconds.to_i
@@ -36,6 +37,7 @@ class EZmetrics
36
37
this_second_metrics["statuses"][status_group] += 1
37
38
else
38
39
@this_second_metrics = {
40
+ "second" => this_second,
39
41
"duration_sum" => safe_payload[:duration],
40
42
"duration_max" => safe_payload[:duration],
41
43
"views_sum" => safe_payload[:views],
@@ -57,23 +59,38 @@ class EZmetrics
57
59
end
58
60
59
61
def show(options=nil)
60
- @options = options || default_options
61
- interval_start = Time.now.to_i - interval_seconds
62
- interval_keys = (interval_start..Time.now.to_i).to_a
62
+ @options = options || default_options
63
+ partitioned_metrics ? aggregate_partitioned_data : aggregate_data
64
+ end
63
- @interval_metrics = redis.mget(interval_keys).compact.map { |hash| Oj.load(hash) }
64
65
65
- return {} unless interval_metrics.any?
66
+ def partition_by(time_unit=:minute)
67
+ time_unit = PARTITION_UNITS.include?(time_unit) ? time_unit : :minute
68
+ @partitioned_metrics = interval_metrics.group_by { |h| second_to_partition_unit(time_unit, h["second"]) }
69
+ self
70
+ end
71
+
72
+ private
66
73
74
+ attr_reader :redis, :interval_seconds, :interval_metrics, :requests,
75
+ :storage_key, :safe_payload, :this_second_metrics, :partitioned_metrics, :options
76
+
77
+ def aggregate_data
78
+ return {} unless interval_metrics.any?
67
79
@requests = interval_metrics.sum { |hash| hash["statuses"]["all"] }
68
80
build_result
69
81
rescue
70
82
{}
71
83
end
72
84
73
- private
74
-
75
- attr_reader :redis, :interval_seconds, :interval_metrics, :requests,
76
- :storage_key, :safe_payload, :this_second_metrics, :options
85
+ def aggregate_partitioned_data
86
+ partitioned_metrics.map do |partition, metrics|
87
+ @interval_metrics = metrics
88
+ @requests = interval_metrics.sum { |hash| hash["statuses"]["all"] }
89
+ { timestamp: partition, data: build_result }
90
+ end
91
+ rescue
92
+ new(options)
93
+ end
77
94
78
95
def build_result
79
96
result = {}
@@ -95,6 +112,22 @@ class EZmetrics
95
112
result
96
113
end
97
114
115
+ def second_to_partition_unit(time_unit, second)
116
+ return second if time_unit == :second
117
+ time_unit_depth = { minute: 4, hour: 3, day: 2 }
118
+ reset_depth = time_unit_depth[time_unit]
119
+ time_to_array = Time.at(second).to_a[0..5].reverse
120
+ Time.new(*time_to_array[0..reset_depth]).to_i
121
+ end
122
+
123
+ def interval_metrics
124
+ @interval_metrics ||= begin
125
+ interval_start = Time.now.to_i - interval_seconds
126
+ interval_keys = (interval_start..Time.now.to_i).to_a
127
+ redis.mget(interval_keys).compact.map { |hash| Oj.load(hash) }
128
+ end
129
+ end
130
+
98
131
def aggregate(metrics, aggregation_function)
99
132
return unless AGGREGATION_FUNCTIONS.include?(aggregation_function)
100
133
return avg("#{metrics}_sum") if aggregation_function == :avg
data/lib/ezmetrics/benchmark.rb CHANGED
@@ -16,11 +16,11 @@ class EZmetrics::Benchmark
16
16
}
17
17
end
18
18
19
- def measure_aggregation
19
+ def measure_aggregation(partition_by=nil)
20
20
write_metrics
21
21
print_header
22
22
intervals.each do |interval, seconds|
23
- result = measure_aggregation_time(interval, seconds)
23
+ result = measure_aggregation_time(interval, seconds, partition_by)
24
24
print_row(result)
25
25
end
26
26
cleanup_metrics
@@ -36,6 +36,7 @@ class EZmetrics::Benchmark
36
36
seconds.times do |i|
37
37
second = start - i
38
38
payload = {
39
+ "second" => second,
39
40
"duration_sum" => rand(10000),
40
41
"duration_max" => rand(10000),
41
42
"views_sum" => rand(1000),
@@ -45,11 +46,11 @@ class EZmetrics::Benchmark
45
46
"queries_sum" => rand(100),
46
47
"queries_max" => rand(100),
47
48
"statuses" => {
48
- "2xx" => rand(10),
49
- "3xx" => rand(10),
50
- "4xx" => rand(10),
51
- "5xx" => rand(10),
52
- "all" => rand(40)
49
+ "2xx" => rand(1..10),
50
+ "3xx" => rand(1..10),
51
+ "4xx" => rand(1..10),
52
+ "5xx" => rand(1..10),
53
+ "all" => rand(1..40)
53
54
}
54
55
}
55
56
redis.setex(second, seconds, Oj.dump(payload))
@@ -63,9 +64,15 @@ class EZmetrics::Benchmark
63
64
redis.del(interval_keys)
64
65
end
65
66
66
- def measure_aggregation_time(interval, seconds)
67
+ def measure_aggregation_time(interval, seconds, partition_by)
67
68
iterations.times do
68
- durations << ::Benchmark.measure { EZmetrics.new(seconds).show }.real
69
+ durations << ::Benchmark.measure do
70
+ if partition_by
71
+ EZmetrics.new(seconds).partition_by(partition_by).show
72
+ else
73
+ EZmetrics.new(seconds).show
74
+ end
75
+ end.real
69
76
end
70
77
71
78
return {
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
--- !ruby/object:Gem::Specification
2
2
name: ezmetrics
3
3
version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
platform: ruby
6
6
authors:
7
7
- Nicolae Rotaru
@@ -66,7 +66,7 @@ dependencies:
66
66
- - "~>"
67
67
- !ruby/object:Gem::Version
68
68
version: '3.5'
69
- description: A simple tool for capturing and displaying Rails metrics.
69
+ description: Simple, lightweight and fast metrics aggregation for Rails.
70
70
email: nyku.rn@gmail.com
71
71
executables: []
72
72
extensions: []
@@ -76,10 +76,11 @@ files:
76
76
- README.md
77
77
- lib/ezmetrics.rb
78
78
- lib/ezmetrics/benchmark.rb
79
- homepage: https://github.com/nyku/ezmetrics
79
+ homepage: https://nyku.github.io/ezmetrics
80
80
licenses:
81
81
- GPL-3.0
82
- metadata: {}
82
+ metadata:
83
+ source_code_uri: https://github.com/nyku/ezmetrics
83
84
post_install_message:
84
85
rdoc_options: []
85
86
require_paths: