Class: Lumberjack::LogEntryMatcher

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

Overview

A flexible matching utility for testing and filtering log entries based on multiple criteria. This class provides pattern-based matching against log entry components including message content, severity levels, program names, and custom attributes with support for nested attribute structures.

The matcher uses Ruby's case equality operator (===) for flexible matching, supporting exact values, regular expressions, ranges, classes, and other pattern matching constructs. It's primarily designed for use with the Test device in testing scenarios but can be used anywhere log entry filtering is needed.

A matcher can optionally be constructed with an entry formatter. Filter values are always compared raw first. If a raw comparison fails, the filter value is run through the formatter and compared again. Since log entries are formatted before they are written to a device, this allows expectations to be written with unformatted values (an Exception, for example) and still match the formatted values captured on the entry.

See Also:

Defined Under Namespace

Classes: IndifferentHash, Score

Instance Method Summary collapse

Constructor Details

#initialize(message: nil, severity: nil, progname: nil, attributes: nil, formatter: nil) ⇒ LogEntryMatcher

Create a new log entry matcher with optional filtering criteria. All parameters are optional and nil values indicate no filtering for that component. The matcher uses case equality (===) for flexible pattern matching against each specified criterion.

Parameters:

  • message (Object, nil) (defaults to: nil)

    Pattern to match against log entry messages. Supports strings, regular expressions, or any object responding to ===

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

    Severity level to match. Accepts numeric levels or symbolic names (:debug, :info, etc.)

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

    Pattern to match against program names. Supports strings, regular expressions, or any object responding to ===

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

    Hash of attribute patterns to match against log entry attributes. Supports nested attribute matching and dot notation. Any other object is matched against the entire attributes hash with === so matchers like RSpec's hash_including can be used.

  • formatter (Lumberjack::EntryFormatter, Lumberjack::Logger, nil) (defaults to: nil)

    Optional formatter used to format filter values when a raw comparison fails. A Logger can be passed to use its entry formatter. The message filter is formatted with the message formatter; when the result is a MessageAttributes, only the message part is used and the derived attributes are ignored. Attribute filter values are formatted with the attribute formatter using their dot notation names. Pattern objects (classes, regular expressions, ranges, procs, hashes, and test framework matchers) are never formatted.

Raises:

  • (ArgumentError)

    If the formatter is not an EntryFormatter or a Logger.



51
52
53
54
55
56
57
58
59
60
61
# File 'lib/lumberjack/log_entry_matcher.rb', line 51

def initialize(message: nil, severity: nil, progname: nil, attributes: nil, formatter: nil)
  message = message.strip if message.is_a?(String)
  @message_filter = message
  @severity_filter = Severity.coerce(severity) if severity
  @progname_filter = progname
  if attributes
    @attributes_filter = attributes.is_a?(Hash) ? Utils.expand_attributes(attributes) : attributes
  end
  @formatter = resolve_formatter(formatter)
  @formatted_attribute_filters = {}
end

Instance Method Details

#closest(entries) ⇒ Lumberjack::LogEntry?

Find the closest matching log entry from a list of candidates. This method scores each entry based on how well it matches the specified criteria and returns the entry with the highest score, provided it meets a minimum threshold. If no entries meet the threshold, nil is returned.

Parameters:

Returns:



129
130
131
132
133
# File 'lib/lumberjack/log_entry_matcher.rb', line 129

def closest(entries)
  scored_entries = entries.map { |entry| [entry, entry_score(entry)] }
  best_score = scored_entries.max_by { |_, score| score }
  (best_score&.last.to_f >= Score::MIN_SCORE_THRESHOLD) ? best_score.first : nil
end

#diff(entry) ⇒ Hash

Compare a log entry against the matcher criteria and return only the fields that do not match. An empty hash means the entry matches, so diff(entry).empty? is always equal to match?(entry).

The returned hash uses string keys for the fields ("message", "severity", "progname", and "attributes"). Each mismatched field maps to a hash with :expected and :actual (the entry value). Severity values are converted to labels on both sides for readability. When a formatter is set and the filter value was formatted, :expected shows the formatted filter value so both sides of the mismatch are in the same form.

Attribute mismatches are reported per attribute using dot notation keys. A missing attribute is reported with actual: nil. An attribute that was expected to be absent (a nil or empty filter value) is reported with the raw filter as :expected and the entry value as :actual. When the attributes filter is not a hash (a matcher object applied to the whole attributes hash), a failure is reported as a single hash with :expected and :actual keys instead of per attribute detail.

Parameters:

Returns:

  • (Hash)

    A hash of the fields that do not match; empty if the entry matches



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/lumberjack/log_entry_matcher.rb', line 94

def diff(entry)
  result = {}

  unless match_message?(entry.message)
    result["message"] = {expected: expected_message, actual: entry.message}
  end

  unless match_filter?(entry.severity, @severity_filter)
    result["severity"] = {expected: Severity.level_to_label(@severity_filter), actual: entry.severity_label}
  end

  unless match_filter?(entry.progname, @progname_filter)
    result["progname"] = {expected: @progname_filter, actual: entry.progname}
  end

  if @attributes_filter
    attributes = IndifferentHash.wrap(Utils.expand_attributes(entry.attributes))
    if @attributes_filter.is_a?(Hash)
      mismatches = attribute_mismatches(attributes, @attributes_filter)
      result["attributes"] = mismatches unless mismatches.empty?
    elsif !match_filter?(attributes, @attributes_filter)
      result["attributes"] = {expected: @attributes_filter, actual: attributes}
    end
  end

  result
end

#match?(entry) ⇒ Boolean

Test whether a log entry matches all specified criteria. The entry must satisfy all non-nil filter conditions to be considered a match. Uses case equality (===) for flexible pattern matching.

Parameters:

Returns:

  • (Boolean)

    True if the entry matches all specified criteria, false otherwise



69
70
71
# File 'lib/lumberjack/log_entry_matcher.rb', line 69

def match?(entry)
  diff(entry).empty?
end