logstash-input-beats 3.1.0.beta1-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|