ezmetrics 1.1.1 → 1.2.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/README.md +88 -7
- data/lib/ezmetrics.rb +42 -9
- data/lib/ezmetrics/benchmark.rb +16 -9
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0affbdfcda841ffe4eec9105ded5526b13d209ccf0aa564e1b84c91651079d0e
|
4
|
+
data.tar.gz: 8c0f3879ee0f2cedeba7f6b0526f679ef9962e2270c7639837821f226a0f3580
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 28d6883722e140005b24071ab637104d6d0e16e22a75fffac734b242fafbb88c1ecdbfd4d4d44a11439a8cbac5d0ad5dcfba705ed26a706f6abb83f55f3f5513
|
7
|
+
data.tar.gz: b02ddcabf2669f9458a21dd37e0495218eedff6f15047c923b81d6fbcd311ce34ed2d27c7ea664a97aebaf806eac653dd4ec09fb3ee526607da796ec4c3a54f2
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/ezmetrics)
|
4
4
|
|
5
|
-
|
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(
|
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(
|
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
|
-
|
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
|
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
|
61
|
-
|
62
|
-
|
63
|
-
@interval_metrics = redis.mget(interval_keys).compact.map { |hash| Oj.load(hash) }
|
62
|
+
@options = options || default_options
|
63
|
+
partitioned_metrics ? aggregate_partitioned_data : aggregate_data
|
64
|
+
end
|
64
65
|
|
65
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
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.
|
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:
|
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.
|
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:
|