1
0
mirror of https://github.com/meineerde/rackstash.git synced 2026-02-01 01:37:12 +00:00

Rename the DropIf filter to Drop and allow it to drop a percentage of events

With this, we also drop the ability to define conditions in the filter
itself. When adding a filter, users can still setup a condition using
the common functionality of all filters.
This commit is contained in:
Holger Just 2018-08-28 10:33:17 +02:00
parent 9152b67df0
commit 8a19dea76c
5 changed files with 141 additions and 105 deletions

View File

@ -183,7 +183,7 @@ require 'rackstash/filter/anonymize_ip_mask'
require 'rackstash/filter/clear_color'
require 'rackstash/filter/default_fields'
require 'rackstash/filter/default_tags'
require 'rackstash/filter/drop_if'
require 'rackstash/filter/drop'
require 'rackstash/filter/rename'
require 'rackstash/filter/replace'
require 'rackstash/filter/truncate_message'

View File

@ -0,0 +1,72 @@
# 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 'rackstash/filter'
module Rackstash
module Filter
# This filter skips a certain percentage of events passed through it. It
# does not change the `event` hash in any way on its own.
#
# You can select the events to be filtered be using an `:if` or `:unless`
# guard when adding the filter to the {FilterChain}.
#
# @example
# Rackstash::Flow.new(STDOUT) do
# # Drop half of all the events which have a 'debug' tag
# filter :drop, percent: 50, if: ->(event) { event['tags'].include?('debug') }
# end
class Drop
# @return [Integer] the percentage of events dropped by this filter
attr_reader :percent
# @param percent [Integer] the percentage of events passed through this
# filter which are dropped. Can be an integer between 0 and 100
# (inclusive).
def initialize(percent: 100)
@percent = Integer(percent)
unless percent.between?(0, 100)
raise ArgumentError, "percent must be an Integer between 0 and 100"
end
@rand = Random.new
end
# Run the filter against the passed `event` hash.
#
# We drop a defined percentage of log events passing through the filter.
# If an `event` is selected to be dropped, we return `false`, else we just
# return the passed event.
#
# @param event [Hash] an event hash
# @return [Hash, false] the given `event` or `false` if the event is
# dropped
def call(event)
return false if drop?
event
end
private
# @return [Bool] `true` is the event should be dropped based on the
# defined drop percentage, `false` otherwise.
def drop?
return true if @percent == 100
return true if random_percentage < @percent
false
end
# @return [Integer] a random number between 0 and 99 (inclusive).
def random_percentage
@rand.rand(100)
end
end
register Drop, :drop
end
end

View File

@ -1,58 +0,0 @@
# 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 'rackstash/filter'
module Rackstash
module Filter
# Skip the further processing of the event if the provided condition is
# truethy. In that case, the event will be dropped and not be written to the
# log adapter.
#
# This filter is a basic example of how you can write filters which abort
# further processing of an event. You can write your own filters which
# provide similar (but probably more useful) behavior.
#
# @example
# Rackstash::Flow.new(STDOUT) do
# # Drop the event if it has the 'debug' tag
# filter :drop_if, ->(event) { event['tags'].include?('debug') }
# end
class DropIf
# @param drop_if [#call] a callable object (e.g. a `Proc`) which returns a
# truethy or falsey value on `call` with an `event` hash. If it returns
# something truethy, we abort any further processing of the event. If the
# `drop_if` filter is not given, we expect a block to be provided which
# is used instead.
def initialize(drop_if = nil, &block)
if drop_if.respond_to?(:call)
@drop_if = drop_if
elsif block_given?
@drop_if = block
else
raise ArgumentError, 'must provide a condition when to drop the event'
end
end
# Run the filter against the passed `event` hash.
#
# We will call the `drop_if` object with the passed event. If the return
# value is truethy, we abort any further processing of the event. This
# filter does not change the `event` hash in any way on its own.
#
# @param event [Hash] an event hash
# @return [Hash, false] the given `event` or `false` if the `drop_if`
# condition was evaluated
def call(event)
return false if @drop_if.call(event)
event
end
end
register DropIf, :drop_if
end
end

View File

@ -1,46 +0,0 @@
# frozen_string_literal: true
#
# Copyright 2017 - 2018 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/filter/drop_if'
RSpec.describe Rackstash::Filter::DropIf do
describe '#initialize' do
it 'expects a condition' do
expect { described_class.new }.to raise_error ArgumentError
end
it 'accepts a callable object' do
expect { described_class.new(->(event) {}) }.not_to raise_error
end
it 'accepts a block' do
expect { described_class.new {} }.not_to raise_error
end
end
describe '#call' do
it 'returns the event if the condition is falsey' do
event = { 'foo' => 'bar' }
expect(described_class.new(->(_event) { false }).call(event)).to equal event
expect(described_class.new(->(_event) { nil }).call(event)).to equal event
expect(described_class.new { |_event| false }.call(event)).to equal event
expect(described_class.new { |_event| nil }.call(event)).to equal event
end
it 'returns false if the condition is truethy' do
event = { 'foo' => 'bar' }
expect(described_class.new(->(_event) { true }).call(event)).to be false
expect(described_class.new(->(_event) { event }).call(event)).to be false
expect(described_class.new { |_event| true }.call(event)).to be false
expect(described_class.new { |_event| event }.call(event)).to be false
end
end
end

View File

@ -0,0 +1,68 @@
# frozen_string_literal: true
#
# Copyright 2017 - 2018 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/filter/drop'
RSpec.describe Rackstash::Filter::Drop do
let(:event) {
{what: :ever}
}
describe '#initialize' do
it 'accepts a percentage' do
filter = described_class.new(percent: 23)
expect(filter.percent).to eql 23
end
it 'defaults to 100%' do
expect(described_class.new.percent).to eql 100
end
it 'only accepts valid percentages' do
expect { described_class.new(percent: -1) }.to raise_error ArgumentError
expect { described_class.new(percent: 101) }.to raise_error ArgumentError
expect { described_class.new(percent: 'value') }.to raise_error ArgumentError
expect { described_class.new(percent: :value) }.to raise_error TypeError
expect { described_class.new(percent: false) }.to raise_error TypeError
end
end
describe '#call' do
it 'always returns the event with percent: 0' do
drop_all = described_class.new(percent: 0)
expect(1_000.times.count { drop_all.call(event) == event }).to eql 1_000
end
it 'drops about half of the events with percent: 50' do
drop_half = described_class.new(percent: 50)
expect(drop_half).to receive(:random_percentage)
.and_return(*[0, 99] * 500)
expect(1_000.times.count { drop_half.call(event) == event })
.to eql 500
end
it 'drops 99% of the events with percent: 99' do
drop_most = described_class.new(percent: 99)
expect(drop_most).to receive(:random_percentage)
.and_return(*(0..99).to_a.shuffle * 10)
expect(1_000.times.count { drop_most.call(event) == event })
.to eql 10
end
it 'always returns false with percent: 100' do
drop_all = described_class.new(percent: 100)
expect(1_000.times.count { drop_all.call(event) == event }).to eql 0
end
end
end