Class: RequestLogAnalyzer::Tracker::NumericValue
- Defined in:
- lib/request_log_analyzer/tracker/numeric_value.rb
Instance Attribute Summary collapse
-
#categories ⇒ Object
readonly
Returns the value of attribute categories.
Attributes inherited from Base
Instance Method Summary collapse
-
#bucket_average_value(index) ⇒ Object
Returns the average of the lower and upper bound of the bucket.
-
#bucket_index(value) ⇒ Object
Returns the bucket index for a value.
-
#bucket_interval(index) ⇒ Object
Returns the range of values for a bucket.
-
#bucket_lower_bound(index) ⇒ Object
Returns the lower value of a bucket given its index.
-
#bucket_upper_bound(index) ⇒ Object
Returns the upper value of a bucket given its index.
-
#bucket_value(index, type = nil) ⇒ Object
Returns a single value representing a bucket.
-
#bucketize(category, value) ⇒ Object
Records a hit on a bucket that includes the given value.
-
#display_value(value) ⇒ Object
Display a value.
-
#hits(cat) ⇒ Object
Get the number of hits of a specific category.
-
#hits_overall ⇒ Object
Get the total hits of a all categories.
-
#max(cat) ⇒ Object
Get the maximum duration of a specific category.
-
#mean(cat) ⇒ Object
Get the average duration of a specific category.
-
#mean_overall ⇒ Object
Get the average duration of a all categories.
- #median(category) ⇒ Object
-
#min(cat) ⇒ Object
Get the minimal duration of a specific category.
- #percentile(category, x, type = nil) ⇒ Object
-
#percentile_index(category, x, inclusive = false) ⇒ Object
Returns the upper bound value that would include x% of the hits.
- #percentile_indices(category, start, finish) ⇒ Object
-
#percentile_interval(category, x) ⇒ Object
Returns a percentile interval, i.e.
-
#prepare ⇒ Object
Sets up the numeric value tracker.
-
#report(output) ⇒ Object
Generate a request report to the given output object By default colulative and average duration are generated.
-
#report_table(output, sort, options = {}, &_block) ⇒ Object
Block function to build a result table using a provided sorting function.
-
#sorted_by(by = nil) ⇒ Object
Return categories sorted by a given key.
-
#statistics_header(options) ⇒ Object
Returns the column header for a statistics table to report on the statistics result.
-
#statistics_row(cat) ⇒ Object
Returns a row of statistics information for a report table, given a category.
-
#stddev(cat) ⇒ Object
Get the standard deviation of the duration of a specific category.
-
#sum(cat) ⇒ Object
Get the total duration of a specific category.
-
#sum_overall ⇒ Object
Get the cumlative duration of a all categories.
-
#title ⇒ Object
Returns the title of this tracker for reports.
-
#to_yaml_object ⇒ Object
Returns all the categories and the tracked duration as a hash than can be exported to YAML.
-
#update(request) ⇒ Object
Get the value information from the request and store it in the respective categories.
-
#update_statistics(category, number) ⇒ Object
Update the running calculation of statistics with the newly found numeric value.
-
#variance(cat) ⇒ Object
Get the variance of the duration of a specific category.
Methods inherited from Base
#create_lambda, #finalize, #initialize, #setup_should_update_checks!, #should_update?
Constructor Details
This class inherits a constructor from RequestLogAnalyzer::Tracker::Base
Instance Attribute Details
#categories ⇒ Object (readonly)
Returns the value of attribute categories.
3 4 5 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 3 def categories @categories end |
Instance Method Details
#bucket_average_value(index) ⇒ Object
Returns the average of the lower and upper bound of the bucket.
147 148 149 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 147 def bucket_average_value(index) (bucket_lower_bound(index) + bucket_upper_bound(index)) / 2 end |
#bucket_index(value) ⇒ Object
Returns the bucket index for a value
129 130 131 132 133 134 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 129 def bucket_index(value) return 0 if value < @min_bucket_value return @number_of_buckets - 1 if value >= @max_bucket_value ((Math.log(value) - Math.log(@min_bucket_value)) / @bucket_size).floor end |
#bucket_interval(index) ⇒ Object
Returns the range of values for a bucket.
161 162 163 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 161 def bucket_interval(index) Range.new(bucket_lower_bound(index), bucket_upper_bound(index), true) end |
#bucket_lower_bound(index) ⇒ Object
Returns the lower value of a bucket given its index
137 138 139 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 137 def bucket_lower_bound(index) Math.exp((index * @bucket_size) + Math.log(@min_bucket_value)) end |
#bucket_upper_bound(index) ⇒ Object
Returns the upper value of a bucket given its index
142 143 144 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 142 def bucket_upper_bound(index) bucket_lower_bound(index + 1) end |
#bucket_value(index, type = nil) ⇒ Object
Returns a single value representing a bucket.
152 153 154 155 156 157 158 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 152 def bucket_value(index, type = nil) case type when :begin, :start, :lower, :lower_bound then bucket_lower_bound(index) when :end, :finish, :upper, :upper_bound then bucket_upper_bound(index) else bucket_average_value(index) end end |
#bucketize(category, value) ⇒ Object
Records a hit on a bucket that includes the given value.
166 167 168 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 166 def bucketize(category, value) @categories[category][:buckets][bucket_index(value)] += 1 end |
#display_value(value) ⇒ Object
Display a value
73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 73 def display_value(value) return '- ' if value.nil? return '0 ' if value.zero? case [Math.log10(value.abs).floor, 0].max when 0...4 then '%d ' % value when 4...7 then '%dk' % (value / 1000) when 7...10 then '%dM' % (value / 1_000_000) when 10...13 then '%dG' % (value / 1_000_000_000) when 13...16 then '%dT' % (value / 1_000_000_000_000) else '%dP' % (value / 1_000_000_000_000_000) end end |
#hits(cat) ⇒ Object
Get the number of hits of a specific category. cat The category
244 245 246 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 244 def hits(cat) @categories[cat][:hits] end |
#hits_overall ⇒ Object
Get the total hits of a all categories.
296 297 298 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 296 def hits_overall @categories.reduce(0) { |sum, (_, cat)| sum + cat[:hits] } end |
#max(cat) ⇒ Object
Get the maximum duration of a specific category. cat The category
262 263 264 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 262 def max(cat) @categories[cat][:max] end |
#mean(cat) ⇒ Object
Get the average duration of a specific category. cat The category
268 269 270 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 268 def mean(cat) @categories[cat][:mean] end |
#mean_overall ⇒ Object
Get the average duration of a all categories.
286 287 288 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 286 def mean_overall sum_overall / hits_overall end |
#median(category) ⇒ Object
199 200 201 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 199 def median(category) percentile(category, 50, :average) end |
#min(cat) ⇒ Object
Get the minimal duration of a specific category. cat The category
256 257 258 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 256 def min(cat) @categories[cat][:min] end |
#percentile(category, x, type = nil) ⇒ Object
195 196 197 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 195 def percentile(category, x, type = nil) bucket_value(percentile_index(category, x, type == :upper), type) end |
#percentile_index(category, x, inclusive = false) ⇒ Object
Returns the upper bound value that would include x% of the hits.
171 172 173 174 175 176 177 178 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 171 def percentile_index(category, x, inclusive = false) total_encountered = 0 @categories[category][:buckets].each_with_index do |count, index| total_encountered += count percentage = ((total_encountered.to_f / hits(category).to_f) * 100).floor return index if (inclusive && percentage >= x) || (!inclusive && percentage > x) end end |
#percentile_indices(category, start, finish) ⇒ Object
180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 180 def percentile_indices(category, start, finish) result = [nil, nil] total_encountered = 0 @categories[category][:buckets].each_with_index do |count, index| total_encountered += count percentage = ((total_encountered.to_f / hits(category).to_f) * 100).floor if !result[0] && percentage > start result[0] = index elsif !result[1] && percentage >= finish result[1] = index return result end end end |
#percentile_interval(category, x) ⇒ Object
Returns a percentile interval, i.e. the lower bound and the upper bound of the values that represent the x%-interval for the bucketized dataset.
A 90% interval means that 5% of the values would have been lower than the lower bound and 5% would have been higher than the upper bound, leaving 90% of the values within the bounds. You can also provide a Range to specify the lower bound and upper bound percentages (e.g. 5..95).
209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 209 def percentile_interval(category, x) case x when Range lower, upper = percentile_indices(category, x.begin, x.end) Range.new(bucket_lower_bound(lower), bucket_upper_bound(upper)) when Numeric percentile_interval(category, Range.new((100 - x) / 2, (100 - (100 - x) / 2))) else fail 'What does it mean?' end end |
#prepare ⇒ Object
Sets up the numeric value tracker. It will check whether the value and category options are set that are used to extract and categorize the values during parsing. Two lambda procedures are created for these tasks
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 8 def prepare fail "No value field set up for numeric tracker #{inspect}" unless [:value] fail "No categorizer set up for numeric tracker #{inspect}" unless [:category] unless [:multiple] @categorizer = create_lambda([:category]) @valueizer = create_lambda([:value]) end @number_of_buckets = [:number_of_buckets] || 1000 @min_bucket_value = [:min_bucket_value] ? [:min_bucket_value].to_f : 0.000001 @max_bucket_value = [:max_bucket_value] ? [:max_bucket_value].to_f : 1_000_000_000 # precalculate the bucket size @bucket_size = (Math.log(@max_bucket_value) - Math.log(@min_bucket_value)) / @number_of_buckets.to_f @categories = {} end |
#report(output) ⇒ Object
Generate a request report to the given output object By default colulative and average duration are generated. Any options for the report should have been set during initialize. output The output object
91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 91 def report(output) sortings = output.[:sort] || [:sum, :mean] sortings.each do |sorting| report_table(output, sorting, title: "#{title} - by #{sorting}") end if [:total] output.puts output.puts "#{output.colorize(title, :white, :bold)} - total: " + output.colorize(display_value(sum_overall), :brown, :bold) end end |
#report_table(output, sort, options = {}, &_block) ⇒ Object
Block function to build a result table using a provided sorting function. output The output object. amount The number of rows in the report table (default 10).
Options
* </tt>:title</tt> The title of the table
* </tt>:sort</tt> The key to sort on (:hits, :cumulative, :average, :min or :max)
62 63 64 65 66 67 68 69 70 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 62 def report_table(output, sort, = {}, &_block) output.puts top_categories = output.slice_results(sorted_by(sort)) output.with_style(top_line: true) do output.table(*statistics_header(title: [:title], highlight: sort)) do |rows| top_categories.each { |(category, _)| rows << statistics_row(category) } end end end |
#sorted_by(by = nil) ⇒ Object
Return categories sorted by a given key. by The key to sort on. This parameter can be omitted if a sorting block is provided instead
302 303 304 305 306 307 308 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 302 def sorted_by(by = nil) if block_given? categories.sort { |a, b| yield(b[1]) <=> yield(a[1]) } else categories.sort { |a, b| send(by, b[0]) <=> send(by, a[0]) } end end |
#statistics_header(options) ⇒ Object
Returns the column header for a statistics table to report on the statistics result
311 312 313 314 315 316 317 318 319 320 321 322 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 311 def statistics_header() [ { title: [:title], width: :rest }, { title: 'Hits', align: :right, highlight: ([:highlight] == :hits), min_width: 4 }, { title: 'Sum', align: :right, highlight: ([:highlight] == :sum), min_width: 6 }, { title: 'Mean', align: :right, highlight: ([:highlight] == :mean), min_width: 6 }, { title: 'StdDev', align: :right, highlight: ([:highlight] == :stddev), min_width: 6 }, { title: 'Min', align: :right, highlight: ([:highlight] == :min), min_width: 6 }, { title: 'Max', align: :right, highlight: ([:highlight] == :max), min_width: 6 }, { title: '95 %tile', align: :right, highlight: ([:highlight] == :percentile_interval), min_width: 11 } ] end |
#statistics_row(cat) ⇒ Object
Returns a row of statistics information for a report table, given a category
325 326 327 328 329 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 325 def statistics_row(cat) [cat, hits(cat), display_value(sum(cat)), display_value(mean(cat)), display_value(stddev(cat)), display_value(min(cat)), display_value(max(cat)), display_value(percentile_interval(cat, 95).begin) + '-' + display_value(percentile_interval(cat, 95).end)] end |
#stddev(cat) ⇒ Object
Get the standard deviation of the duration of a specific category. cat The category
274 275 276 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 274 def stddev(cat) Math.sqrt(variance(cat)) end |
#sum(cat) ⇒ Object
Get the total duration of a specific category. cat The category
250 251 252 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 250 def sum(cat) @categories[cat][:sum] end |
#sum_overall ⇒ Object
Get the cumlative duration of a all categories.
291 292 293 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 291 def sum_overall @categories.reduce(0.0) { |sum, (_, cat)| sum + cat[:sum] } end |
#title ⇒ Object
Returns the title of this tracker for reports
104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 104 def title @title ||= begin if [:title] [:title] else title_builder = '' title_builder << "#{[:value]} " if [:value].is_a?(Symbol) title_builder << ([:category].is_a?(Symbol) ? "per #{[:category]}" : 'per request') title_builder end end end |
#to_yaml_object ⇒ Object
Returns all the categories and the tracked duration as a hash than can be exported to YAML
118 119 120 121 122 123 124 125 126 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 118 def to_yaml_object return nil if @categories.empty? @categories.each do |cat, info| info[:stddev] = stddev(cat) info[:median] = median(cat) if info[:buckets] info[:interval_95_percent] = percentile_interval(cat, 95) if info[:buckets] end @categories end |
#update(request) ⇒ Object
Get the value information from the request and store it in the respective categories.
If a request can contain multiple usable values for this tracker, the :multiple option should be set to true. In this case, all the values and respective categories will be read from the request using the #every method from the fields given in the :value and :category option.
If the request contains only one suitable value and the :multiple is not set, it will read the single value and category from the fields provided in the :value and :category option, or calculate it with any lambda procedure that is assigned to these options. The request will be passed to procedure as input for the calculation.
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 40 def update(request) if [:multiple] found_categories = request.every([:category]) found_values = request.every([:value]) fail 'Capture mismatch for multiple values in a request' unless found_categories.length == found_values.length found_categories.each_with_index do |cat, index| update_statistics(cat, found_values[index]) if cat && found_values[index].is_a?(Numeric) end else category = @categorizer.call(request) value = @valueizer.call(request) update_statistics(category, value) if (value.is_a?(Numeric) || value.is_a?(Array)) && category end end |
#update_statistics(category, number) ⇒ Object
Update the running calculation of statistics with the newly found numeric value.
category-
The category for which to update the running statistics calculations
number-
The numeric value to update the calculations with.
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 224 def update_statistics(category, number) return number.map { |n| update_statistics(category, n) } if number.is_a?(Array) @categories[category] ||= { hits: 0, sum: 0, mean: 0.0, sum_of_squares: 0.0, min: number, max: number, buckets: Array.new(@number_of_buckets, 0) } delta = number - @categories[category][:mean] @categories[category][:hits] += 1 @categories[category][:mean] += (delta / @categories[category][:hits]) @categories[category][:sum_of_squares] += delta * (number - @categories[category][:mean]) @categories[category][:sum] += number @categories[category][:min] = number if number < @categories[category][:min] @categories[category][:max] = number if number > @categories[category][:max] bucketize(category, number) end |
#variance(cat) ⇒ Object
Get the variance of the duration of a specific category. cat The category
280 281 282 283 |
# File 'lib/request_log_analyzer/tracker/numeric_value.rb', line 280 def variance(cat) return 0.0 if @categories[cat][:hits] <= 1 (@categories[cat][:sum_of_squares] / (@categories[cat][:hits] - 1)) end |