1
0
mirror of https://github.com/meineerde/rackstash.git synced 2026-01-31 17:27:13 +00:00

Register encoders using a ClassRegistry

This commit is contained in:
Holger Just 2017-12-20 23:58:32 +01:00
parent de6b60925a
commit 6eaeaedaf8
9 changed files with 191 additions and 6 deletions

View File

@ -173,6 +173,13 @@ require 'rackstash/adapter/logger'
require 'rackstash/adapter/io'
require 'rackstash/adapter/null'
require 'rackstash/encoder/hash'
require 'rackstash/encoder/json'
require 'rackstash/encoder/lograge'
require 'rackstash/encoder/logstash'
require 'rackstash/encoder/message'
require 'rackstash/encoder/raw'
require 'rackstash/filter/clear_color'
require 'rackstash/filter/default_fields'
require 'rackstash/filter/default_tags'

View File

@ -5,9 +5,80 @@
# This software may be modified and distributed under the terms
# of the MIT license. See the LICENSE.txt file for details.
require 'rackstash/encoder/hash'
require 'rackstash/encoder/json'
require 'rackstash/encoder/lograge'
require 'rackstash/encoder/logstash'
require 'rackstash/encoder/message'
require 'rackstash/encoder/raw'
module Rackstash
# An Encoder is part of a {Flow} where they are responsible to transform the
# filtered event into a format suitable for writing by the final log
# {Adapter}.
#
# The encoder needs to be selected together with the log {Adapter}. While many
# adapters support different encoders, some require a specific format to be
# sent over a wire. Please consult the documentation of your desired adapter
# for details.
#
# Each adapter can define their common default encoder. In a flow, you can
# optionally overwrite the used encoder to select a different log format (e.g.
# to log using the {Lograge} key-value syntax instead of the common default of
# {JSON}.
#
# An encoder can be any object responding to `encode`, e.g. a Proc or an
# instance of a class inside this module. Note that although Strings respond
# to the `encode` method, they are not suitable encoders since Strings can not
# deal with events on their own.
module Encoder
class << self
# @param encoder_class [Class] a class from which a new encoder can be
# created. Filter objects must respond to `encode` and accept an event
# hash.
# @param names [Array<String,Symbol>] one or more names for the
# registered `encoder_class`. Using these names, the user can create a
# new encoder object from the registered class in {.build}.
# @raise [TypeError] if objects of type were passed
# @return [Class] the passed `filter_class`
def register(encoder_class, *names)
unless encoder_class.is_a?(Class) &&
encoder_class.instance_methods.include?(:encode)
raise TypeError, 'Can only register encoder classes'
end
names.flatten.each do |name|
registry[name] = encoder_class
end
encoder_class
end
# @return [ClassRegistry] the registry object which allows to register and
# retrieve available encoder classes
def registry
@registry ||= Rackstash::ClassRegistry.new('encoder'.freeze)
end
# Create a new encoder instance from the specified class and the given
# arguments. The class can be given as an actual class or as the name of
# an encoder in which case we are resolving it to a class registered to
# the {.registry}.
#
# @param encoder_spec [Class, Symbol, String, #encode] 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(encoder_spec, *args, &block)
if encoder_spec.respond_to?(:encode) && !encoder_spec.is_a?(String)
encoder_spec
else
registry[encoder_spec].new(*args, &block)
end
end
end
end
end

View File

@ -5,6 +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/encoder'
require 'rackstash/encoder/helper/message'
require 'rackstash/encoder/helper/timestamp'
@ -26,5 +27,7 @@ module Rackstash
event
end
end
register Hash, :hash
end
end

View File

@ -7,6 +7,7 @@
require 'json'
require 'rackstash/encoder'
require 'rackstash/encoder/helper/message'
require 'rackstash/encoder/helper/timestamp'
@ -29,5 +30,7 @@ module Rackstash
::JSON.dump(event)
end
end
register JSON, :json, :JSON
end
end

View File

@ -5,6 +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/encoder'
require 'rackstash/encoder/helper/timestamp'
module Rackstash
@ -136,5 +137,7 @@ module Rackstash
"#{key}=#{value}"
end
end
register Lograge, :lograge
end
end

View File

@ -5,6 +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/encoder'
require 'rackstash/encoder/json'
module Rackstash
@ -26,5 +27,7 @@ module Rackstash
super(event)
end
end
register Logstash, :logstash
end
end

View File

@ -5,6 +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/encoder'
require 'rackstash/encoder/helper/message'
module Rackstash
@ -70,5 +71,7 @@ module Rackstash
end
end
end
register Message, :message
end
end

View File

@ -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/encoder'
module Rackstash
module Encoder
# The Raw encoder passes along the raw unformatted event hash. It still
@ -20,5 +22,7 @@ module Rackstash
event
end
end
register Raw, :raw
end
end

View File

@ -0,0 +1,88 @@
# frozen_string_literal: true
#
# Copyright 2017 Holger Just
#
# This software may be modified and distributed under the terms
# of the MIT license. See the LICENSE.txt file for details.
require 'spec_helper'
require 'securerandom'
require 'rackstash/encoder'
describe Rackstash::Encoder do
let(:registry) { Rackstash::ClassRegistry.new('encoder') }
let(:encoder_class) {
Class.new do
def encode(event)
'encoded'
end
end
}
let(:encoder_name) { :"encoder_class_#{SecureRandom.hex(6)}" }
describe '.build' do
before do
allow(described_class).to receive(:registry).and_return(registry)
described_class.register(encoder_class, encoder_name)
end
it 'builds an encoder from a class' do
args = ['arg1', foo: 'bar']
expect(encoder_class).to receive(:new).with(*args)
described_class.build(encoder_class, *args)
end
it 'builds a encoder from a Symbol' do
args = ['arg1', foo: 'bar']
expect(encoder_class).to receive(:new).with(*args)
p described_class.registry
described_class.build(encoder_name.to_sym, *args)
end
it 'builds a encoder from a String' do
args = ['arg1', foo: 'bar']
expect(encoder_class).to receive(:new).with(*args)
described_class.build(encoder_name.to_s, *args)
end
it 'returns an existing encoder' do
encoder = Class.new do
def encode(event)
'custom'
end
end.new
expect(described_class.build(encoder)).to equal encoder
expect(described_class.build(encoder, :ignored, 42)).to equal encoder
end
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 encoders')
expect { described_class.build(nil) }
.to raise_error(TypeError, 'nil can not be used to describe encoders')
expect { described_class.build(true) }
.to raise_error(TypeError, 'true can not be used to describe encoders')
end
it 'raises a KeyError for undefined encoders' do
expect { described_class.build('MissingEncoder') }
.to raise_error(KeyError, 'No encoder was registered for "MissingEncoder"')
expect { described_class.build(:missing_encoder) }
.to raise_error(KeyError, 'No encoder was registered for :missing_encoder')
end
end
describe 'registry' do
it 'returns the encoder registry' do
expect(described_class.registry).to be_instance_of Rackstash::ClassRegistry
expect(described_class.registry.object_type).to eql 'encoder'
end
end
end