class RequestLogAnalyzer::Tracker::NumericValue

Attributes

categories[R]

Public Instance Methods

bucket_average_value(index) click to toggle source

Returns the average of the lower and upper bound of the bucket.

    # File lib/request_log_analyzer/tracker/numeric_value.rb
147 def bucket_average_value(index)
148   (bucket_lower_bound(index) + bucket_upper_bound(index)) / 2
149 end
bucket_index(value) click to toggle source

Returns the bucket index for a value

    # File lib/request_log_analyzer/tracker/numeric_value.rb
129 def bucket_index(value)
130   return 0 if value < @min_bucket_value
131   return @number_of_buckets - 1 if value >= @max_bucket_value
132 
133   ((Math.log(value) - Math.log(@min_bucket_value)) / @bucket_size).floor
134 end
bucket_interval(index) click to toggle source

Returns the range of values for a bucket.

    # File lib/request_log_analyzer/tracker/numeric_value.rb
161 def bucket_interval(index)
162   Range.new(bucket_lower_bound(index), bucket_upper_bound(index), true)
163 end
bucket_lower_bound(index) click to toggle source

Returns the lower value of a bucket given its index

    # File lib/request_log_analyzer/tracker/numeric_value.rb
137 def bucket_lower_bound(index)
138   Math.exp((index * @bucket_size) + Math.log(@min_bucket_value))
139 end
bucket_upper_bound(index) click to toggle source

Returns the upper value of a bucket given its index

    # File lib/request_log_analyzer/tracker/numeric_value.rb
142 def bucket_upper_bound(index)
143   bucket_lower_bound(index + 1)
144 end
bucket_value(index, type = nil) click to toggle source

Returns a single value representing a bucket.

    # File lib/request_log_analyzer/tracker/numeric_value.rb
152 def bucket_value(index, type = nil)
153   case type
154   when :begin, :start, :lower, :lower_bound then bucket_lower_bound(index)
155   when :end, :finish, :upper, :upper_bound then  bucket_upper_bound(index)
156   else bucket_average_value(index)
157   end
158 end
bucketize(category, value) click to toggle source

Records a hit on a bucket that includes the given value.

    # File lib/request_log_analyzer/tracker/numeric_value.rb
166 def bucketize(category, value)
167   @categories[category][:buckets][bucket_index(value)] += 1
168 end
display_value(value) click to toggle source

Display a value

   # File lib/request_log_analyzer/tracker/numeric_value.rb
73 def display_value(value)
74   return '- ' if value.nil?
75   return '0 ' if value.zero?
76 
77   case [Math.log10(value.abs).floor, 0].max
78     when  0...4  then '%d ' % value
79     when  4...7  then '%dk' % (value / 1000)
80     when  7...10 then '%dM' % (value / 1_000_000)
81     when 10...13 then '%dG' % (value / 1_000_000_000)
82     when 13...16 then '%dT' % (value / 1_000_000_000_000)
83     else              '%dP' % (value / 1_000_000_000_000_000)
84   end
85 end
hits(cat) click to toggle source

Get the number of hits of a specific category. cat The category

    # File lib/request_log_analyzer/tracker/numeric_value.rb
244 def hits(cat)
245   @categories[cat][:hits]
246 end
hits_overall() click to toggle source

Get the total hits of a all categories.

    # File lib/request_log_analyzer/tracker/numeric_value.rb
296 def hits_overall
297   @categories.reduce(0) { |sum, (_, cat)| sum + cat[:hits] }
298 end
max(cat) click to toggle source

Get the maximum duration of a specific category. cat The category

    # File lib/request_log_analyzer/tracker/numeric_value.rb
262 def max(cat)
263   @categories[cat][:max]
264 end
mean(cat) click to toggle source

Get the average duration of a specific category. cat The category

    # File lib/request_log_analyzer/tracker/numeric_value.rb
268 def mean(cat)
269   @categories[cat][:mean]
270 end
mean_overall() click to toggle source

Get the average duration of a all categories.

    # File lib/request_log_analyzer/tracker/numeric_value.rb
286 def mean_overall
287   sum_overall / hits_overall
288 end
median(category) click to toggle source
    # File lib/request_log_analyzer/tracker/numeric_value.rb
199 def median(category)
200   percentile(category, 50, :average)
201 end
min(cat) click to toggle source

Get the minimal duration of a specific category. cat The category

    # File lib/request_log_analyzer/tracker/numeric_value.rb
256 def min(cat)
257   @categories[cat][:min]
258 end
percentile(category, x, type = nil) click to toggle source
    # File lib/request_log_analyzer/tracker/numeric_value.rb
