philiprehberger-log_filter 0.1.2

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3b4641d0484bf50c2cc4b567eae1af24be89009b8e85c74b9e603f10ec835f63
4
+ data.tar.gz: d79d7ff53b0fe31b17ead3ea34d05006c856408ee7c88bf7c6eedf6f4bca808e
5
+ SHA512:
6
+ metadata.gz: 84988453f7ab48620e52056c05f41bae19a7963a8e008aef5e5e9320d92c5d5b7afb1697afeecfb6597c4a094f3090fcf84f7e3950e2459369d76f018576a098
7
+ data.tar.gz: 39aef2aa4d1f08094a5f77a91535ceadb09d6ff506b068ee4fc56b4dc9ae48840f6fb6fb0e973d03216fce40fd424d53cc8b1e91b501235f145e4eb030b51c2c
data/CHANGELOG.md ADDED
@@ -0,0 +1,22 @@
1
+ # Changelog
2
+
3
+ All notable changes to this gem will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.2] - 2026-03-13
11
+
12
+ ### Fixed
13
+ - Fix RuboCop ExtraSpacing offense in gemspec metadata
14
+
15
+ ## [0.1.0] - 2026-03-13
16
+
17
+ ### Added
18
+ - Initial release
19
+ - `Filter` class with `#drop`, `#drop_if`, and `#replace` rules
20
+ - `Wrapper` class for wrapping Ruby Logger with filter support
21
+ - `Presets` module with `health_check`, `assets`, and `bots` factory methods
22
+ - Convenience methods on `Philiprehberger::LogFilter` module
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 philiprehberger
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # philiprehberger-log_filter
2
+
3
+ [![Tests](https://github.com/philiprehberger/rb-log-filter/actions/workflows/ci.yml/badge.svg)](https://github.com/philiprehberger/rb-log-filter/actions/workflows/ci.yml)
4
+ [![Gem Version](https://badge.fury.io/rb/philiprehberger-log_filter.svg)](https://rubygems.org/gems/philiprehberger-log_filter)
5
+
6
+ Pattern-based log filtering with drop, replace, and preset rules.
7
+
8
+ ## Requirements
9
+
10
+ - Ruby >= 3.1
11
+
12
+ ## Installation
13
+
14
+ Add to your Gemfile:
15
+
16
+ ```ruby
17
+ gem "philiprehberger-log_filter"
18
+ ```
19
+
20
+ Then run:
21
+
22
+ ```bash
23
+ bundle install
24
+ ```
25
+
26
+ Or install directly:
27
+
28
+ ```bash
29
+ gem install philiprehberger-log_filter
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ ```ruby
35
+ require "philiprehberger/log_filter"
36
+
37
+ # Build a custom filter chain
38
+ filter = Philiprehberger::LogFilter::Filter.new
39
+ .drop(/health_?check/i)
40
+ .drop(/DEBUG/)
41
+ .replace(/password=\S+/, "password=[REDACTED]")
42
+
43
+ filter.apply("GET /healthcheck 200") # => nil (dropped)
44
+ filter.apply("DEBUG some noise") # => nil (dropped)
45
+ filter.apply("login password=abc123") # => "login password=[REDACTED]"
46
+ filter.apply("GET /api/users 200") # => "GET /api/users 200"
47
+ ```
48
+
49
+ ### Wrapping a Logger
50
+
51
+ ```ruby
52
+ require "logger"
53
+ require "philiprehberger/log_filter"
54
+
55
+ logger = Logger.new($stdout)
56
+ filter = Philiprehberger::LogFilter::Filter.new
57
+ .drop(/healthcheck/i)
58
+ .replace(/token=\S+/, "token=[REDACTED]")
59
+
60
+ filtered_logger = Philiprehberger::LogFilter.wrap(logger, filter)
61
+
62
+ filtered_logger.info("GET /healthcheck 200") # silently dropped
63
+ filtered_logger.info("auth token=secret123") # logs "auth token=[REDACTED]"
64
+ filtered_logger.info("GET /api/users 200") # logs normally
65
+ ```
66
+
67
+ ### Using Presets
68
+
69
+ ```ruby
70
+ # Drop health-check noise
71
+ filter = Philiprehberger::LogFilter.health_check_filter
72
+ filtered_logger = Philiprehberger::LogFilter.wrap(logger, filter)
73
+
74
+ # Drop static asset requests
75
+ filter = Philiprehberger::LogFilter.asset_filter
76
+
77
+ # Drop bot/crawler traffic
78
+ filter = Philiprehberger::LogFilter.bot_filter
79
+ ```
80
+
81
+ ### Block-Based Drop Rules
82
+
83
+ ```ruby
84
+ filter = Philiprehberger::LogFilter::Filter.new
85
+ .drop_if { |msg| msg.length > 1000 } # drop excessively long messages
86
+ .drop_if { |msg| msg.count("\n") > 10 } # drop multi-line spam
87
+ ```
88
+
89
+ ## API
90
+
91
+ | Class / Method | Description |
92
+ |----------------|-------------|
93
+ | `Filter.new` | Create a new empty filter chain |
94
+ | `Filter#drop(pattern)` | Add a regex drop rule; returns self |
95
+ | `Filter#drop_if(&block)` | Add a block-based drop rule; returns self |
96
+ | `Filter#replace(pattern, replacement)` | Add a replacement rule; returns self |
97
+ | `Filter#apply(message)` | Run all rules; returns transformed string or nil |
98
+ | `Wrapper.new(logger, filter)` | Wrap a Logger with a filter |
99
+ | `Presets.health_check` | Filter dropping health-check paths |
100
+ | `Presets.assets` | Filter dropping static-asset requests |
101
+ | `Presets.bots` | Filter dropping bot/crawler traffic |
102
+ | `LogFilter.wrap(logger, filter)` | Convenience wrapper constructor |
103
+ | `LogFilter.health_check_filter` | Shortcut for `Presets.health_check` |
104
+ | `LogFilter.asset_filter` | Shortcut for `Presets.assets` |
105
+ | `LogFilter.bot_filter` | Shortcut for `Presets.bots` |
106
+
107
+ ## Development
108
+
109
+ ```bash
110
+ bundle install
111
+ bundle exec rspec # Run tests
112
+ bundle exec rubocop # Check code style
113
+ ```
114
+
115
+ ## License
116
+
117
+ MIT
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Philiprehberger
4
+ module LogFilter
5
+ # Chain of rules that can drop or transform log messages.
6
+ #
7
+ # Rules are evaluated in the order they were added. A drop rule
8
+ # short-circuits and returns +nil+. A replace rule mutates the
9
+ # message string before passing it to the next rule.
10
+ class Filter
11
+ # @return [Array<Hash>] the ordered list of rules
12
+ attr_reader :rules
13
+
14
+ def initialize
15
+ @rules = []
16
+ end
17
+
18
+ # Add a pattern-based drop rule. Messages matching +pattern+ are suppressed.
19
+ #
20
+ # @param pattern [Regexp] the pattern to match against
21
+ # @return [self] for chaining
22
+ def drop(pattern)
23
+ @rules << { type: :drop_pattern, pattern: pattern }
24
+ self
25
+ end
26
+
27
+ # Add a block-based drop rule. Messages for which the block returns
28
+ # a truthy value are suppressed.
29
+ #
30
+ # @yield [message] evaluates whether the message should be dropped
31
+ # @yieldparam message [String]
32
+ # @yieldreturn [Boolean]
33
+ # @return [self] for chaining
34
+ def drop_if(&block)
35
+ @rules << { type: :drop_block, block: block }
36
+ self
37
+ end
38
+
39
+ # Add a replacement rule. Occurrences of +pattern+ in the message
40
+ # are replaced with +replacement+.
41
+ #
42
+ # @param pattern [Regexp] the pattern to match
43
+ # @param replacement [String] the replacement string
44
+ # @return [self] for chaining
45
+ def replace(pattern, replacement)
46
+ @rules << { type: :replace, pattern: pattern, replacement: replacement }
47
+ self
48
+ end
49
+
50
+ # Run all rules against +message+ in order.
51
+ #
52
+ # @param message [String] the log message to filter
53
+ # @return [String, nil] the transformed message, or +nil+ if dropped
54
+ def apply(message)
55
+ result = message.dup
56
+
57
+ @rules.each do |rule|
58
+ result = apply_rule(rule, result)
59
+ return nil if result.nil?
60
+ end
61
+
62
+ result
63
+ end
64
+
65
+ private
66
+
67
+ # @param rule [Hash] a single rule hash
68
+ # @param message [String] the current message
69
+ # @return [String, nil]
70
+ def apply_rule(rule, message)
71
+ case rule[:type]
72
+ when :drop_pattern then message.match?(rule[:pattern]) ? nil : message
73
+ when :drop_block then rule[:block].call(message) ? nil : message
74
+ when :replace then message.gsub(rule[:pattern], rule[:replacement])
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Philiprehberger
4
+ module LogFilter
5
+ # Factory methods that return pre-configured {Filter} instances for
6
+ # common log-noise scenarios.
7
+ module Presets
8
+ # Filter that drops health-check request log lines.
9
+ #
10
+ # @return [Filter] a filter suppressing health-check paths
11
+ def self.health_check
12
+ Filter.new.drop(%r{health_?check|/health|/ping|/ready|/alive}i)
13
+ end
14
+
15
+ # Filter that drops static-asset request log lines.
16
+ #
17
+ # @return [Filter] a filter suppressing asset paths
18
+ def self.assets
19
+ Filter.new.drop(/\.(css|js|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|map)\b/i)
20
+ end
21
+
22
+ # Filter that drops bot/crawler request log lines.
23
+ #
24
+ # @return [Filter] a filter suppressing bot user-agents
25
+ def self.bots
26
+ Filter.new.drop(/bot|crawler|spider|slurp|googlebot|bingbot/i)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Philiprehberger
4
+ module LogFilter
5
+ VERSION = "0.1.2"
6
+ end
7
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Philiprehberger
4
+ module LogFilter
5
+ # Wraps a Ruby Logger (or any object responding to the standard log
6
+ # level methods) and applies a {Filter} to every message before
7
+ # forwarding.
8
+ class Wrapper
9
+ LOG_LEVELS = %i[debug info warn error fatal].freeze
10
+
11
+ # @param logger [Logger] the underlying logger to delegate to
12
+ # @param filter [Filter] the filter to apply to messages
13
+ def initialize(logger, filter)
14
+ @logger = logger
15
+ @filter = filter
16
+ end
17
+
18
+ LOG_LEVELS.each do |level|
19
+ # @param message [String, nil] the log message
20
+ # @param args [Array] additional positional arguments
21
+ # @return [void]
22
+ define_method(level) do |message = nil, *args, &block|
23
+ message = block&.call if message.nil? && block
24
+ return if message.nil?
25
+
26
+ filtered = @filter.apply(message.to_s)
27
+ return if filtered.nil?
28
+
29
+ @logger.public_send(level, filtered, *args)
30
+ end
31
+ end
32
+
33
+ # @return [Integer] the current log level
34
+ def level
35
+ @logger.level
36
+ end
37
+
38
+ # @param new_level [Integer, Symbol] the new log level
39
+ # @return [void]
40
+ def level=(new_level)
41
+ @logger.level = new_level
42
+ end
43
+
44
+ # Close the underlying logger.
45
+ #
46
+ # @return [void]
47
+ def close
48
+ @logger.close
49
+ end
50
+
51
+ # Delegate unknown methods to the underlying logger.
52
+ def method_missing(method_name, ...)
53
+ if @logger.respond_to?(method_name)
54
+ @logger.public_send(method_name, ...)
55
+ else
56
+ super
57
+ end
58
+ end
59
+
60
+ # @return [Boolean]
61
+ def respond_to_missing?(method_name, include_private = false)
62
+ @logger.respond_to?(method_name, include_private) || super
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "log_filter/version"
4
+ require_relative "log_filter/filter"
5
+ require_relative "log_filter/wrapper"
6
+ require_relative "log_filter/presets"
7
+
8
+ module Philiprehberger
9
+ module LogFilter
10
+ class Error < StandardError; end
11
+
12
+ def self.health_check_filter
13
+ Presets.health_check
14
+ end
15
+
16
+ def self.asset_filter
17
+ Presets.assets
18
+ end
19
+
20
+ def self.bot_filter
21
+ Presets.bots
22
+ end
23
+
24
+ def self.wrap(logger, filter)
25
+ Wrapper.new(logger, filter)
26
+ end
27
+ end
28
+ end
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: philiprehberger-log_filter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Philip Rehberger
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-03-13 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Pattern-based log filtering — drop or transform log lines matching rules.
14
+ Includes preset filters for health checks, static assets, and bot traffic.
15
+ email:
16
+ - me@philiprehberger.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - CHANGELOG.md
22
+ - LICENSE
23
+ - README.md
24
+ - lib/philiprehberger/log_filter.rb
25
+ - lib/philiprehberger/log_filter/filter.rb
26
+ - lib/philiprehberger/log_filter/presets.rb
27
+ - lib/philiprehberger/log_filter/version.rb
28
+ - lib/philiprehberger/log_filter/wrapper.rb
29
+ homepage: https://github.com/philiprehberger/rb-log-filter
30
+ licenses:
31
+ - MIT
32
+ metadata:
33
+ homepage_uri: https://github.com/philiprehberger/rb-log-filter
34
+ source_code_uri: https://github.com/philiprehberger/rb-log-filter
35
+ changelog_uri: https://github.com/philiprehberger/rb-log-filter/blob/main/CHANGELOG.md
36
+ rubygems_mfa_required: 'true'
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: 3.1.0
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ requirements: []
52
+ rubygems_version: 3.5.22
53
+ signing_key:
54
+ specification_version: 4
55
+ summary: Pattern-based log filtering with drop, replace, and preset rules
56
+ test_files: []