lib/new_relic/stats.rb in newrelic_rpm-2.13.4 vs lib/new_relic/stats.rb in newrelic_rpm-2.13.5.beta1

- old
+ new

@@ -7,63 +7,101 @@ call_count == 0 end def time_str(value_ms) case - when value_ms >= 10000 - "%.1f s" % (value_ms / 1000.0) - when value_ms >= 5000 - "%.2f s" % (value_ms / 1000.0) + when value_ms >= 10000 + "%.1f s" % (value_ms / 1000.0) + when value_ms >= 5000 + "%.2f s" % (value_ms / 1000.0) else - "%.0f ms" % value_ms + "%.0f ms" % value_ms end end - + + # makes sure we aren't dividing by zero + def checked_calculation(numerator, denominator) + if denominator.nil? || denominator == 0 + 0.0 + else + numerator / denominator + end + end + def average_call_time - return 0 if call_count == 0 - total_call_time / call_count + checked_calculation(total_call_time, call_count) end def average_exclusive_time - return 0 if call_count == 0 - total_exclusive_time / call_count + checked_calculation(total_exclusive_time, call_count) end # merge by adding to average response time # - used to compose multiple metrics e.g. dispatcher time + mongrel queue time def sum_merge! (other_stats) - Array(other_stats).each do |s| - self.total_call_time += s.total_call_time - self.total_exclusive_time += s.total_exclusive_time - self.min_call_time += s.min_call_time - self.max_call_time += s.max_call_time - #self.call_count += s.call_count - do not add call count because we are stacking these times on top of each other - self.sum_of_squares += s.sum_of_squares if s.sum_of_squares - self.begin_time = s.begin_time if s.begin_time.to_f < begin_time.to_f || begin_time.to_f == 0.0 - self.end_time = s.end_time if s.end_time.to_f > end_time.to_f + Array(other_stats).each do |other| + self.sum_attributes(other) end - self end + def sum_attributes(other) + update_totals(other) + stack_min_max_from(other) + update_boundaries(other) + end + + def stack_min_max_from(other) + self.min_call_time += other.min_call_time + self.max_call_time += other.max_call_time + end + + def update_boundaries(other) + self.begin_time = other.begin_time if should_replace_begin_time?(other) + self.end_time = other.end_time if should_replace_end_time?(other) + end + + def should_replace_end_time?(other) + end_time.to_f < other.end_time.to_f + end + + def should_replace_begin_time?(other) + other.begin_time.to_f < begin_time.to_f || begin_time.to_f == 0.0 + end + + def update_totals(other) + self.total_call_time += other.total_call_time + self.total_exclusive_time += other.total_exclusive_time + self.sum_of_squares += other.sum_of_squares + end + + def min_time_less?(other) + (other.min_call_time < min_call_time && other.call_count > 0) || call_count == 0 + end + + def expand_min_max_to(other) + self.min_call_time = other.min_call_time if min_time_less?(other) + self.max_call_time = other.max_call_time if other.max_call_time > max_call_time + end + + def merge_attributes(other) + update_totals(other) + expand_min_max_to(other) + self.call_count += other.call_count + update_boundaries(other) + end + def merge!(other_stats) - Array(other_stats).each do |s| - self.total_call_time += s.total_call_time - self.total_exclusive_time += s.total_exclusive_time - self.min_call_time = s.min_call_time if (s.min_call_time < min_call_time && s.call_count > 0) || call_count == 0 - self.max_call_time = s.max_call_time if s.max_call_time > max_call_time - self.call_count += s.call_count - self.sum_of_squares += s.sum_of_squares if s.sum_of_squares - self.begin_time = s.begin_time if s.begin_time.to_f < begin_time.to_f || begin_time.to_f == 0.0 - self.end_time = s.end_time if s.end_time.to_f > end_time.to_f + Array(other_stats).each do |other| + merge_attributes(other) end self end def merge(other_stats) stats = self.clone - stats.merge! other_stats + stats.merge!(other_stats) end # split into an array of timeslices whose # time boundaries start on (begin_time + (n * duration)) and whose # end time ends on (begin_time * (n + 1) * duration), except for the @@ -116,33 +154,27 @@ self.begin_time = Time.at(0) self.end_time = Time.at(0) end def as_percentage_of(other_stats) - return 0 if other_stats.total_call_time == 0 - return (total_call_time / other_stats.total_call_time) * 100.0 + checked_calculation(total_call_time, other_stats.total_call_time) * 100.0 end # the stat total_call_time is a percent def as_percentage - if call_count.zero? - 0 - else - (total_call_time / call_count) * 100.0 - end + average_call_time * 100.0 end def duration - end_time - begin_time + end_time ? (end_time - begin_time) : 0.0 end + def midpoint + begin_time + (duration/2) + end def calls_per_minute - if duration.zero? - 0 - else - (call_count / duration.to_f) * 60.0 - end + checked_calculation(call_count, duration) * 60 end def total_call_time_per_minute 60.0 * time_percentage end @@ -159,17 +191,15 @@ end # returns the time spent in this component as a percentage of the total # time window. def time_percentage - return 0 if duration == 0 - total_call_time / duration + checked_calculation(total_call_time, duration) end def exclusive_time_percentage - return 0 if duration == 0 - total_exclusive_time / duration + checked_calculation(total_exclusive_time, duration) end alias average_value average_call_time alias average_response_time average_call_time alias requests_per_minute calls_per_minute @@ -179,24 +209,13 @@ end # Summary string to facilitate testing def summary format = "%m/%d/%y %I:%M%p" - "[#{Time.at(begin_time).utc.strftime(format)} UTC, #{'%2.3fs' % duration}; #{'%2i' % call_count} calls #{'%4i' % to_ms(average_call_time)} ms]" + "[#{Time.at(begin_time.to_f).utc.strftime(format)} UTC, #{'%2.3fs' % duration.to_f}; #{'%2i' % call_count.to_i} calls #{'%4i' % average_call_time.to_f}s]" end - # round all of the values to n decimal points - def round! - self.total_call_time = round_to_3(total_call_time) - self.total_exclusive_time = round_to_3(total_exclusive_time) - self.min_call_time = round_to_3(min_call_time) - self.max_call_time = round_to_3(max_call_time) - self.sum_of_squares = round_to_3(sum_of_squares) - self.begin_time = begin_time - self.end_time = end_time - end - # calculate this set of stats to be a percentage fraction # of the provided stats, which has an overlapping time window. # used as a key part of the split algorithm def fraction_of(s) min_end = (end_time < s.end_time ? end_time : s.end_time) @@ -218,30 +237,19 @@ self.sum_of_squares = sum_of_squares * percentage self end - # returns s,t,f def get_apdex [@call_count, @total_call_time.to_i, @total_exclusive_time.to_i] end def apdex_score s, t, f = get_apdex (s.to_f + (t.to_f / 2)) / (s+t+f).to_f end - - private - - def to_ms(number) - (number*1000).round - end - - def round_to_3(val) - (val * 1000).round / 1000.0 - end end class StatsBase include Stats @@ -262,15 +270,15 @@ super end def to_json(*a) {'call_count' => call_count, - 'min_call_time' => min_call_time, - 'max_call_time' => max_call_time, - 'total_call_time' => total_call_time, - 'total_exclusive_time' => total_exclusive_time, - 'sum_of_squares' => sum_of_squares}.to_json(*a) + 'min_call_time' => min_call_time, + 'max_call_time' => max_call_time, + 'total_call_time' => total_call_time, + 'total_exclusive_time' => total_exclusive_time, + 'sum_of_squares' => sum_of_squares}.to_json(*a) end # In this class, we explicitly don't track begin and end time here, to save space during # cross process serialization via xml. Still the accessor methods must be provided for merge to work. @@ -346,23 +354,21 @@ end end class ScopedMethodTraceStats < MethodTraceStats + attr_accessor :unscoped_stats def initialize(unscoped_stats) super() - @unscoped_stats = unscoped_stats + self.unscoped_stats = unscoped_stats end def trace_call(call_time, exclusive_time = call_time) - @unscoped_stats.trace_call call_time, exclusive_time + unscoped_stats.trace_call call_time, exclusive_time super call_time, exclusive_time end def record_multiple_data_points(total_value, count=1) - @unscoped_stats.record_multiple_data_points(total_value, count) + unscoped_stats.record_multiple_data_points(total_value, count) super total_value, count - end - def unscoped_stats - @unscoped_stats end end end