Class: Philiprehberger::LogFilter::Filter
- Inherits:
-
Object
- Object
- Philiprehberger::LogFilter::Filter
- 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
Instance Attribute Summary collapse
-
#rules ⇒ Array<Hash>
readonly
The ordered list of rules.
Instance Method Summary collapse
-
#apply(message) ⇒ String?
Run all rules against
messagein order. -
#chain(other) ⇒ Filter
Compose this filter with
otherinto a new filter. -
#drop(pattern) ⇒ self
Add a pattern-based drop rule.
-
#drop_field(key) ⇒ self
Add a rule to remove a field from JSON log messages.
-
#drop_if {|message| ... } ⇒ self
Add a block-based drop rule.
-
#initialize ⇒ Filter
constructor
A new instance of Filter.
-
#mask_field(key, with: '***') ⇒ self
Add a rule to mask a field value in JSON log messages.
-
#replace(pattern, replacement) ⇒ self
Add a replacement rule.
-
#reset_stats! ⇒ void
Reset all statistics counters to zero.
-
#sample(pattern, rate:) ⇒ self
Add a sampling rule.
-
#stats ⇒ Hash
Return current filter statistics.
-
#tap_each {|message| ... } ⇒ self
Add a side-effecting inspection rule.
-
#truncate(max_length, suffix: '…') ⇒ self
Add a rule that caps outgoing messages at
max_lengthcharacters, appendingsuffixwhen truncation occurred.
Constructor Details
#initialize ⇒ Filter
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
#rules ⇒ Array<Hash> (readonly)
Returns 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.
142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/philiprehberger/log_filter/filter.rb', line 142 def apply() result = .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.
172 173 174 175 176 |
# File 'lib/philiprehberger/log_filter/filter.rb', line 172 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.
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.
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.
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.
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.
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.
132 133 134 135 136 |
# File 'lib/philiprehberger/log_filter/filter.rb', line 132 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.
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 |
#stats ⇒ Hash
Return current filter statistics.
125 126 127 |
# File 'lib/philiprehberger/log_filter/filter.rb', line 125 def stats @mutex.synchronize { @stats.dup } end |
#tap_each {|message| ... } ⇒ self
Add a side-effecting inspection rule. The block is called with every message that reaches this stage of the pipeline (after any previous transforms applied). The message is then passed through unchanged —the block’s return value is ignored. Exceptions raised inside the block are not swallowed.
Useful for counting, metrics emission, or attaching to a debugger without altering the filter output.
101 102 103 104 |
# File 'lib/philiprehberger/log_filter/filter.rb', line 101 def tap_each(&block) @rules << { type: :tap, block: block } self end |
#truncate(max_length, suffix: '…') ⇒ self
Add a rule that caps outgoing messages at max_length characters, appending suffix when truncation occurred. Messages shorter than or equal to max_length pass through unchanged. Never drops a message, only transforms it.
115 116 117 118 119 120 |
# File 'lib/philiprehberger/log_filter/filter.rb', line 115 def truncate(max_length, suffix: '…') raise ArgumentError, 'max_length must be a positive Integer' unless max_length.is_a?(Integer) && max_length.positive? @rules << { type: :truncate, max_length: max_length, suffix: suffix } self end |