ezmetrics 1.0.6 → 1.1.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 +5 -5
- data/README.md +88 -7
- data/lib/ezmetrics.rb +64 -67
- data/lib/ezmetrics/benchmark.rb +13 -12
- metadata +30 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5ba2e24bca76b2786c1264b779e72764e123094150855cae241d673505df95ad
|
4
|
+
data.tar.gz: 3c297b92a53c51b8b881fbbafb15929b9bac36eb53164c3faab37c2caa72db02
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d1e8118d48cac15dca166c6c4f0d8e38344b28bfcc85343fa87ff4877d34bc7f12b9841241de563723261df5a48210cf1838c09eece3bc15d5f2dde6a58c8c2
|
7
|
+
data.tar.gz: 8e62b7b53f73a6343c8fd6f11e0e8a9f856314dd262c10611cd9bf061636c8801255de58818a1736069679525bed73c949e6f61778910de8a306da30c352e2a2
|
data/README.md
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
|
5
5
|
A simple tool for capturing and displaying Rails metrics.
|
6
6
|
|
7
|
-
|
8
7
|
## Installation
|
9
8
|
|
10
9
|
```
|
@@ -16,6 +15,7 @@ gem 'ezmetrics'
|
|
16
15
|
### Getting started
|
17
16
|
|
18
17
|
This tool captures and aggregates Rails application metrics such as
|
18
|
+
|
19
19
|
- `duration`
|
20
20
|
- `views`
|
21
21
|
- `db`
|
@@ -30,6 +30,7 @@ You can change the timeframe according to your needs and save the metrics by cal
|
|
30
30
|
# Store the metrics for 60 seconds (default behaviour)
|
31
31
|
EZmetrics.new.log(duration: 100.5, views: 40.7, db: 59.8, queries: 4, status: 200)
|
32
32
|
```
|
33
|
+
|
33
34
|
or
|
34
35
|
|
35
36
|
```ruby
|
@@ -53,7 +54,6 @@ or
|
|
53
54
|
|
54
55
|
> Please note that you can combine these timeframes, for example - store for 10 minutes, display for 5 minutes.
|
55
56
|
|
56
|
-
|
57
57
|
### Capture metrics
|
58
58
|
|
59
59
|
Just add an initializer to your application:
|
@@ -121,6 +121,86 @@ This will return a hash with the following structure:
|
|
121
121
|
}
|
122
122
|
```
|
123
123
|
|
124
|
+
### Output configuration
|
125
|
+
|
126
|
+
The output can be easily configured by specifying aggregation options as in the following examples:
|
127
|
+
|
128
|
+
1. Single
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
EZmetrics.new.show(duration: :max)
|
132
|
+
```
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
{
|
136
|
+
duration: {
|
137
|
+
max: 9675
|
138
|
+
}
|
139
|
+
}
|
140
|
+
```
|
141
|
+
|
142
|
+
2. Multiple
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
EZmetrics.new.show(queries: [:max, :avg])
|
146
|
+
```
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
{
|
150
|
+
queries: {
|
151
|
+
max: 76,
|
152
|
+
avg: 26
|
153
|
+
}
|
154
|
+
}
|
155
|
+
```
|
156
|
+
|
157
|
+
3. Requests
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
EZmetrics.new.show(requests: true)
|
161
|
+
```
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
{
|
165
|
+
requests: {
|
166
|
+
all: 2000,
|
167
|
+
grouped: {
|
168
|
+
"2xx" => 1900,
|
169
|
+
"3xx" => 15,
|
170
|
+
"4xx" => 80,
|
171
|
+
"5xx" => 5
|
172
|
+
}
|
173
|
+
}
|
174
|
+
}
|
175
|
+
```
|
176
|
+
|
177
|
+
4. Combined
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
EZmetrics.new.show(views: :avg, :db: [:avg, :max], requests: true)
|
181
|
+
```
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
{
|
185
|
+
views: {
|
186
|
+
avg: 12
|
187
|
+
},
|
188
|
+
db: {
|
189
|
+
avg: 155,
|
190
|
+
max: 4382
|
191
|
+
},
|
192
|
+
requests: {
|
193
|
+
all: 2000,
|
194
|
+
grouped: {
|
195
|
+
"2xx" => 1900,
|
196
|
+
"3xx" => 15,
|
197
|
+
"4xx" => 80,
|
198
|
+
"5xx" => 5
|
199
|
+
}
|
200
|
+
}
|
201
|
+
}
|
202
|
+
```
|
203
|
+
|
124
204
|
### Performance
|
125
205
|
|
126
206
|
The implementation is based on **Redis** commands such as:
|
@@ -137,11 +217,12 @@ You can check the **aggregation** time by running:
|
|
137
217
|
EZmetrics::Benchmark.new.measure_aggregation
|
138
218
|
```
|
139
219
|
|
140
|
-
The result of running this benchmark on a
|
220
|
+
The result of running this benchmark on a _2017 Macbook Pro 2.9 GHz Intel Core i7 with 16 GB of RAM_:
|
141
221
|
|
142
222
|
| Interval | Duration (seconds) |
|
143
|
-
|
223
|
+
| :------: | :----------------: |
|
144
224
|
| 1 minute | 0.0 |
|
145
|
-
| 1 hour | 0.
|
146
|
-
| 12 hours |
|
147
|
-
| 24 hours |
|
225
|
+
| 1 hour | 0.05 |
|
226
|
+
| 12 hours | 0.66 |
|
227
|
+
| 24 hours | 1.83 |
|
228
|
+
| 48 hours | 4.06 |
|
data/lib/ezmetrics.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
-
require "redis"
|
2
|
-
require "
|
1
|
+
require "redis"
|
2
|
+
require "redis/connection/hiredis"
|
3
|
+
require "oj"
|
3
4
|
|
4
5
|
class EZmetrics
|
6
|
+
METRICS = [:duration, :views, :db, :queries].freeze
|
7
|
+
AGGREGATION_FUNCTIONS = [:max, :avg].freeze
|
8
|
+
|
5
9
|
def initialize(interval_seconds=60)
|
6
10
|
@interval_seconds = interval_seconds.to_i
|
7
11
|
@redis = Redis.new
|
@@ -22,9 +26,9 @@ class EZmetrics
|
|
22
26
|
@this_second_metrics = redis.get("#{storage_key}:#{this_second}")
|
23
27
|
|
24
28
|
if this_second_metrics
|
25
|
-
@this_second_metrics =
|
29
|
+
@this_second_metrics = Oj.load(this_second_metrics)
|
26
30
|
|
27
|
-
|
31
|
+
METRICS.each do |metrics_type|
|
28
32
|
update_sum(metrics_type)
|
29
33
|
update_max(metrics_type)
|
30
34
|
end
|
@@ -47,31 +51,66 @@ class EZmetrics
|
|
47
51
|
this_second_metrics["statuses"][status_group] = 1
|
48
52
|
end
|
49
53
|
|
50
|
-
redis.setex("#{storage_key}:#{this_second}", interval_seconds,
|
51
|
-
|
54
|
+
redis.setex("#{storage_key}:#{this_second}", interval_seconds, Oj.dump(this_second_metrics))
|
52
55
|
true
|
53
56
|
rescue => error
|
54
57
|
formatted_error(error)
|
55
58
|
end
|
56
59
|
|
57
|
-
def show
|
60
|
+
def show(options=nil)
|
61
|
+
@options = options || default_options
|
58
62
|
interval_start = Time.now.to_i - interval_seconds
|
59
63
|
interval_keys = (interval_start..Time.now.to_i).to_a.map { |second| "#{storage_key}:#{second}" }
|
60
|
-
@interval_metrics = redis.mget(interval_keys).compact.map { |hash|
|
64
|
+
@interval_metrics = redis.mget(interval_keys).compact.map { |hash| Oj.load(hash) }
|
61
65
|
|
62
|
-
return
|
66
|
+
return {} unless interval_metrics.any?
|
63
67
|
|
64
68
|
@requests = interval_metrics.map { |hash| hash["statuses"]["all"] }.compact.sum
|
65
|
-
|
66
|
-
metrics_object
|
69
|
+
build_result
|
67
70
|
rescue
|
68
|
-
|
71
|
+
{}
|
69
72
|
end
|
70
73
|
|
71
74
|
private
|
72
75
|
|
73
|
-
attr_reader :redis, :interval_seconds, :interval_metrics, :requests,
|
74
|
-
:safe_payload, :this_second_metrics
|
76
|
+
attr_reader :redis, :interval_seconds, :interval_metrics, :requests,
|
77
|
+
:storage_key, :safe_payload, :this_second_metrics, :options
|
78
|
+
|
79
|
+
def build_result
|
80
|
+
result = {}
|
81
|
+
|
82
|
+
if options[:requests]
|
83
|
+
result[:requests] = {
|
84
|
+
all: requests,
|
85
|
+
grouped: {
|
86
|
+
"2xx" => count("2xx"),
|
87
|
+
"3xx" => count("3xx"),
|
88
|
+
"4xx" => count("4xx"),
|
89
|
+
"5xx" => count("5xx")
|
90
|
+
}
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
options.each do |metrics, aggregation_functions|
|
95
|
+
next unless METRICS.include?(metrics)
|
96
|
+
aggregation_functions = [aggregation_functions] unless aggregation_functions.is_a?(Array)
|
97
|
+
next unless aggregation_functions.any?
|
98
|
+
|
99
|
+
aggregation_functions.each do |aggregation_function|
|
100
|
+
result[metrics] ||= {}
|
101
|
+
result[metrics][aggregation_function] = aggregate(metrics, aggregation_function)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
result
|
105
|
+
ensure
|
106
|
+
result
|
107
|
+
end
|
108
|
+
|
109
|
+
def aggregate(metrics, aggregation_function)
|
110
|
+
return unless AGGREGATION_FUNCTIONS.include?(aggregation_function)
|
111
|
+
return avg("#{metrics}_sum".to_sym) if aggregation_function == :avg
|
112
|
+
return max("#{metrics}_max".to_sym) if aggregation_function == :max
|
113
|
+
end
|
75
114
|
|
76
115
|
def update_sum(metrics)
|
77
116
|
this_second_metrics["#{metrics}_sum"] += safe_payload[metrics.to_sym]
|
@@ -94,65 +133,23 @@ class EZmetrics
|
|
94
133
|
interval_metrics.map { |h| h["statuses"][group.to_s] }.sum
|
95
134
|
end
|
96
135
|
|
97
|
-
def
|
136
|
+
def default_options
|
98
137
|
{
|
99
|
-
|
100
|
-
|
101
|
-
|
138
|
+
duration: AGGREGATION_FUNCTIONS,
|
139
|
+
views: AGGREGATION_FUNCTIONS,
|
140
|
+
db: AGGREGATION_FUNCTIONS,
|
141
|
+
queries: AGGREGATION_FUNCTIONS,
|
142
|
+
requests: true
|
102
143
|
}
|
103
144
|
end
|
104
145
|
|
105
|
-
def
|
106
|
-
{
|
107
|
-
duration: {
|
108
|
-
avg: avg(:duration_sum),
|
109
|
-
max: max(:duration_max)
|
110
|
-
},
|
111
|
-
views: {
|
112
|
-
avg: avg(:views_sum),
|
113
|
-
max: max(:views_max)
|
114
|
-
},
|
115
|
-
db: {
|
116
|
-
avg: avg(:db_sum),
|
117
|
-
max: max(:db_max)
|
118
|
-
},
|
119
|
-
queries: {
|
120
|
-
avg: avg(:queries_sum),
|
121
|
-
max: max(:queries_max)
|
122
|
-
},
|
123
|
-
requests: {
|
124
|
-
all: requests,
|
125
|
-
grouped: {
|
126
|
-
"2xx" => count("2xx"),
|
127
|
-
"3xx" => count("3xx"),
|
128
|
-
"4xx" => count("4xx"),
|
129
|
-
"5xx" => count("5xx")
|
130
|
-
}
|
131
|
-
}
|
132
|
-
}
|
133
|
-
end
|
134
|
-
|
135
|
-
def empty_metrics_object
|
146
|
+
def formatted_error(error)
|
136
147
|
{
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
},
|
141
|
-
views: {
|
142
|
-
avg: 0,
|
143
|
-
max: 0
|
144
|
-
},
|
145
|
-
db: {
|
146
|
-
avg: 0,
|
147
|
-
max: 0
|
148
|
-
},
|
149
|
-
queries: {
|
150
|
-
avg: 0,
|
151
|
-
max: 0
|
152
|
-
},
|
153
|
-
requests: {}
|
148
|
+
error: error.class.name,
|
149
|
+
message: error.message,
|
150
|
+
backtrace: error.backtrace.reject { |line| line.match(/ruby|gems/) }
|
154
151
|
}
|
155
152
|
end
|
156
153
|
end
|
157
154
|
|
158
|
-
require "ezmetrics/benchmark"
|
155
|
+
require "ezmetrics/benchmark"
|
data/lib/ezmetrics/benchmark.rb
CHANGED
@@ -11,7 +11,8 @@ class EZmetrics::Benchmark
|
|
11
11
|
"1.minute" => 60,
|
12
12
|
"1.hour " => 3600,
|
13
13
|
"12.hours" => 43200,
|
14
|
-
"24.hours" => 86400
|
14
|
+
"24.hours" => 86400,
|
15
|
+
"48.hours" => 172800
|
15
16
|
}
|
16
17
|
end
|
17
18
|
|
@@ -35,15 +36,15 @@ class EZmetrics::Benchmark
|
|
35
36
|
seconds.times do |i|
|
36
37
|
second = start - i
|
37
38
|
payload = {
|
38
|
-
"duration_sum"
|
39
|
-
"duration_max"
|
40
|
-
"views_sum"
|
41
|
-
"views_max"
|
42
|
-
"db_sum"
|
43
|
-
"db_max"
|
44
|
-
"queries_sum"
|
45
|
-
"queries_max"
|
46
|
-
"statuses"
|
39
|
+
"duration_sum" => rand(10000),
|
40
|
+
"duration_max" => rand(10000),
|
41
|
+
"views_sum" => rand(1000),
|
42
|
+
"views_max" => rand(1000),
|
43
|
+
"db_sum" => rand(8000),
|
44
|
+
"db_max" => rand(8000),
|
45
|
+
"queries_sum" => rand(100),
|
46
|
+
"queries_max" => rand(100),
|
47
|
+
"statuses" => {
|
47
48
|
"2xx" => rand(10),
|
48
49
|
"3xx" => rand(10),
|
49
50
|
"4xx" => rand(10),
|
@@ -51,7 +52,7 @@ class EZmetrics::Benchmark
|
|
51
52
|
"all" => rand(40)
|
52
53
|
}
|
53
54
|
}
|
54
|
-
redis.setex("ez-metrics:#{second}", seconds,
|
55
|
+
redis.setex("ez-metrics:#{second}", seconds, Oj.dump(payload))
|
55
56
|
end
|
56
57
|
nil
|
57
58
|
end
|
@@ -84,4 +85,4 @@ class EZmetrics::Benchmark
|
|
84
85
|
def print_footer
|
85
86
|
print "#{'─'*31}\n"
|
86
87
|
end
|
87
|
-
end
|
88
|
+
end
|
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.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nicolae Rotaru
|
@@ -24,6 +24,34 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '4.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: hiredis
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.6.3
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.6.3
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: oj
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.10'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.10'
|
27
55
|
- !ruby/object:Gem::Dependency
|
28
56
|
name: rspec
|
29
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -67,8 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
67
95
|
- !ruby/object:Gem::Version
|
68
96
|
version: '0'
|
69
97
|
requirements: []
|
70
|
-
|
71
|
-
rubygems_version: 2.6.13
|
98
|
+
rubygems_version: 3.0.6
|
72
99
|
signing_key:
|
73
100
|
specification_version: 4
|
74
101
|
summary: Rails metrics aggregation tool.
|