diff --git a/CHANGELOG.md b/CHANGELOG.md index abb3abd..63a5bcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 3.4.1 + - Added backward compatibility of timestamp format to provide consistent fingerprint [#67](https://github.com/logstash-plugins/logstash-filter-fingerprint/pull/67) + ## 3.4.0 - Added support for 128bit murmur variant [#66](https://github.com/logstash-plugins/logstash-filter-fingerprint/pull/66). diff --git a/lib/logstash/filters/fingerprint.rb b/lib/logstash/filters/fingerprint.rb index e027109..3907d0e 100644 --- a/lib/logstash/filters/fingerprint.rb +++ b/lib/logstash/filters/fingerprint.rb @@ -24,6 +24,25 @@ # To generate UUIDs, prefer the <>. class LogStash::Filters::Fingerprint < LogStash::Filters::Base + ## + # Logstash 8+ has variable-length serialization of timestamps + # that do not include subsecond info for whole-second timestamps. + # For backward-compatibility we refine the implementation to use + # our own three-decimal-place formatter for whole-second + # timestamps. + if LOGSTASH_VERSION.split('.').first.to_i >= 8 + module MinimumSerializationLengthTimestamp + THREE_DECIMAL_INSTANT_FORMATTER = java.time.format.DateTimeFormatterBuilder.new.appendInstant(3).toFormatter + refine LogStash::Timestamp do + def to_s + return super unless nsec == 0 + THREE_DECIMAL_INSTANT_FORMATTER.format(to_java.toInstant) + end + end + end + using MinimumSerializationLengthTimestamp + end + INTEGER_MAX_32BIT = (1 << 31) - 1 INTEGER_MIN_32BIT = -(1 << 31) diff --git a/spec/filters/fingerprint_spec.rb b/spec/filters/fingerprint_spec.rb index 48710cc..2ae946b 100644 --- a/spec/filters/fingerprint_spec.rb +++ b/spec/filters/fingerprint_spec.rb @@ -19,6 +19,10 @@ plugin.filter(event) end + def ge_version_8 + LOGSTASH_VERSION.split('.').first.to_i >= 8 + end + context "with a string field" do let(:data) { {"clientip" => "123.123.123.123" } } let(:config) { super().merge("source" => ["clientip" ]) } @@ -273,7 +277,7 @@ end context 'Timestamps' do - epoch_time = Time.at(0).gmtime + let(:epoch_time) { Time.at(0).gmtime } let(:config) { super().merge("source" => ['@timestamp']) } describe 'OpenSSL Fingerprinting' do @@ -297,9 +301,35 @@ let(:fingerprint_method) { "MURMUR3_128" } let(:data) { { "@timestamp" => epoch_time } } it "fingerprints the timestamp correctly" do - expect(fingerprint).to eq("37785b62a8cae473acc315d39b66d86e") + expect(fingerprint).to eq('37785b62a8cae473acc315d39b66d86e') end end + + describe "fractional seconds" do + let(:fingerprint_method) { "MURMUR3" } + let(:data) { { "@timestamp" => epoch_time } } + + describe "millisecond" do + let(:epoch_time) { LogStash::Timestamp.new('2000-01-01T05:00:00.12Z') } + it "fingerprints the timestamp correctly" do + expect(fingerprint).to eq(4263087275) + end + end + + describe "microsecond" do + let(:epoch_time) { LogStash::Timestamp.new('2000-01-01T05:00:00.123456Z') } + it "fingerprints the timestamp correctly" do + expect(fingerprint).to eq(4188855160) + end + end if ge_version_8 + + describe "nanosecond" do + let(:epoch_time) { LogStash::Timestamp.new('2000-01-01T05:00:00.123456789Z') } + it "fingerprints the timestamp correctly" do + expect(fingerprint).to eq(3520111535) + end + end if ge_version_8 + end end describe "post fingerprint execution triggers" do