Class: Philiprehberger::LogFilter::Filter

Inherits:
Object
  • Object
show all
Defined in:
lib/philiprehberger/log_filter/filter.rb

Overview

Chain of rules that can drop or transform log messages.

Rules are evaluated in the order they were added. A drop rule short-circuits and returns nil. A replace rule mutates the message string before passing it to the next rule.

Direct Known Subclasses

ChainedFilter

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeFilter

Returns a new instance of Filter.



17
18
19
20
21
# File 'lib/philiprehberger/log_filter/filter.rb', line 17

def initialize
  @rules = []
  @mutex = Mutex.new
  @stats = { dropped: 0, passed: 0, replaced: 0, sampled: 0 }
end

Instance Attribute Details

#rulesArray<Hash> (readonly)

Returns the ordered list of rules.

Returns:

  • (Array<Hash>)

    the ordered list of rules



15
16
17
# File 'lib/philiprehberger/log_filter/filter.rb', line 15

def rules
  @rules
end

Instance Method Details

#apply(message) ⇒ String?

Run all rules against message in order.

Parameters:

  • message (String)

    the log message to filter

Returns:

  • (String, nil)

    the transformed message, or nil if dropped



109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/philiprehberger/log_filter/filter.rb', line 109

def apply(message)
  result = message.dup

  @rules.each do |rule|
    result = apply_rule(rule, result)
    if result.nil?
      increment_stat(:dropped)
      return nil
    end
  end

  increment_stat(:passed)
  result
end

#chain(other) ⇒ Filter

Compose this filter with other into a new filter.

The returned filter’s apply runs self.apply first and passes the result through other.apply. If the first filter drops the message (returns nil), the second filter is not invoked and the chained apply returns nil. Composition is associative, so a.chain(b).chain© behaves transitively.

The chained filter tracks its own stats independently of the two source filters; each source filter continues to track its own counters when invoked.

Parameters:

  • other (Filter)

    the filter to run after this one

Returns:

  • (Filter)

    a new filter composing self and other

Raises:



139
140
141
142
143
# File 'lib/philiprehberger/log_filter/filter.rb', line 139

def chain(other)
  raise ArgumentError, 'other must be a Philiprehberger::LogFilter::Filter' unless other.is_a?(Filter)

  ChainedFilter.new(self, other)
end

#drop(pattern) ⇒ self

Add a pattern-based drop rule. Messages matching pattern are suppressed.

Parameters:

  • pattern (Regexp)

    the pattern to match against

Returns:

  • (self)

    for chaining



27
28
29
30
# File 'lib/philiprehberger/log_filter/filter.rb', line 27

def drop(pattern)
  @rules << { type: :drop_pattern, pattern: pattern }
  self
end

#drop_field(key) ⇒ self

Add a rule to remove a field from JSON log messages. Non-JSON messages pass through unmodified.

Parameters:

  • key (String)

    the JSON field key to remove

Returns:

  • (self)

    for chaining



73
74
75
76
# File 'lib/philiprehberger/log_filter/filter.rb', line 73

def drop_field(key)
  @rules << { type: :drop_field, key: key.to_s }
  self
end

#drop_if {|message| ... } ⇒ self

Add a block-based drop rule. Messages for which the block returns a truthy value are suppressed.

Yields:

  • (message)

    evaluates whether the message should be dropped

Yield Parameters:

  • message (String)

Yield Returns:

  • (Boolean)

Returns:

  • (self)

    for chaining



39
40
41
42
# File 'lib/philiprehberger/log_filter/filter.rb', line 39

def drop_if(&block)
  @rules << { type: :drop_block, block: block }
  self
end

#mask_field(key, with: '***') ⇒ self

Add a rule to mask a field value in JSON log messages. Non-JSON messages pass through unmodified.

Parameters:

  • key (String)

    the JSON field key to mask

  • with (String) (defaults to: '***')

    the mask replacement value

Returns:

  • (self)

    for chaining



84
85
86
87
# File 'lib/philiprehberger/log_filter/filter.rb', line 84

def mask_field(key, with: '***')
  @rules << { type: :mask_field, key: key.to_s, mask: with }
  self
end

#replace(pattern, replacement) ⇒ self

Add a replacement rule. Occurrences of pattern in the message are replaced with replacement.

Parameters:

  • pattern (Regexp)

    the pattern to match

  • replacement (String)

    the replacement string

Returns:

  • (self)

    for chaining



50
51
52
53
# File 'lib/philiprehberger/log_filter/filter.rb', line 50

def replace(pattern, replacement)
  @rules << { type: :replace, pattern: pattern, replacement: replacement }
  self
end

#reset_stats!void

This method returns an undefined value.

Reset all statistics counters to zero.



99
100
101
102
103
# File 'lib/philiprehberger/log_filter/filter.rb', line 99

def reset_stats!
  @mutex.synchronize do
    @stats = { dropped: 0, passed: 0, replaced: 0, sampled: 0 }
  end
end

#sample(pattern, rate:) ⇒ self

Add a sampling rule. Only pass through rate fraction of messages matching pattern. Non-matching messages pass through unaffected.

Parameters:

  • pattern (Regexp)

    the pattern to match

  • rate (Float)

    sampling rate between 0.0 and 1.0

Returns:

  • (self)

    for chaining

Raises:

  • (ArgumentError)


61
62
63
64
65
66
# File 'lib/philiprehberger/log_filter/filter.rb', line 61

def sample(pattern, rate:)
  raise ArgumentError, 'rate must be between 0.0 and 1.0' unless rate.is_a?(Numeric) && rate >= 0.0 && rate <= 1.0

  @rules << { type: :sample, pattern: pattern, rate: rate.to_f }
  self
end

#statsHash

Return current filter statistics.

Returns:

  • (Hash)

    counters for :dropped, :passed, :replaced, :sampled



92
93
94
# File 'lib/philiprehberger/log_filter/filter.rb', line 92

def stats
  @mutex.synchronize { @stats.dup }
end