Class: Lumberjack::LogEntryMatcher::Score

Inherits:
Object
  • Object
show all
Defined in:
lib/lumberjack/log_entry_matcher/score.rb

Overview

Class responsible for scoring and matching log entries against filters. This class provides fuzzy matching capabilities to find the best matching log entry when exact matches are not available.

Constant Summary collapse

MIN_SCORE_THRESHOLD =

Minimum score threshold for considering a match (30% match)

0.3

Class Method Summary collapse

Class Method Details

.calculate_attributes_score(entry_attributes, attributes_filter) ⇒ Float

Calculate score for attribute matching. Compares entry attributes against filter attributes and returns a score based on how many attributes match.

Parameters:

  • entry_attributes (Hash)

    The attributes from the log entry.

  • attributes_filter (Hash)

    The attributes filter to match against.

Returns:

  • (Float)

    A score between 0.0 and 1.0 based on attribute matches.



131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/lumberjack/log_entry_matcher/score.rb', line 131

def calculate_attributes_score(entry_attributes, attributes_filter)
  return 0.0 unless entry_attributes && attributes_filter.is_a?(Hash)

  attributes_filter = deep_stringify_keys(Lumberjack::Utils.expand_attributes(attributes_filter))
  attributes = deep_stringify_keys(Lumberjack::Utils.expand_attributes(entry_attributes))

  total_attribute_filters = count_attribute_filters(attributes_filter)
  return 0.0 if total_attribute_filters == 0

  matched_attributes = count_matched_attributes(attributes, attributes_filter)
  matched_attributes.to_f / total_attribute_filters
end

.calculate_field_score(value, filter) ⇒ Float

Calculate score for any field value against a filter. Returns a score between 0.0 and 1.0 based on how well the value matches the filter.

Parameters:

  • value (Object)

    The value to match against the filter.

  • filter (String, Regexp, Object)

    The filter to match the value against.

Returns:

  • (Float)

    A score between 0.0 and 1.0 indicating match quality.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/lumberjack/log_entry_matcher/score.rb', line 81

def calculate_field_score(value, filter)
  return 0.0 unless value && filter

  case filter
  when String
    value_str = value.to_s
    if value_str == filter
      1.0
    elsif value_str.include?(filter)
      0.7
    else
      # Use string similarity for partial matching
      similarity = string_similarity(value_str, filter)
      (similarity > 0.5) ? similarity * 0.6 : 0.0
    end
  when Regexp
    filter.match?(value.to_s) ? 1.0 : 0.0
  else
    # For other matchers (like RSpec matchers), try to use === operator
    begin
      (filter === value) ? 1.0 : 0.0
    rescue
      0.0
    end
  end
end

.calculate_match_score(entry, message: nil, severity: nil, attributes: nil, progname: nil) ⇒ Float

Calculate the overall match score for an entry against all provided filters. Returns a score between 0.0 and 1.0, where 1.0 represents a perfect match.

Parameters:

  • entry (Lumberjack::LogEntry)

    The log entry to score.

  • message (String, Regexp, nil) (defaults to: nil)

    The message filter to match against.

  • severity (Integer, nil) (defaults to: nil)

    The severity level to match against.

  • attributes (Hash, nil) (defaults to: nil)

    The attributes hash to match against.

  • progname (String, nil) (defaults to: nil)

    The program name to match against.

Returns:

  • (Float)

    A score between 0.0 and 1.0 indicating match quality.



20
21
22
23
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/lumberjack/log_entry_matcher/score.rb', line 20

def calculate_match_score(entry, message: nil, severity: nil, attributes: nil, progname: nil)
  scores = []
  weights = []

  # Check message match
  if message
    message_score = calculate_field_score(entry.message, message)
    scores << message_score
    weights << 0.5  # Weight message matching highly
  end

  # Check severity match
  if severity
    severity_score = if entry.severity == severity
      1.0  # Exact severity match
    else
      severity_proximity_score(entry.severity, severity)  # Partial severity match
    end
    scores << severity_score
    weights << 0.2
  end

  # Check progname match
  if progname
    progname_score = calculate_field_score(entry.progname, progname)
    scores << progname_score
    weights << 0.2
  end

  # Check attributes match
  if attributes.is_a?(Hash) && !attributes.empty?
    attributes_score = calculate_attributes_score(entry.attributes, attributes)
    scores << attributes_score
    weights << 0.3
  end

  # Return 0 if no criteria were provided
  return 0.0 if scores.empty?

  # Calculate weighted average, but apply a penalty if any score is 0
  # This ensures that completely failed criteria significantly impact the result
  total_weighted_score = scores.zip(weights).map { |score, weight| score * weight }.sum
  total_weight = weights.sum
  base_score = total_weighted_score / total_weight

  # Apply penalty for zero scores: reduce the score based on how many criteria completely failed
  zero_scores = scores.count(0.0)
  if zero_scores > 0
    penalty_factor = 1.0 - (zero_scores.to_f / scores.length * 0.5)  # Up to 50% penalty
    base_score *= penalty_factor
  end

  base_score
end

.severity_proximity_score(entry_severity, filter_severity) ⇒ Float

Calculate proximity score based on log severity distance. Provides partial scoring for severities that are close to the target.

Parameters:

  • entry_severity (Integer)

    The severity level of the log entry.

  • filter_severity (Integer)

    The target severity level to match.

Returns:

  • (Float)

    A score between 0.0 and 1.0 based on severity proximity.



114
115
116
117
118
119
120
121
122
# File 'lib/lumberjack/log_entry_matcher/score.rb', line 114

def severity_proximity_score(entry_severity, filter_severity)
  severity_diff = (entry_severity - filter_severity).abs
  case severity_diff
  when 0 then 1.0
  when 1 then 0.7
  when 2 then 0.4
  else 0.0
  end
end