mirror of
https://github.com/meineerde/rackstash.git
synced 2025-10-17 14:01:01 +00:00
A single Sink is tied to a single Logger. It is responsible to: * Create a log event from a Buffer on #write and send it to each of the flows independently. * Forward all actions to all of the defined Flows. The Sink provides access to all configured data of the Logger which is used for persisting the Buffers.
336 lines
9.0 KiB
Ruby
336 lines
9.0 KiB
Ruby
# 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/buffer'
|
|
|
|
describe Rackstash::Buffer do
|
|
let(:buffer_options) { {} }
|
|
let(:sink) { instance_double(Rackstash::Sink) }
|
|
let(:buffer) { described_class.new(sink, **buffer_options) }
|
|
|
|
describe '#allow_empty?' do
|
|
it 'defaults to false' do
|
|
expect(buffer.allow_empty?).to be false
|
|
end
|
|
end
|
|
|
|
describe '#add_message' do
|
|
it 'adds a message to the buffer' do
|
|
msg = double(message: 'Hello World', time: Time.now)
|
|
expect(buffer.add_message(msg)).to equal msg
|
|
|
|
expect(buffer.messages).to eql [msg]
|
|
end
|
|
|
|
it 'sets the timestamp' do
|
|
time = Time.parse('2016-10-17 13:37:00 +03:00')
|
|
msg = double(message: 'Hello World', time: time)
|
|
|
|
buffer.add_message msg
|
|
expect(buffer.timestamp).to eql '2016-10-17T10:37:00.000Z'
|
|
end
|
|
|
|
context 'when buffering?' do
|
|
before do
|
|
buffer_options[:buffering] = true
|
|
end
|
|
|
|
it 'does not call #flush' do
|
|
expect(buffer).not_to receive(:flush)
|
|
buffer.add_message double(message: 'Hello World!', time: Time.now)
|
|
end
|
|
|
|
it 'does not call #clear' do
|
|
expect(buffer).not_to receive(:clear)
|
|
buffer.add_message double(message: 'Hello World!', time: Time.now)
|
|
expect(buffer.messages.count).to eql 1
|
|
end
|
|
end
|
|
|
|
context 'when not buffering?' do
|
|
before do
|
|
buffer_options[:buffering] = false
|
|
end
|
|
|
|
it 'calls #flush' do
|
|
expect(buffer).to receive(:flush)
|
|
buffer.add_message double(message: 'Hello World!', time: Time.now)
|
|
end
|
|
|
|
it 'calls #clear' do
|
|
allow(buffer).to receive(:flush)
|
|
expect(buffer).to receive(:clear).and_call_original
|
|
buffer.add_message double(message: 'Hello World!', time: Time.now)
|
|
expect(buffer.messages.count).to eql 0
|
|
expect(buffer.pending?).to be false
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#buffering?' do
|
|
it 'defaults to false' do
|
|
expect(buffer.buffering?).to be true
|
|
end
|
|
|
|
it 'can be overwritten in initialize' do
|
|
buffer_options[:buffering] = false
|
|
expect(buffer.buffering?).to be false
|
|
end
|
|
end
|
|
|
|
describe '#clear' do
|
|
it 'removes all fields and tags' do
|
|
buffer.fields['foo'] = 'bar'
|
|
buffer.tag 'super_important'
|
|
|
|
buffer.clear
|
|
expect(buffer.tags).to be_empty
|
|
expect(buffer.fields).to be_empty
|
|
end
|
|
|
|
it 'clears the message buffer' do
|
|
buffer.add_message double(message: 'Hello World!', time: Time.now)
|
|
buffer.clear
|
|
|
|
expect(buffer.messages).to eql []
|
|
end
|
|
|
|
it 'removes the pending flag' do
|
|
buffer.add_message double(message: 'raw', time: Time.now)
|
|
|
|
expect(buffer.pending?).to be true
|
|
buffer.clear
|
|
expect(buffer.pending?).to be false
|
|
end
|
|
|
|
it 'resets the timestamp' do
|
|
buffer.timestamp(Time.parse('2016-10-17 15:37:00 +02:00'))
|
|
expect(buffer.timestamp).to eql '2016-10-17T13:37:00.000Z'
|
|
|
|
buffer.clear
|
|
|
|
expect(Time).to receive(:now).and_call_original
|
|
expect(buffer.timestamp).not_to eql '2016-10-17T13:37:00.000Z'
|
|
end
|
|
end
|
|
|
|
describe '#fields' do
|
|
it 'returns a Rackstash::Fields::Hash' do
|
|
expect(buffer.fields).to be_a Rackstash::Fields::Hash
|
|
|
|
# Always returns the same fields object
|
|
expect(buffer.fields).to equal buffer.fields
|
|
end
|
|
|
|
it 'forbids setting reserved fields' do
|
|
expect { buffer.fields['message'] = 'test' } .to raise_error ArgumentError
|
|
expect { buffer.fields['tags'] = 'test' } .to raise_error ArgumentError
|
|
expect { buffer.fields['@version'] = 'test' } .to raise_error ArgumentError
|
|
expect { buffer.fields['@timestamp'] = 'test' } .to raise_error ArgumentError
|
|
end
|
|
end
|
|
|
|
describe '#flush' do
|
|
before do
|
|
# Create a buffering Buffer to prevent #add_message from flushing the
|
|
# Buffer on its own.
|
|
buffer_options[:buffering] = true
|
|
end
|
|
|
|
context 'when pending?' do
|
|
before do
|
|
buffer.add_message double(message: 'Hello World!', time: Time.now)
|
|
|
|
# We might call Buffer#flush during the following tests
|
|
allow(sink).to receive(:write).with(buffer).once
|
|
end
|
|
|
|
it 'flushes the buffer to the sink' do
|
|
expect(sink).to receive(:write).with(buffer).once
|
|
buffer.flush
|
|
end
|
|
|
|
it 'does not clear the buffer' do
|
|
expect(buffer).not_to receive(:clear)
|
|
buffer.flush
|
|
expect(buffer.messages.count).to eql 1
|
|
end
|
|
|
|
it 'returns the buffer' do
|
|
expect(buffer.flush).to equal buffer
|
|
end
|
|
end
|
|
|
|
context 'when not pending?' do
|
|
it 'does not flushes the buffer to the sink' do
|
|
expect(sink).not_to receive(:write)
|
|
buffer.flush
|
|
end
|
|
|
|
it 'does not clear the buffer' do
|
|
expect(buffer).not_to receive(:clear)
|
|
buffer.flush
|
|
end
|
|
|
|
it 'returns nil' do
|
|
expect(buffer.flush).to be nil
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#messages' do
|
|
it 'returns an array of messages' do
|
|
msg = double(message: 'Hello World', time: Time.now)
|
|
buffer.add_message(msg)
|
|
|
|
expect(buffer.messages).to eql [msg]
|
|
end
|
|
|
|
it 'returns a new array each time' do
|
|
expect(buffer.messages).not_to equal buffer.messages
|
|
|
|
expect(buffer.messages).to eql []
|
|
buffer.messages << 'invalid'
|
|
expect(buffer.messages).to eql []
|
|
end
|
|
end
|
|
|
|
describe '#pending?' do
|
|
it 'sets pending when adding a message' do
|
|
buffer.add_message double(message: 'some message', time: Time.now)
|
|
expect(buffer.pending?).to be true
|
|
end
|
|
|
|
context 'allow_empty == true' do
|
|
before do
|
|
buffer_options[:allow_empty] = true
|
|
expect(buffer.allow_empty?).to be true
|
|
end
|
|
|
|
it 'defaults to false' do
|
|
expect(buffer.pending?).to be false
|
|
end
|
|
|
|
it 'is true if there are any fields' do
|
|
buffer.fields['alice'] = 'bob'
|
|
expect(buffer.pending?).to be true
|
|
end
|
|
|
|
it 'is true if there are any tags' do
|
|
buffer.tags << 'alice'
|
|
expect(buffer.pending?).to be true
|
|
end
|
|
end
|
|
|
|
context 'allow_empty == false' do
|
|
before do
|
|
buffer_options[:allow_empty] = false
|
|
expect(buffer.allow_empty?).to be false
|
|
end
|
|
|
|
it 'defaults to false' do
|
|
expect(buffer.pending?).to be false
|
|
end
|
|
|
|
it 'ignores fields' do
|
|
buffer.fields['alice'] = 'bob'
|
|
expect(buffer.pending?).to be false
|
|
end
|
|
|
|
it 'ignores tags' do
|
|
buffer.tags << 'alice'
|
|
expect(buffer.pending?).to be false
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#tag' do
|
|
it 'adds tags' do
|
|
buffer.tag # don't fail with empty argument list
|
|
buffer.tag 'tag1', 'tag2'
|
|
expect(buffer.tags).to contain_exactly('tag1', 'tag2')
|
|
end
|
|
|
|
it 'adds tags only once' do
|
|
buffer.tag 'hello'
|
|
buffer.tag :hello
|
|
|
|
expect(buffer.tags).to contain_exactly('hello')
|
|
end
|
|
|
|
it 'stringifys tags and expands procs' do
|
|
buffer.tag 123, :symbol, -> { :proc }
|
|
expect(buffer.tags).to contain_exactly('123', 'symbol', 'proc')
|
|
end
|
|
|
|
it 'does not set blank tags' do
|
|
buffer.tag 'tag', nil, [], '', {}
|
|
expect(buffer.tags).to contain_exactly('tag')
|
|
end
|
|
|
|
describe 'when passing procs' do
|
|
let(:struct) {
|
|
Struct.new(:value) do
|
|
def to_s
|
|
value
|
|
end
|
|
end
|
|
}
|
|
|
|
let(:object) {
|
|
struct.new('Hello')
|
|
}
|
|
|
|
it 'expands single-value proc objects' do
|
|
buffer.tag(-> { self }, scope: object)
|
|
expect(buffer.tags).to contain_exactly('Hello')
|
|
end
|
|
|
|
it 'expands multi-value proc objects' do
|
|
buffer.tag(-> { [[self, 'foobar'], 123] }, scope: object)
|
|
expect(buffer.tags).to contain_exactly('Hello', 'foobar', '123')
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#timestamp' do
|
|
it 'initializes @timestamp to Time.now.utc' do
|
|
now = Time.parse('2016-10-17 13:37:00 +03:00')
|
|
|
|
expect(Time).to receive(:now).once.and_return(now)
|
|
expect(now).to receive(:getutc).once.and_return(now.getutc)
|
|
|
|
expect(buffer.timestamp).to eql '2016-10-17T10:37:00.000Z'
|
|
expect(buffer.timestamp).to eql '2016-10-17T10:37:00.000Z'
|
|
end
|
|
|
|
it 'initializes @timestamp with the passed time' do
|
|
now = Time.parse('2016-10-17 13:37:00 +03:00')
|
|
|
|
expect(Time).not_to receive(:now)
|
|
expect(buffer.timestamp(now)).to eql '2016-10-17T10:37:00.000Z'
|
|
expect(buffer.timestamp).to eql '2016-10-17T10:37:00.000Z'
|
|
end
|
|
|
|
it 'does not overwrites an already set timestamp' do
|
|
first = Time.parse('2016-10-17 10:10:10 +03:00')
|
|
second = Time.parse('2016-10-17 20:20:20 +03:00')
|
|
|
|
buffer.timestamp(first)
|
|
expect(buffer.timestamp).to eql '2016-10-17T07:10:10.000Z'
|
|
|
|
buffer.timestamp
|
|
expect(buffer.timestamp).to eql '2016-10-17T07:10:10.000Z'
|
|
|
|
buffer.timestamp(second)
|
|
expect(buffer.timestamp).to eql '2016-10-17T07:10:10.000Z'
|
|
end
|
|
end
|
|
end
|