logstash-input-beats 3.1.0.beta1-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +131 -0
- data/CONTRIBUTORS +17 -0
- data/Gemfile +4 -0
- data/LICENSE +14 -0
- data/NOTICE.TXT +5 -0
- data/PROTOCOL.md +127 -0
- data/README.md +98 -0
- data/VERSION +1 -0
- data/lib/logstash-input-beats_jars.rb +17 -0
- data/lib/logstash/inputs/beats.rb +184 -0
- data/lib/logstash/inputs/beats/codec_callback_listener.rb +26 -0
- data/lib/logstash/inputs/beats/decoded_event_transform.rb +34 -0
- data/lib/logstash/inputs/beats/event_transform_common.rb +48 -0
- data/lib/logstash/inputs/beats/message_listener.rb +96 -0
- data/lib/logstash/inputs/beats/raw_event_transform.rb +18 -0
- data/lib/logstash/inputs/beats/tls.rb +40 -0
- data/lib/tasks/build.rake +15 -0
- data/lib/tasks/test.rake +65 -0
- data/logstash-input-beats.gemspec +41 -0
- data/spec/inputs/beats/codec_callback_listener_spec.rb +33 -0
- data/spec/inputs/beats/decoded_event_transform_spec.rb +74 -0
- data/spec/inputs/beats/event_transform_common_spec.rb +11 -0
- data/spec/inputs/beats/message_listener_spec.rb +108 -0
- data/spec/inputs/beats/raw_event_transform_spec.rb +26 -0
- data/spec/inputs/beats/tls_spec.rb +39 -0
- data/spec/inputs/beats_spec.rb +99 -0
- data/spec/integration/filebeat_spec.rb +234 -0
- data/spec/integration/logstash_forwarder_spec.rb +104 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/support/client_process_helpers.rb +28 -0
- data/spec/support/file_helpers.rb +61 -0
- data/spec/support/flores_extensions.rb +82 -0
- data/spec/support/integration_shared_context.rb +73 -0
- data/spec/support/logstash_test.rb +66 -0
- data/spec/support/shared_examples.rb +56 -0
- data/vendor/jar-dependencies/com/fasterxml/jackson/core/jackson-annotations/2.7.5/jackson-annotations-2.7.5.jar +0 -0
- data/vendor/jar-dependencies/com/fasterxml/jackson/core/jackson-core/2.7.5/jackson-core-2.7.5.jar +0 -0
- data/vendor/jar-dependencies/com/fasterxml/jackson/core/jackson-databind/2.7.5/jackson-databind-2.7.5.jar +0 -0
- data/vendor/jar-dependencies/com/fasterxml/jackson/module/jackson-module-afterburner/2.7.5/jackson-module-afterburner-2.7.5.jar +0 -0
- data/vendor/jar-dependencies/io/netty/netty-all/4.1.1.Final/netty-all-4.1.1.Final.jar +0 -0
- data/vendor/jar-dependencies/io/netty/netty-tcnative-boringssl-static/1.1.33.Fork17/netty-tcnative-boringssl-static-1.1.33.Fork17.jar +0 -0
- data/vendor/jar-dependencies/org/apache/logging/log4j/log4j-1.2-api/2.6.1/log4j-1.2-api-2.6.1.jar +0 -0
- data/vendor/jar-dependencies/org/apache/logging/log4j/log4j-api/2.6.1/log4j-api-2.6.1.jar +0 -0
- data/vendor/jar-dependencies/org/apache/logging/log4j/log4j-core/2.6.1/log4j-core-2.6.1.jar +0 -0
- data/vendor/jar-dependencies/org/apache/logging/log4j/log4j-slf4j-impl/2.6.1/log4j-slf4j-impl-2.6.1.jar +0 -0
- data/vendor/jar-dependencies/org/bouncycastle/bcpkix-jdk15on/1.54/bcpkix-jdk15on-1.54.jar +0 -0
- data/vendor/jar-dependencies/org/bouncycastle/bcprov-jdk15on/1.54/bcprov-jdk15on-1.54.jar +0 -0
- data/vendor/jar-dependencies/org/javassist/javassist/3.20.0-GA/javassist-3.20.0-GA.jar +0 -0
- data/vendor/jar-dependencies/org/logstash/beats/logstash-input-beats/3.1.0.beta1/logstash-input-beats-3.1.0.beta1.jar +0 -0
- metadata +313 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/inputs/beats"
|
3
|
+
|
4
|
+
module LogStash module Inputs class Beats
|
5
|
+
# Use the new callback based approch instead of using blocks
|
6
|
+
# so we can retain some context of the execution, and make it easier to test
|
7
|
+
class CodecCallbackListener
|
8
|
+
attr_accessor :data
|
9
|
+
# The path acts as the `stream_identity`,
|
10
|
+
# usefull when the clients is reading multiples files
|
11
|
+
attr_accessor :path
|
12
|
+
|
13
|
+
def initialize(data, hash, path, transformer, queue)
|
14
|
+
@data = data
|
15
|
+
@hash = hash
|
16
|
+
@path = path
|
17
|
+
@queue = queue
|
18
|
+
@transformer = transformer
|
19
|
+
end
|
20
|
+
|
21
|
+
def process_event(event)
|
22
|
+
@transformer.transform(event, @hash)
|
23
|
+
@queue << event
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end; end; end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/inputs/beats/event_transform_common"
|
3
|
+
module LogStash module Inputs class Beats
|
4
|
+
# Take the extracted content from the codec, merged with the other data coming
|
5
|
+
# from beats, apply the configured tags, normalize the host and try to coerce
|
6
|
+
# the timestamp if it was provided in the hash.
|
7
|
+
class DecodedEventTransform < EventTransformCommon
|
8
|
+
def transform(event, hash)
|
9
|
+
ts = coerce_ts(hash.delete("@timestamp"))
|
10
|
+
|
11
|
+
event.set("@timestamp", ts) unless ts.nil?
|
12
|
+
hash.each { |k, v| event.set(k, v) }
|
13
|
+
super(event)
|
14
|
+
event.tag("beats_input_codec_#{codec_name}_applied")
|
15
|
+
event
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def coerce_ts(ts)
|
20
|
+
return nil if ts.nil?
|
21
|
+
timestamp = LogStash::Timestamp.coerce(ts)
|
22
|
+
|
23
|
+
return timestamp if timestamp
|
24
|
+
|
25
|
+
@logger.warn("Unrecognized @timestamp value, setting current time to @timestamp",
|
26
|
+
:value => ts.inspect)
|
27
|
+
return nil
|
28
|
+
rescue LogStash::TimestampParserError => e
|
29
|
+
@logger.warn("Error parsing @timestamp string, setting current time to @timestamp",
|
30
|
+
:value => ts.inspect, :exception => e.message)
|
31
|
+
return nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end; end; end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module LogStash module Inputs class Beats
|
3
|
+
# Base Transform class, expose the plugin decorate method,
|
4
|
+
# apply the tags and make sure we copy the beat hostname into `host`
|
5
|
+
# for backward compatibility.
|
6
|
+
class EventTransformCommon
|
7
|
+
def initialize(input)
|
8
|
+
@input = input
|
9
|
+
@logger = input.logger
|
10
|
+
end
|
11
|
+
|
12
|
+
# Copies the beat.hostname field into the host field unless
|
13
|
+
# the host field is already defined
|
14
|
+
def copy_beat_hostname(event)
|
15
|
+
host = event.get("[beat][hostname]")
|
16
|
+
|
17
|
+
if host && event.get("host").nil?
|
18
|
+
event.set("host", host)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# This break the `#decorate` method visibility of the plugin base
|
23
|
+
# class, the method is protected and we cannot access it, but well ruby
|
24
|
+
# can let you do all the wrong thing.
|
25
|
+
#
|
26
|
+
# I think the correct behavior would be to allow the plugin to return a
|
27
|
+
# `Decorator` object that we can pass to other objects, since only the
|
28
|
+
# plugin know the data used to decorate. This would allow a more component
|
29
|
+
# based workflow.
|
30
|
+
def decorate(event)
|
31
|
+
@input.send(:decorate, event)
|
32
|
+
end
|
33
|
+
|
34
|
+
def codec_name
|
35
|
+
@codec_name ||= if @input.codec.respond_to?(:base_codec)
|
36
|
+
@input.codec.base_codec.class.config_name
|
37
|
+
else
|
38
|
+
@input.codec.class.config_name
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def transform(event)
|
43
|
+
copy_beat_hostname(event)
|
44
|
+
decorate(event)
|
45
|
+
event
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end; end; end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "thread_safe"
|
3
|
+
require "logstash-input-beats_jars"
|
4
|
+
import "org.logstash.beats.MessageListener"
|
5
|
+
|
6
|
+
module LogStash module Inputs class Beats
|
7
|
+
class MessageListener
|
8
|
+
include org.logstash.beats.IMessageListener
|
9
|
+
|
10
|
+
FILEBEAT_LOG_LINE_FIELD = "message".freeze
|
11
|
+
LSF_LOG_LINE_FIELD = "line".freeze
|
12
|
+
|
13
|
+
ConnectionState = Struct.new(:ctx, :codec)
|
14
|
+
|
15
|
+
attr_reader :logger, :input, :connections_list
|
16
|
+
|
17
|
+
def initialize(queue, input)
|
18
|
+
@connections_list = ThreadSafe::Hash.new
|
19
|
+
@queue = queue
|
20
|
+
@logger = input.logger
|
21
|
+
@input = input
|
22
|
+
|
23
|
+
@nocodec_transformer = RawEventTransform.new(@input)
|
24
|
+
@codec_transformer = DecodedEventTransform.new(@input)
|
25
|
+
end
|
26
|
+
|
27
|
+
def onNewMessage(ctx, message)
|
28
|
+
hash = message.getData()
|
29
|
+
|
30
|
+
target_field = extract_target_field(hash)
|
31
|
+
|
32
|
+
if target_field.nil?
|
33
|
+
event = LogStash::Event.new(hash)
|
34
|
+
@nocodec_transformer.transform(event)
|
35
|
+
@queue << event
|
36
|
+
else
|
37
|
+
codec(ctx).accept(CodecCallbackListener.new(target_field,
|
38
|
+
hash,
|
39
|
+
message.getIdentityStream(),
|
40
|
+
@codec_transformer,
|
41
|
+
@queue))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def onNewConnection(ctx)
|
46
|
+
register_connection(ctx)
|
47
|
+
end
|
48
|
+
|
49
|
+
def onConnectionClose(ctx)
|
50
|
+
unregister_connection(ctx)
|
51
|
+
end
|
52
|
+
|
53
|
+
def onException(ctx)
|
54
|
+
unregister_connection(ctx)
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def codec(ctx)
|
59
|
+
connections_list[ctx].codec
|
60
|
+
end
|
61
|
+
|
62
|
+
def register_connection(ctx)
|
63
|
+
connections_list[ctx] = ConnectionState.new(ctx, input.codec.dup)
|
64
|
+
end
|
65
|
+
|
66
|
+
def unregister_connection(ctx)
|
67
|
+
flush_buffer(ctx)
|
68
|
+
connections_list.delete(ctx)
|
69
|
+
end
|
70
|
+
|
71
|
+
def flush_buffer(ctx)
|
72
|
+
transformer = EventTransformCommon.new(@input)
|
73
|
+
|
74
|
+
codec(ctx).flush do |event|
|
75
|
+
transformer.transform(event)
|
76
|
+
@queue << event
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def extract_target_field(hash)
|
81
|
+
if from_filebeat?(hash)
|
82
|
+
hash.delete(FILEBEAT_LOG_LINE_FIELD).to_s
|
83
|
+
elsif from_logstash_forwarder?(hash)
|
84
|
+
hash.delete(LSF_LOG_LINE_FIELD).to_s
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def from_filebeat?(hash)
|
89
|
+
!hash[FILEBEAT_LOG_LINE_FIELD].nil?
|
90
|
+
end
|
91
|
+
|
92
|
+
def from_logstash_forwarder?(hash)
|
93
|
+
!hash[LSF_LOG_LINE_FIELD].nil?
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end; end; end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/inputs/beats/event_transform_common"
|
3
|
+
module LogStash module Inputs class Beats
|
4
|
+
# Take the the raw output from the library, decorate it with
|
5
|
+
# the configured tags in the plugins and normalize the hostname
|
6
|
+
# for backward compatibility
|
7
|
+
#
|
8
|
+
#
|
9
|
+
# @see [Lumberjack::Beats::Parser]
|
10
|
+
#
|
11
|
+
class RawEventTransform < EventTransformCommon
|
12
|
+
def transform(event)
|
13
|
+
super(event)
|
14
|
+
event.tag("beats_input_raw_event")
|
15
|
+
event
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end; end;end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module LogStash module Inputs class Beats
|
3
|
+
class TLS
|
4
|
+
class TLSOption
|
5
|
+
include Comparable
|
6
|
+
|
7
|
+
attr_reader :name, :version
|
8
|
+
def initialize(name, version)
|
9
|
+
@name = name
|
10
|
+
@version = version
|
11
|
+
end
|
12
|
+
|
13
|
+
def <=>(other)
|
14
|
+
version <=> other.version
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
TLS_PROTOCOL_OPTIONS = [
|
19
|
+
TLSOption.new("TLSv1", 1),
|
20
|
+
TLSOption.new("TLSv1.1", 1.1),
|
21
|
+
TLSOption.new("TLSv1.2", 1.2)
|
22
|
+
]
|
23
|
+
|
24
|
+
def self.min
|
25
|
+
TLS_PROTOCOL_OPTIONS.min
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.max
|
29
|
+
TLS_PROTOCOL_OPTIONS.max
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.get_supported(versions)
|
33
|
+
if versions.is_a?(Range)
|
34
|
+
TLS_PROTOCOL_OPTIONS.select { |tls| versions.cover?(tls.version) }
|
35
|
+
else
|
36
|
+
TLS_PROTOCOL_OPTIONS.select { |tls| versions == tls.version }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end; end; end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "jars/installer"
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
task :vendor do
|
6
|
+
system("./gradlew vendor")
|
7
|
+
version = File.read("VERSION").strip
|
8
|
+
end
|
9
|
+
|
10
|
+
desc "clean"
|
11
|
+
task :clean do
|
12
|
+
["build", "vendor/jar-dependencies", "Gemfile.lock"].each do |p|
|
13
|
+
FileUtils.rm_rf(p)
|
14
|
+
end
|
15
|
+
end
|
data/lib/tasks/test.rake
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
OS_PLATFORM = RbConfig::CONFIG["host_os"]
|
3
|
+
VENDOR_PATH = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "vendor"))
|
4
|
+
|
5
|
+
if OS_PLATFORM == "linux"
|
6
|
+
FILEBEAT_URL = "https://beats-nightlies.s3.amazonaws.com/filebeat/filebeat-5.0.0-alpha4-SNAPSHOT-linux-x86_64.tar.gz"
|
7
|
+
elsif OS_PLATFORM == "darwin"
|
8
|
+
FILEBEAT_URL = "https://beats-nightlies.s3.amazonaws.com/filebeat/filebeat-5.0.0-alpha4-SNAPSHOT-darwin-x86_64.tar.gz"
|
9
|
+
end
|
10
|
+
|
11
|
+
LSF_URL = "https://download.elastic.co/logstash-forwarder/binaries/logstash-forwarder_#{OS_PLATFORM}_amd64"
|
12
|
+
|
13
|
+
require "fileutils"
|
14
|
+
@files=[]
|
15
|
+
|
16
|
+
task :default do
|
17
|
+
system("rake -T")
|
18
|
+
end
|
19
|
+
|
20
|
+
require "logstash/devutils/rake"
|
21
|
+
|
22
|
+
namespace :test do
|
23
|
+
namespace :integration do
|
24
|
+
task :setup do
|
25
|
+
Rake::Task["test:integration:setup:filebeat"].invoke
|
26
|
+
Rake::Task["test:integration:setup:lsf"].invoke
|
27
|
+
end
|
28
|
+
|
29
|
+
namespace :setup do
|
30
|
+
desc "Download lastest stable version of Logstash-forwarder"
|
31
|
+
task :lsf do
|
32
|
+
destination = File.join(VENDOR_PATH, "logstash-forwarder")
|
33
|
+
FileUtils.rm_rf(destination)
|
34
|
+
FileUtils.mkdir_p(destination)
|
35
|
+
download_destination = File.join(destination, "logstash-forwarder")
|
36
|
+
puts "Logstash-forwarder: downloading from #{LSF_URL} to #{download_destination}"
|
37
|
+
download(LSF_URL, download_destination)
|
38
|
+
File.chmod(0755, download_destination)
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "Download nigthly filebeat for integration testing"
|
42
|
+
task :filebeat do
|
43
|
+
FileUtils.mkdir_p(VENDOR_PATH)
|
44
|
+
download_destination = File.join(VENDOR_PATH, "filebeat.tar.gz")
|
45
|
+
destination = File.join(VENDOR_PATH, "filebeat")
|
46
|
+
FileUtils.rm_rf(download_destination)
|
47
|
+
FileUtils.rm_rf(destination)
|
48
|
+
FileUtils.rm_rf(File.join(VENDOR_PATH, "filebeat.tar"))
|
49
|
+
puts "Filebeat: downloading from #{FILEBEAT_URL} to #{download_destination}"
|
50
|
+
download(FILEBEAT_URL, download_destination)
|
51
|
+
|
52
|
+
untar_all(download_destination, File.join(VENDOR_PATH, "filebeat")) { |e| e }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Uncompress all the file from the archive this only work with
|
59
|
+
# one level directory structure and its fine for LSF and filebeat packaging.
|
60
|
+
def untar_all(file, destination)
|
61
|
+
untar(file) do |entry|
|
62
|
+
out = entry.full_name.split("/").last
|
63
|
+
File.join(destination, out)
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
BEATS_VERSION = File.read("VERSION").strip unless defined?(BEATS_VERSION)
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "logstash-input-beats"
|
5
|
+
s.version = BEATS_VERSION
|
6
|
+
s.licenses = ["Apache License (2.0)"]
|
7
|
+
s.summary = "Receive events using the lumberjack protocol."
|
8
|
+
s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
|
9
|
+
s.authors = ["Elastic"]
|
10
|
+
s.email = "info@elastic.co"
|
11
|
+
s.homepage = "http://www.elastic.co/guide/en/logstash/current/index.html"
|
12
|
+
s.require_paths = ["lib", "vendor/jar-dependencies"]
|
13
|
+
|
14
|
+
# Files
|
15
|
+
s.files = Dir["lib/**/*","spec/**/*","*.gemspec","*.md","CONTRIBUTORS","Gemfile","LICENSE","NOTICE.TXT", "vendor/jar-dependencies/**/*.jar", "vendor/jar-dependencies/**/*.rb", "VERSION"]
|
16
|
+
# Tests
|
17
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
18
|
+
|
19
|
+
# Special flag to let us know this is actually a logstash plugin
|
20
|
+
s.metadata = { "logstash_plugin" => "true", "logstash_group" => "input" }
|
21
|
+
|
22
|
+
# Gem dependencies
|
23
|
+
s.add_runtime_dependency "logstash-core-plugin-api", "~> 2.0"
|
24
|
+
|
25
|
+
s.add_runtime_dependency "logstash-codec-plain"
|
26
|
+
s.add_runtime_dependency "concurrent-ruby", [ ">= 0.9.2", "<= 1.0.0" ]
|
27
|
+
s.add_runtime_dependency "thread_safe", "~> 0.3.5"
|
28
|
+
s.add_runtime_dependency "logstash-codec-multiline", "~> 3.0"
|
29
|
+
s.add_runtime_dependency 'jar-dependencies', '~> 0.3.4'
|
30
|
+
|
31
|
+
s.add_development_dependency "flores", "~>0.0.6"
|
32
|
+
s.add_development_dependency "rspec"
|
33
|
+
s.add_development_dependency "stud"
|
34
|
+
s.add_development_dependency "pry"
|
35
|
+
s.add_development_dependency "rspec-wait"
|
36
|
+
s.add_development_dependency "logstash-devutils"
|
37
|
+
s.add_development_dependency "logstash-codec-json"
|
38
|
+
s.add_development_dependency "childprocess" # To make filebeat/LSF integration test easier to write.
|
39
|
+
|
40
|
+
s.platform = 'java'
|
41
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/inputs/beats"
|
3
|
+
require "logstash/event"
|
4
|
+
require "spec_helper"
|
5
|
+
require "thread"
|
6
|
+
|
7
|
+
describe LogStash::Inputs::Beats::CodecCallbackListener do
|
8
|
+
let(:data) { "Hello world" }
|
9
|
+
let(:map) do
|
10
|
+
{
|
11
|
+
"beat" => { "hostname" => "newhost" }
|
12
|
+
}
|
13
|
+
end
|
14
|
+
let(:path) { "/var/log/message" }
|
15
|
+
let(:transformer) { double("codec_transformer") }
|
16
|
+
let(:queue_timeout) { 1 }
|
17
|
+
let(:event) { LogStash::Event.new }
|
18
|
+
let(:queue) { Queue.new }
|
19
|
+
|
20
|
+
before do
|
21
|
+
allow(transformer).to receive(:transform).with(event, map).and_return(event)
|
22
|
+
end
|
23
|
+
|
24
|
+
subject { described_class.new(data, map, path, transformer, queue) }
|
25
|
+
|
26
|
+
it "expose the data" do
|
27
|
+
expect(subject.data).to eq(data)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "expose the path" do
|
31
|
+
expect(subject.path).to eq(path)
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/event"
|
3
|
+
require "logstash/timestamp"
|
4
|
+
require "logstash/inputs/beats"
|
5
|
+
require_relative "../../support/shared_examples"
|
6
|
+
require "spec_helper"
|
7
|
+
|
8
|
+
describe LogStash::Inputs::Beats::DecodedEventTransform do
|
9
|
+
let(:config) do
|
10
|
+
{
|
11
|
+
"port" => 0,
|
12
|
+
"type" => "example",
|
13
|
+
"tags" => "beats"
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:input) do
|
18
|
+
LogStash::Inputs::Beats.new(config).tap do |i|
|
19
|
+
i.register
|
20
|
+
end
|
21
|
+
end
|
22
|
+
let(:event) { LogStash::Event.new }
|
23
|
+
let(:map) do
|
24
|
+
{
|
25
|
+
"@metadata" => { "hello" => "world"},
|
26
|
+
"super" => "mario"
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
subject { described_class.new(input).transform(event, map) }
|
31
|
+
|
32
|
+
include_examples "Common Event Transformation"
|
33
|
+
|
34
|
+
it "tags the event" do
|
35
|
+
expect(subject.get("tags")).to include("beats_input_codec_plain_applied")
|
36
|
+
end
|
37
|
+
|
38
|
+
it "merges the other data from the map to the event" do
|
39
|
+
expect(subject.get("super")).to eq(map["super"])
|
40
|
+
expect(subject.get("@metadata")).to include(map["@metadata"])
|
41
|
+
end
|
42
|
+
|
43
|
+
context "map contains a timestamp" do
|
44
|
+
context "when its valid" do
|
45
|
+
let(:timestamp) { Time.now }
|
46
|
+
let(:map) { super.merge({"@timestamp" => timestamp }) }
|
47
|
+
|
48
|
+
it "uses as the event timestamp" do
|
49
|
+
expect(subject.get("@timestamp")).to eq(LogStash::Timestamp.coerce(timestamp))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "when its not valid" do
|
54
|
+
let(:map) { super.merge({"@timestamp" => "invalid" }) }
|
55
|
+
|
56
|
+
it "fallback the current time" do
|
57
|
+
expect(subject.get("@timestamp")).to be_kind_of(LogStash::Timestamp)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "when the map doesn't provide a timestamp" do
|
63
|
+
it "fallback the current time" do
|
64
|
+
expect(subject.get("@timestamp")).to be_kind_of(LogStash::Timestamp)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "when the codec is a base_codec wrapper" do
|
69
|
+
before { config.update("codec" => BeatsInputTest::DummyCodec.new) }
|
70
|
+
it "gets the codec config name from the base codec" do
|
71
|
+
expect(subject.get("tags")).to include("beats_input_codec_dummy_applied")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|