195 def percentile(category, x, type = nil)
196   bucket_value(percentile_index(category, x, type == :upper), type)
197 end
percentile_index(category, x, inclusive = false) click to toggle source

Returns the upper bound value that would include x% of the hits.

    # File lib/request_log_analyzer/tracker/numeric_value.rb
171 def percentile_index(category, x, inclusive = false)
172   total_encountered = 0
173   @categories[category][:buckets].each_with_index do |count, index|
174     total_encountered += count
175     percentage = ((total_encountered.to_f / hits(category).to_f) * 100).floor
176     return index if (inclusive && percentage >= x) || (!inclusive && percentage > x)
177   end
178 end
percentile_indices(category, start, finish) click to toggle source
    # File lib/request_log_analyzer/tracker/numeric_value.rb
180 def percentile_indices(category, start, finish)
181   result = [nil, nil]
182   total_encountered = 0
183   @categories[category][:buckets].each_with_index do |count, index|
184     total_encountered += count
185     percentage = ((total_encountered.to_f / hits(category).to_f) * 100).floor
186     if !result[0] && percentage > start
187       result[0] = index
188     elsif !result[1] && percentage >= finish
189       result[1] = index
190       return result
191     end
192   end
193 end
percentile_interval(category, x) click to toggle source

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).

    # File lib/request_log_analyzer/tracker/numeric_value.rb
209 def percentile_interval(category, x)
210   case x
211   when Range
212     lower, upper = percentile_indices(category, x.begin, x.end)
213     Range.new(bucket_lower_bound(lower), bucket_upper_bound(upper))
214   when Numeric
215     percentile_interval(category, Range.new((100 - x) / 2, (100 - (100 - x) / 2)))
216   else
217     fail 'What does it mean?'
218   end
219 end
prepare() click to toggle source

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

   # File lib/request_log_analyzer/tracker/numeric_value.rb
 8 def prepare
 9   fail "No value field set up for numeric tracker #{inspect}" unless options[:value]
10   fail "No categorizer set up for numeric tracker #{inspect}" unless options[:category]
11 
12   unless options[:multiple]
13     @categorizer = create_lambda(options[:category])
14     @valueizer   = create_lambda(options[:value])
15   end
16 
17   @number_of_buckets = options[:number_of_buckets] || 1000
18   @min_bucket_value  = options[:min_bucket_value] ? options[:min_bucket_value].to_f : 0.000001
19   @max_bucket_value  = options[:max_bucket_value] ? options[:max_bucket_value].to_f : 1_000_000_000
20 
21   # precalculate the bucket size
22   @bucket_size = (Math.log(@max_bucket_value) - Math.log(@min_bucket_value)) / @number_of_buckets.to_f
23 
24   @categories = {}
25 end
report(output) click to toggle source

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

    # File lib/request_log_analyzer/tracker/numeric_value.rb
 91 def report(output)
 92   sortings = output.options[:sort] || [:sum, :mean]
 93   sortings.each do |sorting|
 94     report_table(output, sorting, title: "#{title} - by #{sorting}")
 95   end
 96 
 97   if options[:total]
 98     output.puts
 99     output.puts "#{output.colorize(title, :white, :bold)} - total: " + output.colorize(display_value(sum_overall), :brown, :bold)
100   end
101 end
report_table(output, sort, options = {}, &_block) click to toggle source

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)
   # File lib/request_log_analyzer/tracker/numeric_value.rb
62 def report_table(output, sort, options = {}, &_block)
63   output.puts
64   top_categories = output.slice_results(sorted_by(sort))
65   output.with_style(top_line: true) do
66     output.table(*statistics_header(title: options[:title], highlight: sort)) do |rows|
67       top_categories.each { |(category, _)| rows << statistics_row(category) }
68     end
69   end
70 end
sorted_by(by = nil) { |b| ... } click to toggle source

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

    # File lib/request_log_analyzer/tracker/numeric_value.rb
302 def sorted_by(by = nil)
303   if block_given?
304     categories.sort { |a, b| yield(b[1]) <=> yield(a[1]) }
305   else
306     categories.sort { |a, b| send(by, b[0]) <=> send(by, a[0]) }
307   end
308 end
statistics_header(options) click to toggle source

Returns the column header for a statistics table to report on the statistics result

    # File lib/request_log_analyzer/tracker/numeric_value.rb
