diff --git a/lib/rackstash/encoders/helpers/message.rb b/lib/rackstash/encoders/helpers/message.rb new file mode 100644 index 0000000..0279a96 --- /dev/null +++ b/lib/rackstash/encoders/helpers/message.rb @@ -0,0 +1,42 @@ +# 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 Encoders + module Helpers + # Some useful helper methods for {Encoders} which help in normalizing and + # handling the message list in the event Hash. + module Message + # Normalize the `"message"` field of the given log event Hash. + # + # While the filters still had access to the array of {Message} objects + # for filtering, we now concatenate the raw message objects as a single + # string to get the final message which is set on the `event["message"]` + # key. + # + # We expect that the single messages already contain trailing newline + # characters is deemed useful. These are usually added by the formatter + # of the frontend {Logger}. + # + # @param event [Hash] a log event Hash + # @return [Hash] the given event with the `"message"` key set as a + # single string. + def normalize_message(event) + event[FIELD_MESSAGE] = + case event[FIELD_MESSAGE] + when Array + event[FIELD_MESSAGE].map!(&:to_s).join + when nil + '' + else + event[FIELD_MESSAGE].to_s + end + event + end + end + end + end +end diff --git a/lib/rackstash/encoders/json.rb b/lib/rackstash/encoders/json.rb index 464a9aa..e874f54 100644 --- a/lib/rackstash/encoders/json.rb +++ b/lib/rackstash/encoders/json.rb @@ -6,6 +6,8 @@ require 'json' +require 'rackstash/encoders/helpers/message' + module Rackstash module Encoders # The JSON encoder formats the log event as a single-line JSON string. The @@ -13,9 +15,13 @@ module Rackstash # # Most {Adapters} default to use this codec. class JSON + include Rackstash::Encoders::Helpers::Message + # @param event [Hash] a log event as produced by the {Flow} # @return [String] the event as a single-line JSON string def encode(event) + normalize_message(event) + ::JSON.dump(event) end end diff --git a/lib/rackstash/encoders/message.rb b/lib/rackstash/encoders/message.rb index 7618bb5..253fe7d 100644 --- a/lib/rackstash/encoders/message.rb +++ b/lib/rackstash/encoders/message.rb @@ -4,6 +4,8 @@ # This software may be modified and distributed under the terms # of the MIT license. See the LICENSE.txt file for details. +require 'rackstash/encoders/helpers/message' + module Rackstash module Encoders # The Message encoder only returns the message of log event. All other @@ -13,10 +15,14 @@ module Rackstash # required, mostly during development where debug logs are directly consumed # by humans class Message + include Rackstash::Encoders::Helpers::Message + # @param event [Hash] a log event as produced by the {Flow} # @return [String] the `"message"` field of the event. Trailing whitespace # will be removed. def encode(event) + normalize_message(event) + event[FIELD_MESSAGE].rstrip end end diff --git a/lib/rackstash/flow.rb b/lib/rackstash/flow.rb index a2478e0..6526f39 100644 --- a/lib/rackstash/flow.rb +++ b/lib/rackstash/flow.rb @@ -200,15 +200,10 @@ module Rackstash # 1. At first, we filter the event with the defined filters in their given # order. If any of the filters returns `false`, the writing will be # aborted. No further filters will be applied and the event will not be - # written to the adapter. - # 2. After the filters, we normalize the `event["message"]` field. While the - # filters still had access to the array of {Message} objects for - # filtering, we now concatenate the raw messages as a single string to - # get the final event. The `event["message"]` field now contains a single - # `String` object. - # 3. We encode the event to a format suitable for the adapter using the + # written to the adapter. See {FilterChain#call} for details. + # 2. We encode the event to a format suitable for the adapter using the # configured {#encoder}. - # 4. Finally, the encoded event will be passed to the {#adapter} to be send + # 3. Finally, the encoded event will be passed to the {#adapter} to be send # to the actual log target, e.g. a file or an external log receiver. # # @api private @@ -221,17 +216,6 @@ module Rackstash # Silently abort writing if any filter (and thus the while filter chain) # returns `false`. return false unless @filter_chain.call(event) - - event[FIELD_MESSAGE] = - case event[FIELD_MESSAGE] - when Array - event[FIELD_MESSAGE].map!(&:to_s).join - when nil - '' - else - event[FIELD_MESSAGE].to_s - end - @adapter.write @encoder.encode(event) true end diff --git a/spec/rackstash/encoders/helpers/message_spec.rb b/spec/rackstash/encoders/helpers/message_spec.rb new file mode 100644 index 0000000..e525b88 --- /dev/null +++ b/spec/rackstash/encoders/helpers/message_spec.rb @@ -0,0 +1,35 @@ +# 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/encoders/helpers/message' + +describe Rackstash::Encoders::Helpers::Message do + let(:helper) { Object.new.extend(described_class) } + let(:event) { {} } + + describe '#normalize_message' do + it 'concatenates the message array' do + event['message'] = ["a\n", "b\n"] + + expect(helper.normalize_message(event)).to eql 'message' => "a\nb\n" + end + + it 'sets message to an empty string if not present' do + event['message'] = nil + expect(helper.normalize_message(event)).to eql 'message' => '' + end + + it 'enforces to_s on other messages' do + foo = String.new('foo') + event['message'] = foo + + expect(foo).to receive(:to_s).and_call_original + expect(helper.normalize_message(event)).to eql 'message' => 'foo' + end + end +end diff --git a/spec/rackstash/encoders/json_spec.rb b/spec/rackstash/encoders/json_spec.rb index b41c642..6c6ab33 100644 --- a/spec/rackstash/encoders/json_spec.rb +++ b/spec/rackstash/encoders/json_spec.rb @@ -13,8 +13,8 @@ describe Rackstash::Encoders::JSON do describe '#encode' do it 'formats the passed event hash as a JSON string' do - event = { 'hello' => 'world', 'message' => 'hello' } - expect(encoder.encode(event)).to eql '{"hello":"world","message":"hello"}' + event = { 'hello' => 'world', 'message' => ["hello\n", "world"] } + expect(encoder.encode(event)).to eql '{"hello":"world","message":"hello\nworld"}' end it 'formats newlines as \n' do diff --git a/spec/rackstash/encoders/logstash_spec.rb b/spec/rackstash/encoders/logstash_spec.rb index 28eebe4..cf09eb5 100644 --- a/spec/rackstash/encoders/logstash_spec.rb +++ b/spec/rackstash/encoders/logstash_spec.rb @@ -13,8 +13,8 @@ describe Rackstash::Encoders::Logstash do describe '#encode' do it 'formats the passed event hash as a JSON string and includes @version' do - event = { 'hello' => 'world', 'message' => 'hello' } - expect(encoder.encode(event)).to eql '{"hello":"world","message":"hello","@version":"1"}' + event = { 'hello' => 'world', 'message' => ["hello\n", "world"] } + expect(encoder.encode(event)).to eql '{"hello":"world","message":"hello\nworld","@version":"1"}' end end end diff --git a/spec/rackstash/encoders/message_spec.rb b/spec/rackstash/encoders/message_spec.rb index c67a473..895109d 100644 --- a/spec/rackstash/encoders/message_spec.rb +++ b/spec/rackstash/encoders/message_spec.rb @@ -13,8 +13,8 @@ describe Rackstash::Encoders::Message do describe '#encode' do it 'gets the message from the event hash' do - event = { 'hello' => 'world', 'message' => 'hello' } - expect(encoder.encode(event)).to eql 'hello' + event = { 'hello' => 'world', 'message' => ["hello\n", "world"] } + expect(encoder.encode(event)).to eql "hello\nworld" end it 'rstrips the message' do diff --git a/spec/rackstash/flow_spec.rb b/spec/rackstash/flow_spec.rb index 9c07486..836f3f7 100644 --- a/spec/rackstash/flow_spec.rb +++ b/spec/rackstash/flow_spec.rb @@ -277,30 +277,6 @@ describe Rackstash::Flow do flow.write!(event) end - it 'concatenates message array before encoding' do - event['message'] = ["a\n", "b\n"] - - expect(flow.encoder).to receive(:encode).with('message' => "a\nb\n") - flow.write!(event) - end - - it 'sets message to an emoty string if deleted' do - event['message'] = nil - - expect(flow.encoder).to receive(:encode).with('message' => '') - flow.write!(event) - end - - it 'enforces to_s on other messages' do - foo = String.new('foo') - event['message'] = foo - - expect(foo).to receive(:to_s).and_call_original - expect(flow.encoder).to receive(:encode).with('message' => 'foo') - - flow.write!(event) - end - it 'encodes the event' do expect(flow.encoder).to receive(:encode).with(event) flow.write!(event)