mirror of
https://github.com/meineerde/rackstash.git
synced 2026-01-31 17:27:13 +00:00
Use the ClassRegistry to register filters
This removes the requirement that filters need to be defined in the Rackstash::Filters namespace in order to be usable with a short name. Filters can be registered with an arbitrary name. As before, raw blocks / procs can be defined as ad-hoc filters.
This commit is contained in:
parent
01593a9b8b
commit
a475a5c6fc
@ -172,3 +172,12 @@ require 'rackstash/adapter/file'
|
||||
require 'rackstash/adapter/logger'
|
||||
require 'rackstash/adapter/io'
|
||||
require 'rackstash/adapter/null'
|
||||
|
||||
require 'rackstash/filter/clear_color'
|
||||
require 'rackstash/filter/default_fields'
|
||||
require 'rackstash/filter/default_tags'
|
||||
require 'rackstash/filter/drop_if'
|
||||
require 'rackstash/filter/rename'
|
||||
require 'rackstash/filter/replace'
|
||||
require 'rackstash/filter/truncate_message'
|
||||
require 'rackstash/filter/update'
|
||||
|
||||
@ -5,13 +5,7 @@
|
||||
# This software may be modified and distributed under the terms
|
||||
# of the MIT license. See the LICENSE.txt file for details.
|
||||
|
||||
require 'rackstash/filter/clear_color'
|
||||
require 'rackstash/filter/default_fields'
|
||||
require 'rackstash/filter/default_tags'
|
||||
require 'rackstash/filter/drop_if'
|
||||
require 'rackstash/filter/rename'
|
||||
require 'rackstash/filter/replace'
|
||||
require 'rackstash/filter/truncate_message'
|
||||
require 'rackstash/class_registry'
|
||||
|
||||
module Rackstash
|
||||
# Filters are part of a {Flow} where they can alter the log event before it is
|
||||
@ -29,57 +23,55 @@ module Rackstash
|
||||
# A filter can be any object responding to `call`, e.g. a Proc or a concrete
|
||||
# class inside this module.
|
||||
module Filter
|
||||
# Create a new filter instance from the specified class and the given
|
||||
# arguments. The class can be given as an actual class or as the name of a
|
||||
# filter in which case we are resolving it to a class defined inside the
|
||||
# {Rackstash::Filter} namespace.
|
||||
#
|
||||
# @param klass [Class, Symbol, String, #call] a description of the class
|
||||
# from which we are creating a new filter object. When giving a `Class`,
|
||||
# we are using it as is. When giving a `String` or `Symbol`, we are
|
||||
# determining the associated class from the {Rackstash::Filter} module
|
||||
# and create an instance of that. When giving an object which responds to
|
||||
# `call` already, we return it unchanged, ignoring any additional passed
|
||||
# `args`.
|
||||
# @param args [Array] an optional list of arguments which is passed to the
|
||||
# initializer for the new filter object.
|
||||
# @raise [TypeError] if we can not create a new Filter object from `class`
|
||||
# @raise [NameError] if we could not find a filter class for the specified
|
||||
# class name
|
||||
# @return [Object] a new filter object
|
||||
def self.build(klass, *args, &block)
|
||||
case klass
|
||||
when Class
|
||||
klass.new(*args, &block)
|
||||
when Symbol, String
|
||||
filter_class_name = klass.to_s
|
||||
.sub(/^[a-z\d]*/) { $&.capitalize }
|
||||
.gsub(/(?:_)([a-z\d]*)/) { $1.capitalize }
|
||||
.to_sym
|
||||
filter_class = const_get(filter_class_name, false)
|
||||
filter_class.new(*args, &block)
|
||||
when ->(filter) { filter.respond_to?(:call) }
|
||||
klass
|
||||
else
|
||||
raise TypeError, "Can not build filter for #{klass.inspect}"
|
||||
class << self
|
||||
# @param filter_class [Class] a class from which a new filter can be
|
||||
# created. Filter objects must respond to `call` and accept an event
|
||||
# hash.
|
||||
# @param filter_names [Array<String,Symbol>] one or more names for the
|
||||
# registered `filter_class`. Using these names, the user can create a
|
||||
# new filter object from the registered class in {.build}.
|
||||
# @raise [TypeError] if objects of type were passed
|
||||
# @return [Class] the passed `filter_class`
|
||||
def register(filter_class, *filter_names)
|
||||
filter_names.flatten.each do |name|
|
||||
registry[name] = filter_class
|
||||
end
|
||||
filter_class
|
||||
end
|
||||
end
|
||||
|
||||
# @return [Hash<Symbol => Class>] a Hash with names of filters and their
|
||||
# respective classes which can be used with {Filter.build} to create a
|
||||
# new filter object
|
||||
def self.known
|
||||
constants.each_with_object({}) do |const, known|
|
||||
filter_class = const_get(const, false)
|
||||
next unless filter_class.is_a?(Class)
|
||||
# @return [ClassRegistry] the registry object which allows to register and
|
||||
# retrieve available filter classes
|
||||
def registry
|
||||
@registry ||= Rackstash::ClassRegistry.new('filter'.freeze)
|
||||
end
|
||||
|
||||
filter_class_name = const.to_s
|
||||
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
||||
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
||||
.downcase
|
||||
.to_sym
|
||||
|
||||
known[filter_class_name] = filter_class
|
||||
# Create a new filter instance from the specified class and the given
|
||||
# arguments. The class can be given as an actual class or as the name of a
|
||||
# filter in which case we are resolving it to a class registered to the
|
||||
# {.registry}.
|
||||
#
|
||||
# @param filter_spec [Class, Symbol, String, #call] a description of the
|
||||
# class from which we are creating a new filter object. When giving a
|
||||
# `Class`, we are using it as is to create a new filter object with the
|
||||
# supplied `args` and `block`. When giving a `String` or `Symbol`, we
|
||||
# first use the filter registry to find the matching class. With that,
|
||||
# we then create a filter object as before. When giving an object which
|
||||
# responds to `call` already (e.g. a `Proc`, we return it unchanged,
|
||||
# ignoring any additional passed `args`.
|
||||
# @param args [Array] an optional list of arguments which is passed to the
|
||||
# initializer for the new filter object.
|
||||
# @raise [TypeError] if we can not create a new filter object from the
|
||||
# given `filter_spec`, usually because it is an unsupported type
|
||||
# @raise [KeyError] if we could not find a filter class in the registry
|
||||
# for the specified class name
|
||||
# @return [Object] a new filter object
|
||||
def build(filter_spec, *args, &block)
|
||||
case filter_spec
|
||||
when ->(filter) { filter.respond_to?(:call) }
|
||||
filter_spec
|
||||
else
|
||||
registry[filter_spec].new(*args, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
# This software may be modified and distributed under the terms
|
||||
# of the MIT license. See the LICENSE.txt file for details.
|
||||
|
||||
require 'rackstash/filter'
|
||||
|
||||
module Rackstash
|
||||
module Filter
|
||||
# Remove all ANSI color codes from the `"message"` field of the given event
|
||||
@ -40,5 +42,7 @@ module Rackstash
|
||||
event
|
||||
end
|
||||
end
|
||||
|
||||
register ClearColor, :clear_color
|
||||
end
|
||||
end
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
# This software may be modified and distributed under the terms
|
||||
# of the MIT license. See the LICENSE.txt file for details.
|
||||
|
||||
require 'rackstash/filter'
|
||||
|
||||
module Rackstash
|
||||
module Filter
|
||||
# The {DefaultFields} filter allows to define fields which should be added
|
||||
@ -56,5 +58,7 @@ module Rackstash
|
||||
event.merge!(fields, &resolver)
|
||||
end
|
||||
end
|
||||
|
||||
register DefaultFields, :default_fields
|
||||
end
|
||||
end
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
# This software may be modified and distributed under the terms
|
||||
# of the MIT license. See the LICENSE.txt file for details.
|
||||
|
||||
require 'rackstash/filter'
|
||||
|
||||
module Rackstash
|
||||
module Filter
|
||||
# The {DefaultTags} filter allows to define tags which should be added
|
||||
@ -50,5 +52,7 @@ module Rackstash
|
||||
event
|
||||
end
|
||||
end
|
||||
|
||||
register DefaultTags, :default_tags
|
||||
end
|
||||
end
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
# This software may be modified and distributed under the terms
|
||||
# of the MIT license. See the LICENSE.txt file for details.
|
||||
|
||||
require 'rackstash/filter'
|
||||
|
||||
module Rackstash
|
||||
module Filter
|
||||
# Skip the further processing of the event if the provided condition is
|
||||
@ -50,5 +52,7 @@ module Rackstash
|
||||
event
|
||||
end
|
||||
end
|
||||
|
||||
register DropIf, :drop_if
|
||||
end
|
||||
end
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
# This software may be modified and distributed under the terms
|
||||
# of the MIT license. See the LICENSE.txt file for details.
|
||||
|
||||
require 'rackstash/filter'
|
||||
|
||||
module Rackstash
|
||||
module Filter
|
||||
# Rename one or more fields in the given event.
|
||||
@ -38,5 +40,7 @@ module Rackstash
|
||||
event
|
||||
end
|
||||
end
|
||||
|
||||
register Rename, :rename
|
||||
end
|
||||
end
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
# This software may be modified and distributed under the terms
|
||||
# of the MIT license. See the LICENSE.txt file for details.
|
||||
|
||||
require 'rackstash/filter'
|
||||
|
||||
module Rackstash
|
||||
module Filter
|
||||
# Replace fields in the given event with new values. A new value can be
|
||||
@ -51,5 +53,7 @@ module Rackstash
|
||||
event
|
||||
end
|
||||
end
|
||||
|
||||
register Replace, :replace
|
||||
end
|
||||
end
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
# This software may be modified and distributed under the terms
|
||||
# of the MIT license. See the LICENSE.txt file for details.
|
||||
|
||||
require 'rackstash/filter'
|
||||
|
||||
module Rackstash
|
||||
module Filter
|
||||
# The Truncate filter can be used to restrict the size of the emitted
|
||||
@ -150,5 +152,7 @@ module Rackstash
|
||||
messages.inject(0) { |sum, msg| sum + msg.size }
|
||||
end
|
||||
end
|
||||
|
||||
register TruncateMessage, :truncate_message
|
||||
end
|
||||
end
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
# This software may be modified and distributed under the terms
|
||||
# of the MIT license. See the LICENSE.txt file for details.
|
||||
|
||||
require 'rackstash/filter'
|
||||
|
||||
module Rackstash
|
||||
module Filter
|
||||
# Update fields in the given event with new values. A new value can be
|
||||
@ -53,5 +55,7 @@ module Rackstash
|
||||
event
|
||||
end
|
||||
end
|
||||
|
||||
register Update, :update
|
||||
end
|
||||
end
|
||||
|
||||
@ -142,8 +142,9 @@ describe Rackstash::FilterChain do
|
||||
expect { filter_chain.append(false) }.to raise_error TypeError
|
||||
expect { filter_chain.append(42) }.to raise_error TypeError
|
||||
|
||||
expect { filter_chain.append(:foo) }.to raise_error NameError
|
||||
expect { filter_chain.append('Foo') }.to raise_error NameError
|
||||
# Registered filter was not found
|
||||
expect { filter_chain.append(:foo) }.to raise_error KeyError
|
||||
expect { filter_chain.append('Foo') }.to raise_error KeyError
|
||||
|
||||
expect { filter_chain.append }.to raise_error ArgumentError
|
||||
end
|
||||
@ -341,8 +342,9 @@ describe Rackstash::FilterChain do
|
||||
expect { filter_chain.insert_before(1, false) }.to raise_error TypeError
|
||||
expect { filter_chain.insert_before(1, 42) }.to raise_error TypeError
|
||||
|
||||
expect { filter_chain.insert_before(1, :foo) }.to raise_error NameError
|
||||
expect { filter_chain.insert_before(1, 'Foo') }.to raise_error NameError
|
||||
# Registered filter was not found
|
||||
expect { filter_chain.insert_before(1, :foo) }.to raise_error KeyError
|
||||
expect { filter_chain.insert_before(1, 'Foo') }.to raise_error KeyError
|
||||
|
||||
expect { filter_chain.insert_before(1) }.to raise_error ArgumentError
|
||||
end
|
||||
@ -398,8 +400,9 @@ describe Rackstash::FilterChain do
|
||||
expect { filter_chain.insert_after(1, false) }.to raise_error TypeError
|
||||
expect { filter_chain.insert_after(1, 42) }.to raise_error TypeError
|
||||
|
||||
expect { filter_chain.insert_after(1, :foo) }.to raise_error NameError
|
||||
expect { filter_chain.insert_after(1, 'Foo') }.to raise_error NameError
|
||||
# Registered filter was not found
|
||||
expect { filter_chain.insert_after(1, :foo) }.to raise_error KeyError
|
||||
expect { filter_chain.insert_after(1, 'Foo') }.to raise_error KeyError
|
||||
|
||||
expect { filter_chain.insert_after(1) }.to raise_error ArgumentError
|
||||
end
|
||||
@ -455,8 +458,9 @@ describe Rackstash::FilterChain do
|
||||
expect { filter_chain.unshift(false) }.to raise_error TypeError
|
||||
expect { filter_chain.unshift(42) }.to raise_error TypeError
|
||||
|
||||
expect { filter_chain.unshift(:foo) }.to raise_error NameError
|
||||
expect { filter_chain.unshift('Foo') }.to raise_error NameError
|
||||
# Registered filter was not found
|
||||
expect { filter_chain.unshift(:foo) }.to raise_error KeyError
|
||||
expect { filter_chain.unshift('Foo') }.to raise_error KeyError
|
||||
|
||||
expect { filter_chain.unshift }.to raise_error ArgumentError
|
||||
end
|
||||
|
||||
@ -11,17 +11,23 @@ require 'securerandom'
|
||||
require 'rackstash/filter'
|
||||
|
||||
describe Rackstash::Filter do
|
||||
let(:filter_class) { Class.new }
|
||||
let(:random) { SecureRandom.hex(6) }
|
||||
let(:filter_class_name) { :"FilterClass#{random}" }
|
||||
let(:registry) { Rackstash::ClassRegistry.new('filter') }
|
||||
|
||||
around(:each) do |example|
|
||||
described_class.const_set(filter_class_name, filter_class)
|
||||
example.run
|
||||
described_class.send(:remove_const, filter_class_name)
|
||||
end
|
||||
let(:filter_class) {
|
||||
Class.new do
|
||||
def call(event)
|
||||
'filtered'
|
||||
end
|
||||
end
|
||||
}
|
||||
let(:filter_name) { :"filter_class_#{SecureRandom.hex(6)}" }
|
||||
|
||||
describe '.build' do
|
||||
before do
|
||||
allow(described_class).to receive(:registry).and_return(registry)
|
||||
described_class.register(filter_class, filter_name)
|
||||
end
|
||||
|
||||
it 'builds a filter from a class' do
|
||||
args = ['arg1', foo: 'bar']
|
||||
expect(filter_class).to receive(:new).with(*args)
|
||||
@ -33,45 +39,44 @@ describe Rackstash::Filter do
|
||||
args = ['arg1', foo: 'bar']
|
||||
expect(filter_class).to receive(:new).with(*args)
|
||||
|
||||
described_class.build(:"filter_class#{random}", *args)
|
||||
described_class.build(filter_name.to_sym, *args)
|
||||
end
|
||||
|
||||
it 'builds a filter from a String' do
|
||||
args = ['arg1', foo: 'bar']
|
||||
expect(filter_class).to receive(:new).with(*args)
|
||||
|
||||
described_class.build("filter_class#{random}", *args)
|
||||
described_class.build(filter_name.to_s, *args)
|
||||
end
|
||||
|
||||
it 'returns an existing filter' do
|
||||
filter = -> {}
|
||||
|
||||
expect(described_class.build(filter)).to equal filter
|
||||
expect(described_class.build(filter, :ignored, 42)).to equal filter
|
||||
end
|
||||
|
||||
it 'raises a TypeError with different arguments' do
|
||||
expect { described_class.build(123) }.to raise_error(TypeError)
|
||||
expect { described_class.build(nil) }.to raise_error(TypeError)
|
||||
expect { described_class.build(true) }.to raise_error(TypeError)
|
||||
it 'raises a TypeError with invalid spec types' do
|
||||
expect { described_class.build(123) }
|
||||
.to raise_error(TypeError, '123 can not be used to describe filters')
|
||||
expect { described_class.build(nil) }
|
||||
.to raise_error(TypeError, 'nil can not be used to describe filters')
|
||||
expect { described_class.build(true) }
|
||||
.to raise_error(TypeError, 'true can not be used to describe filters')
|
||||
end
|
||||
|
||||
expect { described_class.build('MissingFilter') }.to raise_error(NameError)
|
||||
expect { described_class.build(:missing_filter) }.to raise_error(NameError)
|
||||
it 'raises a KeyError for undefined filters' do
|
||||
expect { described_class.build('MissingFilter') }
|
||||
.to raise_error(KeyError, 'No filter was registered for "MissingFilter"')
|
||||
expect { described_class.build(:missing_filter) }
|
||||
.to raise_error(KeyError, 'No filter was registered for :missing_filter')
|
||||
end
|
||||
end
|
||||
|
||||
describe '.known' do
|
||||
it 'returns a Hash with known filters' do
|
||||
expect(described_class.known).not_to be_empty
|
||||
|
||||
expect(described_class.known.keys).to all(
|
||||
be_a(Symbol)
|
||||
.and match(/\A[a-z0-9_]+\z/)
|
||||
)
|
||||
expect(described_class.known.values).to all be_a(Class)
|
||||
end
|
||||
|
||||
it 'includes Filter classes' do
|
||||
expect(described_class.known[:"filter_class#{random}"]).to equal filter_class
|
||||
describe 'registry' do
|
||||
it 'returns the filter registry' do
|
||||
expect(described_class.registry).to be_instance_of Rackstash::ClassRegistry
|
||||
expect(described_class.registry.object_type).to eql 'filter'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user