311 def statistics_header(options)
312   [
313     { title: options[:title], width: :rest },
314     { title: 'Hits',   align: :right, highlight: (options[:highlight] == :hits),   min_width: 4 },
315     { title: 'Sum',    align: :right, highlight: (options[:highlight] == :sum),    min_width: 6 },
316     { title: 'Mean',   align: :right, highlight: (options[:highlight] == :mean),   min_width: 6 },
317     { title: 'StdDev', align: :right, highlight: (options[:highlight] == :stddev), min_width: 6 },
318     { title: 'Min',    align: :right, highlight: (options[:highlight] == :min),    min_width: 6 },
319     { title: 'Max',    align: :right, highlight: (options[:highlight] == :max),    min_width: 6 },
320     { title: '95 %tile',    align: :right, highlight: (options[:highlight] == :percentile_interval),  min_width: 11 }
321   ]
322 end
statistics_row(cat) click to toggle source

Returns a row of statistics information for a report table, given a category

    # File lib/request_log_analyzer/tracker/numeric_value.rb
325 def statistics_row(cat)
326   [cat, hits(cat), display_value(sum(cat)), display_value(mean(cat)), display_value(stddev(cat)),
327    display_value(min(cat)), display_value(max(cat)),
328    display_value(percentile_interval(cat, 95).begin) + '-' + display_value(percentile_interval(cat, 95).end)]
329 end
stddev(cat) click to toggle source

Get the standard deviation of the duration of a specific category. cat The category

    # File lib/request_log_analyzer/tracker/numeric_value.rb
274 def stddev(cat)
275   Math.sqrt(variance(cat))
276 end
sum(cat) click to toggle source

Get the total duration of a specific category. cat The category

    # File lib/request_log_analyzer/tracker/numeric_value.rb
250 def sum(cat)
251   @categories[cat][:sum]
252 end
sum_overall() click to toggle source

Get the cumlative duration of a all categories.

    # File lib/request_log_analyzer/tracker/numeric_value.rb
291 def sum_overall
292   @categories.reduce(0.0) { |sum, (_, cat)| sum + cat[:sum] }
293 end
title() click to toggle source

Returns the title of this tracker for reports

    # File lib/request_log_analyzer/tracker/numeric_value.rb
104 def title
105   @title ||= begin
106     if options[:title]
107       options[:title]
108     else
109       title_builder = ''
110       title_builder << "#{options[:value]} " if options[:value].is_a?(Symbol)
111       title_builder << (options[:category].is_a?(Symbol) ? "per #{options[:category]}" : 'per request')
112       title_builder
113     end
114   end
115 end
to_yaml_object() click to toggle source

Returns all the categories and the tracked duration as a hash than can be exported to YAML

    # File lib/request_log_analyzer/tracker/numeric_value.rb
118 def to_yaml_object
119   return nil if @categories.empty?
120   @categories.each do |cat, info|
121     info[:stddev] = stddev(cat)
122     info[:median] = median(cat) if info[:buckets]
123     info[:interval_95_percent] = percentile_interval(cat, 95) if info[:buckets]
124   end
125   @categories
126 end
update(request) click to toggle source

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.

@param [RequestLogAnalyzer::Request] request The request to get the information from.

   # File lib/request_log_analyzer/tracker/numeric_value.rb
40 def update(request)
41   if options[:multiple]
42     found_categories = request.every(options[:category])
43     found_values     = request.every(options[:value])
44     fail 'Capture mismatch for multiple values in a request' unless found_categories.length == found_values.length
45 
46     found_categories.each_with_index do |cat, index|
47       update_statistics(cat, found_values[index]) if cat && found_values[index].is_a?(Numeric)
48     end
49   else
50     category = @categorizer.call(request)
51     value    = @valueizer.call(request)
52     update_statistics(category, value) if (value.is_a?(Numeric) || value.is_a?(Array)) && category
53   end
54 end
update_statistics(category, number) click to toggle source

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.

    # File lib/request_log_analyzer/tracker/numeric_value.rb
224 def update_statistics(category, number)
225   return number.map { |n| update_statistics(category, n) } if number.is_a?(Array)
226 
227   @categories[category] ||= { hits: 0, sum: 0, mean: 0.0, sum_of_squares: 0.0, min: number, max: number,
228                               buckets: Array.new(@number_of_buckets, 0) }
229 
230   delta = number - @categories[category][:mean]
231 
232   @categories[category][:hits]           += 1
233   @categories[category][:mean]           += (delta / @categories[category][:hits])
234   @categories[category][:sum_of_squares] += delta * (number - @categories[category][:mean])
235   @categories[category][:sum]            += number
236   @categories[category][:min]             = number if number < @categories[category][:min]
237   @categories[category][:max]             = number if number > @categories[category][:max]
238 
239   bucketize(category, number)
240 end
variance(cat) click to toggle source

Get the variance of the duration of a specific category. cat The category

    # File lib/request_log_analyzer/tracker/numeric_value.rb
280 def variance(cat)
281   return 0.0 if @categories[cat][:hits] <= 1
282   (@categories[cat][:sum_of_squares] / (@categories[cat][:hits] - 1))
283 end