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

Add filters to add default fields or tags to an event

This commit is contained in:
Holger Just 2017-10-10 21:03:13 +02:00
parent fa73e63a3e
commit 2e5c39d5d0
5 changed files with 230 additions and 0 deletions

View File

@ -6,6 +6,8 @@
# of the MIT license. See the LICENSE.txt file for details.
require 'rackstash/filters/clear_color'
require 'rackstash/filters/default_fields'
require 'rackstash/filters/default_tags'
require 'rackstash/filters/rename'
require 'rackstash/filters/replace'
require 'rackstash/filters/skip_event'

View File

@ -0,0 +1,60 @@
# 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.
module Rackstash
module Filters
# The {DefaultFields} filter allows to define fields which should be added
# to an event if they aren't already explicitly set.
#
# Fields can be given either as a `Hash` or a `Proc` which in turn returns a
# `Hash` on `call`. The `Hash` can be nested arbitrarily deep.
#
# Each `Hash` value can again optionally be a `Proc` which is expected to
# return a field value on `call`. You can set nested Hashes or Arrays and
# define nested Procs which in turn are resolved recursively when applying
# the filter. That way, you can set lazy-evaluated values which are only
# resolved at the time the filter is applied to a logged event.
#
# @example
# Rackstash::Flow.new(STDOUT) do
# # All three defined filters set the same default fields
# filter :default_fields, 'beep' => 'boop'
# filter :default_fields, 'beep' => -> { 'boop' }
# filter :default_fields, -> { { 'beep' => 'boop' } }
# end
class DefaultFields
# @param default_fields [Hash<#to_s => Object>, Proc] a `Hash` specifying
# default values for the named keys. You can either give a literal
# `Hash` object or a `Proc` which returns such a `Hash`.
def initialize(default_fields)
@default_fields = default_fields
end
# Add the defined `default_fields` to the event hash, retaining all
# existing values.
#
# @param event [Hash] an event hash
# @return [Hash] the given `event` with the fields renamed
def call(event)
resolver = lambda do |key, old_val, new_val|
if old_val.nil?
new_val
elsif old_val.is_a?(Hash) && new_val.is_a?(Hash)
old_val.merge(new_val, &resolver)
elsif old_val.is_a?(Array) && new_val.is_a?(Array)
old_val | new_val
else
old_val
end
end
fields = Rackstash::Fields::Hash(@default_fields).to_h
event.merge!(fields, &resolver)
end
end
end
end

View File

@ -0,0 +1,54 @@
# 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.
module Rackstash
module Filters
# The {DefaultTags} filter allows to define tags which should be added
# to an event if they aren't already explicitly set there. All existing tags
# on the event are retained.
#
# The default tags are added to the `"tags"` field of the event `Hash`. They
# can be given either as an `Array` of `String`s or a `Proc` which in turn
# returns an `Array` of `String`s on `call`.
#
# Each value of the Array can again optionally be a Proc which in turn is
# expected to return a String on `call`. All the (potentially nested) procs
# are called recursively when applying the filter. That way, you can set
# lazy-evaluated values which are only resolved at the time the filter is
# applied to a logged event.
#
# @example
# Rackstash::Flow.new(STDOUT) do
# # All three defined filters set the same default tags
# filter :default_tags, ['important', 'request']
# filter :default_tags, -> { ['important', 'request'] }
# filter :default_tags, ['important', -> { 'request' }]
# end
class DefaultTags
# @param default_tags [Array<#to_s>, Set<#to_s>, Proc] an `Array`
# specifying default tags for each event. You can either give a literal
# `Array` containing Strings or a `Proc` which returns such an `Array`.
def initialize(*default_tags)
@default_tags = default_tags
end
# Add the defined `default_tags` to the event hash, retaining all
# existing tags. The `"tags"` field on the event will be normalized to a
# plain `Array` containing only `String`s.
#
# @param event [Hash] an event hash
# @return [Hash] the given `event` with the fields renamed
def call(event)
tags = Rackstash::Fields::Tags(event[FIELD_TAGS])
tags.merge!(@default_tags)
event[FIELD_TAGS] = tags.to_a
event
end
end
end
end

View File

@ -0,0 +1,67 @@
# 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 'rackstash/filters/default_fields'
describe Rackstash::Filters::DefaultFields do
let(:event) {
{
'foo' => 'v1',
'bar' => 'v2'
}
}
def filter!(default_fields)
described_class.new(default_fields).call(event)
end
it 'adds missing normalized fields' do
filter! 'new' => 'boing', 123 => :number
expect(event).to eql({
'foo' => 'v1',
'bar' => 'v2',
'new' => 'boing',
'123' => 'number'
})
end
it 'retains existing fields' do
filter! foo: 'ignored'
expect(event).to eql({
'foo' => 'v1',
'bar' => 'v2'
})
end
it 'deep_merges fields' do
event['deep'] = { 'key' => [42, { 'foo' => 'bar' }, nil], 'new' => nil }
filter! 'deep' => { key: [123], new: 'new' }
expect(event).to eql({
'foo' => 'v1',
'bar' => 'v2',
'deep' => {
'key' => [42, { 'foo' => 'bar' }, nil, 123],
'new' => 'new'
}
})
end
it 'resolves Procs' do
filter! -> { { 'beep' => -> { 'boop' } } }
expect(event).to eql({
'foo' => 'v1',
'bar' => 'v2',
'beep' => 'boop'
})
end
end

View File

@ -0,0 +1,47 @@
# 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 'rackstash/filters/default_tags'
describe Rackstash::Filters::DefaultTags do
let(:event) {
{
'key' => 'value'
}
}
def filter!(*default_tags)
described_class.new(*default_tags).call(event)
end
it 'adds missing tags' do
filter! 'foo', 'bar'
expect(event['tags']).to eql ['foo', 'bar']
end
it 'retains existing tags' do
event['tags'] = ['tag']
filter! 'foo', 'bar'
expect(event['tags']).to eql ['tag', 'foo', 'bar']
end
it 'flattens and normalizes tags' do
event['tags'] = 'bare'
filter! [:foo, [[123]]]
expect(event['tags']).to eql ['bare', 'foo', '123']
end
it 'resolves Procs' do
filter! -> { ['beep', -> { ['boop'] }] }
expect(event['tags']).to eql ['beep', 'boop']
end
end