Class: Fractor::ErrorStatistics

Inherits:
Object
  • Object
show all
Defined in:
lib/fractor/error_statistics.rb

Overview

Error statistics for a specific category or job. Tracks error counts, severity distribution, and trends over time.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(category) ⇒ ErrorStatistics

Returns a new instance of ErrorStatistics.



9
10
11
12
13
14
15
16
17
18
# File 'lib/fractor/error_statistics.rb', line 9

def initialize(category)
  @category = category
  @total_count = 0
  @by_severity = Hash.new(0)
  @by_code = Hash.new(0)
  @recent_errors = []
  @first_seen = nil
  @last_seen = nil
  @mutex = Mutex.new
end

Instance Attribute Details

#by_codeObject (readonly)

Returns the value of attribute by_code.



7
8
9
# File 'lib/fractor/error_statistics.rb', line 7

def by_code
  @by_code
end

#by_severityObject (readonly)

Returns the value of attribute by_severity.



7
8
9
# File 'lib/fractor/error_statistics.rb', line 7

def by_severity
  @by_severity
end

#categoryObject (readonly)

Returns the value of attribute category.



7
8
9
# File 'lib/fractor/error_statistics.rb', line 7

def category
  @category
end

#recent_errorsObject (readonly)

Returns the value of attribute recent_errors.



7
8
9
# File 'lib/fractor/error_statistics.rb', line 7

def recent_errors
  @recent_errors
end

#total_countObject (readonly)

Returns the value of attribute total_count.



7
8
9
# File 'lib/fractor/error_statistics.rb', line 7

def total_count
  @total_count
end

Instance Method Details

#error_rateFloat

Get error rate (errors per second)

Returns:

  • (Float)

    Errors per second



60
61
62
63
64
65
66
67
# File 'lib/fractor/error_statistics.rb', line 60

def error_rate
  return 0.0 unless @first_seen && @last_seen

  duration = @last_seen - @first_seen
  return 0.0 if duration <= 0

  @total_count / duration
end

#highest_severityString?

Get most severe error level

Returns:

  • (String, nil)

    Highest severity level



81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/fractor/error_statistics.rb', line 81

def highest_severity
  return nil if @by_severity.empty?

  severities = [
    WorkResult::SEVERITY_CRITICAL,
    WorkResult::SEVERITY_ERROR,
    WorkResult::SEVERITY_WARNING,
    WorkResult::SEVERITY_INFO,
  ]

  severities.find { |severity| @by_severity[severity].positive? }
end

#increasing?Boolean

Check if error rate is increasing

Returns:

  • (Boolean)

    True if errors are trending upward



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/fractor/error_statistics.rb', line 97

def increasing?
  return false if @recent_errors.size < 10

  recent_10 = @recent_errors.last(10)

  # Check if errors are happening in a short time span (rapid burst)
  first_timestamp = recent_10.first[:timestamp]
  last_timestamp = recent_10.last[:timestamp]
  total_timespan = last_timestamp - first_timestamp

  # If all errors happened in a very short time (burst), consider it increasing
  return true if total_timespan < 1.0 # Less than 1 second for 10 errors

  # Otherwise, check if the rate is increasing by comparing first half vs second half
  first_5 = recent_10.first(5)
  last_5 = recent_10.last(5)

  first_5_timespan = first_5.last[:timestamp] - first_5.first[:timestamp]
  last_5_timespan = last_5.last[:timestamp] - last_5.first[:timestamp]

  # Avoid division by zero - use small epsilon if timespan is very small
  first_5_timespan = 0.001 if first_5_timespan <= 0
  last_5_timespan = 0.001 if last_5_timespan <= 0

  # Calculate error rate (errors per second) for each group
  first_5_rate = 5.0 / first_5_timespan
  last_5_rate = 5.0 / last_5_timespan

  # Consider increasing if the rate is 50% higher
  last_5_rate > first_5_rate * 1.5
end

#most_common_codeString?

Get most common error code

Returns:

  • (String, nil)

    Most common error code



72
73
74
75
76
# File 'lib/fractor/error_statistics.rb', line 72

def most_common_code
  return nil if @by_code.empty?

  @by_code.max_by { |_code, count| count }&.first
end

#record(work_result) ⇒ void

This method returns an undefined value.

Record an error from a work result

Parameters:

  • work_result (WorkResult)

    The failed work result



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/fractor/error_statistics.rb', line 24

def record(work_result)
  @mutex.synchronize do
    @total_count += 1
    @by_severity[work_result.error_severity] += 1
    @by_code[work_result.error_code] += 1 if work_result.error_code

    # Handle both String and Exception error types
    error_obj = work_result.error
    error_message = if error_obj.is_a?(Exception)
                      error_obj.message
                    elsif error_obj.is_a?(String)
                      error_obj
                    else
                      error_obj&.to_s
                    end

    error_entry = {
      timestamp: Time.now,
      error_class: error_obj.is_a?(Exception) ? error_obj.class.name : nil,
      error_message: error_message,
      error_code: work_result.error_code,
      error_severity: work_result.error_severity,
      error_context: work_result.error_context,
    }

    @recent_errors << error_entry
    @recent_errors.shift if @recent_errors.size > 100

    @first_seen ||= Time.now
    @last_seen = Time.now
  end
end

#to_hHash

Get summary hash

Returns:

  • (Hash)

    Summary of statistics



132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/fractor/error_statistics.rb', line 132

def to_h
  {
    category: @category,
    total_count: @total_count,
    error_rate: error_rate.round(2),
    by_severity: @by_severity,
    by_code: @by_code,
    most_common_code: most_common_code,
    highest_severity: highest_severity,
    first_seen: @first_seen,
    last_seen: @last_seen,
    trending: increasing? ? "increasing" : "stable",
  }
end