Class: Fluent::Plugin::Logcheck::RuleLoader

Inherits:
Object
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/fluent/plugin/logcheck/rule_loader.rb

Overview

RuleLoader handles loading and parsing logcheck rule files

Defined Under Namespace

Classes: FileNotFoundError, ParseError

Instance Method Summary collapse

Constructor Details

#initialize(logger: nil)

Parameters:

  • logger (T.untyped) (defaults to: nil)


24
25
26
# File 'lib/fluent/plugin/logcheck/rule_loader.rb', line 24

def initialize(logger: nil)
  @logger = T.let(logger, T.untyped)
end

Instance Method Details

#load_directory(dir_path, rule_type, recursive: true, max_rules: nil) ⇒ Array<RuleSet>

Load rules from a directory

Parameters:

  • dir_path (String)

    Path to the directory

  • rule_type (Symbol, nil)

    Type of rules, or nil for auto-detection

  • recursive (Boolean) (defaults to: true)

    Whether to scan recursively

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

    Maximum number of rules per file

Returns:

  • (Array<RuleSet>)

    Array of loaded rule sets

Raises:



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/fluent/plugin/logcheck/rule_loader.rb', line 89

def load_directory(dir_path, rule_type, recursive: true, max_rules: nil)
  raise FileNotFoundError, "Directory not found: #{dir_path}" unless Dir.exist?(dir_path)

  log_info "Loading rules from directory: #{dir_path} (recursive: #{recursive})"

  rule_sets = T.let([], T::Array[RuleSet])
  pattern = recursive ? File.join(dir_path, '**', '*') : File.join(dir_path, '*')

  Dir.glob(pattern).each do |file_path|
    next unless File.file?(file_path)
    next if should_skip_file?(file_path)

    # Determine rule type
    detected_type = rule_type || detect_rule_type(file_path)
    next unless detected_type

    begin
      rule_set = load_file(file_path, detected_type, max_rules: max_rules)
      rule_sets << rule_set unless rule_set.empty?
    rescue FileNotFoundError, ParseError => e
      log_error "Error loading file #{file_path}: #{e.message}"
      # Continue with other files
    end
  end

  log_info "Loaded #{rule_sets.size} rule sets from directory #{dir_path}"
  rule_sets
end

#load_file(file_path, rule_type, max_rules: nil) ⇒ RuleSet

Load rules from a single file

Parameters:

  • file_path (String)

    Path to the rule file

  • rule_type (Symbol, nil)

    Type of rules (:ignore, :cracking, :violations) or nil for auto-detection

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

    Maximum number of rules to load from file (nil for no limit)

Returns:

  • (RuleSet)

    Loaded rule set containing parsed rules

Raises:



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
74
75
76
77
# File 'lib/fluent/plugin/logcheck/rule_loader.rb', line 36

def load_file(file_path, rule_type, max_rules: nil)
  raise FileNotFoundError, "File not found: #{file_path}" unless File.exist?(file_path)

  # Auto-detect rule type if not provided
  rule_type = detect_rule_type(file_path) if rule_type.nil?
  raise ParseError, "Could not detect rule type for file: #{file_path}" if rule_type.nil?

  log_info "Loading rules from file: #{file_path} (type: #{rule_type})"

  rules = T.let([], T::Array[Rule])
  line_number = 0

  File.foreach(file_path, encoding: 'UTF-8') do |line|
    line_number += 1

    # Skip if we've reached the maximum rules limit
    break if max_rules && rules.size >= max_rules

    # Clean and validate line
    cleaned_line = clean_line(line)
    next if cleaned_line.empty?

    # Try to create a rule from the line
    begin
      rule = Rule.new(cleaned_line, rule_type, file_path, line_number)
      # Test pattern compilation immediately to catch invalid regex
      rule.pattern
      rules << rule
    rescue PatternCompileError => e
      log_warning "Invalid regex pattern at #{file_path}:#{line_number}: #{e.message}"
      # Continue processing other lines
    end
  end

  log_info "Loaded #{rules.size} rules from #{file_path}"
  rule_set = RuleSet.new(rule_type, file_path)
  rules.each { |rule| rule_set.add_rule(rule) }
  rule_set
rescue Encoding::InvalidByteSequenceError, Encoding::UndefinedConversionError => e
  log_error "Encoding error reading file #{file_path}: #{e.message}"
  RuleSet.new(T.must(rule_type), file_path)
end