philiprehberger-log_filter 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +25 -0
- data/lib/philiprehberger/log_filter/filter.rb +58 -0
- data/lib/philiprehberger/log_filter/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d87e6f862d6fe0d39c516f49c18c2aecf3917d28fbfb9a074a0f9a06ce627c33
|
|
4
|
+
data.tar.gz: c3e42d136400a6a0c54f8ff7b342a9340c0b34171e368718236c1043858a32bf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 884ed562f6e2f290dcb92689273bc9785db1a7523b3da0c139e70e10c774e9558fc0840e8d1664ffacd48a2706a2b037cf0c4ac7cb9d71cbfdc76e9c03db8253
|
|
7
|
+
data.tar.gz: da68ea62830a8e198194bd13daaaf9a4604fcec21284597f0b21e2de13bb32d2f1e493b349a2a360e5e07368814c2794d8f24055e025dccaaffa882aaa3d1b65
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.0] - 2026-04-17
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `Filter#chain(other)` composes two filters into a new filter that pipes events through both in order (drops short-circuit)
|
|
14
|
+
|
|
10
15
|
## [0.2.1] - 2026-03-31
|
|
11
16
|
|
|
12
17
|
### Changed
|
data/README.md
CHANGED
|
@@ -114,6 +114,30 @@ filter.apply('{"user":"alice","password":"secret","ssn":"123-45-6789"}')
|
|
|
114
114
|
filter.apply("plain text log line") # => "plain text log line"
|
|
115
115
|
```
|
|
116
116
|
|
|
117
|
+
### Chaining filters
|
|
118
|
+
|
|
119
|
+
Compose two filters into a new filter whose `apply` pipes each event through
|
|
120
|
+
the first filter and then through the second. If the first filter drops the
|
|
121
|
+
event (returns `nil`), the second is skipped. Composition is associative, so
|
|
122
|
+
`a.chain(b).chain(c)` works as expected.
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
require "philiprehberger/log_filter"
|
|
126
|
+
|
|
127
|
+
redact = Philiprehberger::LogFilter::Filter.new
|
|
128
|
+
.replace(/password=\S+/, "password=[REDACTED]")
|
|
129
|
+
|
|
130
|
+
drop_debug = Philiprehberger::LogFilter::Filter.new
|
|
131
|
+
.drop(/DEBUG/)
|
|
132
|
+
|
|
133
|
+
pipeline = drop_debug.chain(redact)
|
|
134
|
+
|
|
135
|
+
pipeline.apply("DEBUG noise") # => nil (dropped by first filter)
|
|
136
|
+
pipeline.apply("login password=abc123") # => "login password=[REDACTED]"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The chained filter tracks its own `stats` independently of the source filters.
|
|
140
|
+
|
|
117
141
|
### Filter Statistics
|
|
118
142
|
|
|
119
143
|
```ruby
|
|
@@ -144,6 +168,7 @@ filter.stats # => { dropped: 0, passed: 0, replaced: 0, sampled: 0 }
|
|
|
144
168
|
| `Filter#drop_field(key)` | Remove a field from JSON log messages; returns self |
|
|
145
169
|
| `Filter#mask_field(key, with:)` | Mask a field value in JSON log messages; returns self |
|
|
146
170
|
| `Filter#apply(message)` | Run all rules; returns transformed string or nil |
|
|
171
|
+
| `Filter#chain(other)` | Compose with another filter; returns a new filter piping events through both |
|
|
147
172
|
| `Filter#stats` | Return counters: dropped, passed, replaced, sampled |
|
|
148
173
|
| `Filter#reset_stats!` | Zero all statistics counters |
|
|
149
174
|
| `Wrapper.new(logger, filter)` | Wrap a Logger with a filter |
|
|
@@ -121,6 +121,27 @@ module Philiprehberger
|
|
|
121
121
|
result
|
|
122
122
|
end
|
|
123
123
|
|
|
124
|
+
# Compose this filter with +other+ into a new filter.
|
|
125
|
+
#
|
|
126
|
+
# The returned filter's +apply+ runs +self.apply+ first and passes the
|
|
127
|
+
# result through +other.apply+. If the first filter drops the message
|
|
128
|
+
# (returns +nil+), the second filter is not invoked and the chained
|
|
129
|
+
# apply returns +nil+. Composition is associative, so
|
|
130
|
+
# +a.chain(b).chain(c)+ behaves transitively.
|
|
131
|
+
#
|
|
132
|
+
# The chained filter tracks its own +stats+ independently of the two
|
|
133
|
+
# source filters; each source filter continues to track its own
|
|
134
|
+
# counters when invoked.
|
|
135
|
+
#
|
|
136
|
+
# @param other [Filter] the filter to run after this one
|
|
137
|
+
# @return [Filter] a new filter composing +self+ and +other+
|
|
138
|
+
# @raise [ArgumentError] if +other+ is not a {Filter}
|
|
139
|
+
def chain(other)
|
|
140
|
+
raise ArgumentError, 'other must be a Philiprehberger::LogFilter::Filter' unless other.is_a?(Filter)
|
|
141
|
+
|
|
142
|
+
ChainedFilter.new(self, other)
|
|
143
|
+
end
|
|
144
|
+
|
|
124
145
|
private
|
|
125
146
|
|
|
126
147
|
# @param stat [Symbol] the stat key to increment
|
|
@@ -205,5 +226,42 @@ module Philiprehberger
|
|
|
205
226
|
nil
|
|
206
227
|
end
|
|
207
228
|
end
|
|
229
|
+
|
|
230
|
+
# A filter produced by {Filter#chain} that pipes events through two
|
|
231
|
+
# source filters in order. Tracks its own stats independently of the
|
|
232
|
+
# inputs. Short-circuits when the first filter drops the event.
|
|
233
|
+
#
|
|
234
|
+
# @api private
|
|
235
|
+
class ChainedFilter < Filter
|
|
236
|
+
# @param first [Filter] the filter to run first
|
|
237
|
+
# @param second [Filter] the filter to run on +first+'s output
|
|
238
|
+
def initialize(first, second)
|
|
239
|
+
super()
|
|
240
|
+
@first = first
|
|
241
|
+
@second = second
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Run +first.apply+ then +second.apply+. If the first returns +nil+,
|
|
245
|
+
# the second is skipped and +nil+ is returned.
|
|
246
|
+
#
|
|
247
|
+
# @param message [String] the log message to filter
|
|
248
|
+
# @return [String, nil] the transformed message, or +nil+ if dropped
|
|
249
|
+
def apply(message)
|
|
250
|
+
intermediate = @first.apply(message)
|
|
251
|
+
if intermediate.nil?
|
|
252
|
+
increment_stat(:dropped)
|
|
253
|
+
return nil
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
result = @second.apply(intermediate)
|
|
257
|
+
if result.nil?
|
|
258
|
+
increment_stat(:dropped)
|
|
259
|
+
return nil
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
increment_stat(:passed)
|
|
263
|
+
result
|
|
264
|
+
end
|
|
265
|
+
end
|
|
208
266
|
end
|
|
209
267
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: philiprehberger-log_filter
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Philip Rehberger
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-04-18 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Pattern-based log filtering — drop or transform log lines matching rules.
|
|
14
14
|
Includes preset filters for health checks, static assets, and bot traffic.
|
|
@@ -26,11 +26,11 @@ files:
|
|
|
26
26
|
- lib/philiprehberger/log_filter/presets.rb
|
|
27
27
|
- lib/philiprehberger/log_filter/version.rb
|
|
28
28
|
- lib/philiprehberger/log_filter/wrapper.rb
|
|
29
|
-
homepage: https://
|
|
29
|
+
homepage: https://philiprehberger.com/open-source-packages/ruby/philiprehberger-log_filter
|
|
30
30
|
licenses:
|
|
31
31
|
- MIT
|
|
32
32
|
metadata:
|
|
33
|
-
homepage_uri: https://
|
|
33
|
+
homepage_uri: https://philiprehberger.com/open-source-packages/ruby/philiprehberger-log_filter
|
|
34
34
|
source_code_uri: https://github.com/philiprehberger/rb-log-filter
|
|
35
35
|
changelog_uri: https://github.com/philiprehberger/rb-log-filter/blob/main/CHANGELOG.md
|
|
36
36
|
bug_tracker_uri: https://github.com/philiprehberger/rb-log-filter/issues
|