logstash-input-log4j 3.0.3-java → 3.0.4-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 +4 -4
- data/CHANGELOG.md +3 -0
- data/lib/logstash/inputs/log4j.rb +40 -5
- data/logstash-input-log4j.gemspec +2 -1
- data/spec/fixtures/log4j.payload +0 -0
- data/spec/inputs/log4j_spec.rb +120 -7
- metadata +28 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8332669c67e8a314ad2bdbeacb8d73ce517f6164
|
4
|
+
data.tar.gz: b08d5eecf241d5205f521ed95cd3aad87b96681d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1228c327c49ebc80c2d1a3551cff839750ec6ed6c1785f766fa36378fff8287eb4642e044cf51eebaf8a01d22a1c3d130815be7e4ec8b7202e5703f33d212ad
|
7
|
+
data.tar.gz: 481d227075c191bf9c4f0d05da2cf6f319c99338cab7251c846f6a3d28a325d90e0d8b729c7cdc5412e1f7dc1f5e4c1378b2c1ddd1e528ed530cddeb21c928e6
|
data/CHANGELOG.md
CHANGED
@@ -41,6 +41,10 @@ class LogStash::Inputs::Log4j < LogStash::Inputs::Base
|
|
41
41
|
# When mode is `client`, the port to connect to.
|
42
42
|
config :port, :validate => :number, :default => 4560
|
43
43
|
|
44
|
+
# Proxy protocol support, only v1 is supported at this time
|
45
|
+
# http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt
|
46
|
+
config :proxy_protocol, :validate => :boolean, :default => false
|
47
|
+
|
44
48
|
# Read timeout in seconds. If a particular TCP connection is
|
45
49
|
# idle for more than this timeout period, we will assume
|
46
50
|
# it is dead and close it.
|
@@ -57,8 +61,6 @@ class LogStash::Inputs::Log4j < LogStash::Inputs::Base
|
|
57
61
|
|
58
62
|
public
|
59
63
|
def register
|
60
|
-
require "java"
|
61
|
-
require "jruby/serialization"
|
62
64
|
|
63
65
|
begin
|
64
66
|
Java::OrgApacheLog4jSpi.const_get("LoggingEvent")
|
@@ -100,10 +102,25 @@ class LogStash::Inputs::Log4j < LogStash::Inputs::Base
|
|
100
102
|
private
|
101
103
|
def handle_socket(socket, output_queue)
|
102
104
|
begin
|
105
|
+
peer = socket.peer
|
106
|
+
if @proxy_protocol
|
107
|
+
pp_hdr = socket.readline
|
108
|
+
pp_info = pp_hdr.split(/\s/)
|
109
|
+
|
110
|
+
# PROXY proto clientip proxyip clientport proxyport
|
111
|
+
if pp_info[0] != "PROXY"
|
112
|
+
@logger.error("invalid proxy protocol header label", :hdr => pp_hdr)
|
113
|
+
return
|
114
|
+
else
|
115
|
+
# would be nice to log the proxy host and port data as well, but minimizing changes
|
116
|
+
peer = pp_info[2]
|
117
|
+
end
|
118
|
+
end
|
103
119
|
ois = socket_to_inputstream(socket)
|
120
|
+
|
104
121
|
while !stop?
|
105
122
|
event = create_event(ois.readObject)
|
106
|
-
event.set("host",
|
123
|
+
event.set("host", peer)
|
107
124
|
decorate(event)
|
108
125
|
output_queue << event
|
109
126
|
end # loop do
|
@@ -124,8 +141,7 @@ class LogStash::Inputs::Log4j < LogStash::Inputs::Base
|
|
124
141
|
|
125
142
|
private
|
126
143
|
def socket_to_inputstream(socket)
|
127
|
-
|
128
|
-
JRubyObjectInputStream.new(java.io.BufferedInputStream.new(socket.to_inputstream))
|
144
|
+
Log4jInputStream.new(java.io.BufferedInputStream.new(socket.to_inputstream))
|
129
145
|
end
|
130
146
|
|
131
147
|
private
|
@@ -178,4 +194,23 @@ class LogStash::Inputs::Log4j < LogStash::Inputs::Base
|
|
178
194
|
def add_socket_mixin(socket)
|
179
195
|
socket.instance_eval { class << self; include ::LogStash::Util::SocketPeer end }
|
180
196
|
end
|
197
|
+
|
198
|
+
class Log4jInputStream < java.io.ObjectInputStream
|
199
|
+
ALLOWED_CLASSES = ["org.apache.log4j.spi.LoggingEvent"]
|
200
|
+
|
201
|
+
def initialize(*args)
|
202
|
+
super
|
203
|
+
@class_loader = org.jruby.Ruby.getGlobalRuntime.getJRubyClassLoader
|
204
|
+
end
|
205
|
+
|
206
|
+
def safety_check(name)
|
207
|
+
raise java.io.InvalidObjectException.new("Object type #{name} is not allowed.") if !ALLOWED_CLASSES.include?(name)
|
208
|
+
end
|
209
|
+
|
210
|
+
def resolveClass(desc)
|
211
|
+
name = desc.getName
|
212
|
+
safety_check(name)
|
213
|
+
java.lang.Class.forName(name, false, @class_loader)
|
214
|
+
end
|
215
|
+
end
|
181
216
|
end # class LogStash::Inputs::Log4j
|
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
|
3
3
|
s.name = 'logstash-input-log4j'
|
4
|
-
s.version = '3.0.
|
4
|
+
s.version = '3.0.4'
|
5
5
|
s.licenses = ['Apache License (2.0)']
|
6
6
|
s.summary = "Read events over a TCP socket from a Log4j SocketAppender"
|
7
7
|
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"
|
@@ -26,5 +26,6 @@ Gem::Specification.new do |s|
|
|
26
26
|
s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
|
27
27
|
|
28
28
|
s.add_development_dependency 'logstash-devutils'
|
29
|
+
s.add_development_dependency 'flores'
|
29
30
|
end
|
30
31
|
|
Binary file
|
data/spec/inputs/log4j_spec.rb
CHANGED
@@ -1,16 +1,23 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require "logstash/devutils/rspec/spec_helper"
|
3
|
+
require "socket"
|
3
4
|
require "logstash/inputs/log4j"
|
4
5
|
require "logstash/plugin"
|
6
|
+
require "stud/try"
|
7
|
+
require "stud/task"
|
8
|
+
require 'timeout'
|
9
|
+
require "flores/random"
|
10
|
+
|
11
|
+
# Uncomment to enable higher level logging if needed during testing.
|
12
|
+
#LogStash::Logging::Logger::configure_logging("DEBUG")
|
5
13
|
|
6
14
|
describe LogStash::Inputs::Log4j do
|
7
15
|
|
8
16
|
it "should register" do
|
9
|
-
|
10
|
-
|
17
|
+
plugin = LogStash::Plugin.lookup("input", "log4j").new("mode" => "client")
|
11
18
|
|
12
19
|
# register will try to load jars and raise if it cannot find jars or if org.apache.log4j.spi.LoggingEvent class is not present
|
13
|
-
expect {
|
20
|
+
expect {plugin.register}.to_not raise_error
|
14
21
|
end
|
15
22
|
|
16
23
|
context "when interrupting the plugin in server mode" do
|
@@ -35,7 +42,7 @@ describe LogStash::Inputs::Log4j do
|
|
35
42
|
end
|
36
43
|
|
37
44
|
context "reading general information from a org.apache.log4j.spi.LoggingEvent" do
|
38
|
-
let (:
|
45
|
+
let (:plugin) { LogStash::Plugin.lookup("input", "log4j").new("mode" => "client") }
|
39
46
|
let (:log_obj) {
|
40
47
|
org.apache.log4j.spi.LoggingEvent.new(
|
41
48
|
"org.apache.log4j.Logger",
|
@@ -59,7 +66,7 @@ describe LogStash::Inputs::Log4j do
|
|
59
66
|
}
|
60
67
|
|
61
68
|
it "creates event with general information" do
|
62
|
-
subject =
|
69
|
+
subject = plugin.create_event(log_obj)
|
63
70
|
expect(subject.get("timestamp")).to eq(1426366971)
|
64
71
|
expect(subject.get("path")).to eq("org.apache.log4j.LayoutTest")
|
65
72
|
expect(subject.get("priority")).to eq("INFO")
|
@@ -74,14 +81,120 @@ describe LogStash::Inputs::Log4j do
|
|
74
81
|
end
|
75
82
|
|
76
83
|
it "creates event without stacktrace" do
|
77
|
-
subject =
|
84
|
+
subject = plugin.create_event(log_obj)
|
78
85
|
expect(subject.get("stack_trace")).to be_nil
|
79
86
|
end
|
80
87
|
|
81
88
|
it "creates event with stacktrace" do
|
82
|
-
subject =
|
89
|
+
subject = plugin.create_event(log_obj_with_stacktrace)
|
83
90
|
#checks stack_trace is collected, exact value is too monstruous
|
84
91
|
expect(subject.get("stack_trace")).not_to be_empty
|
85
92
|
end
|
86
93
|
end
|
94
|
+
|
95
|
+
context "integration test" do
|
96
|
+
let(:host) { "127.0.0.1" }
|
97
|
+
let(:port) do
|
98
|
+
socket, address, port = Flores::Random.tcp_listener
|
99
|
+
socket.close
|
100
|
+
port
|
101
|
+
end
|
102
|
+
|
103
|
+
let(:config) do
|
104
|
+
{
|
105
|
+
"host" => host,
|
106
|
+
"port" => port
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
subject { LogStash::Inputs::Log4j.new(config) }
|
111
|
+
|
112
|
+
before do
|
113
|
+
subject.register
|
114
|
+
end
|
115
|
+
|
116
|
+
let(:thread) do
|
117
|
+
Thread.new { subject.run(queue) }
|
118
|
+
end
|
119
|
+
|
120
|
+
let(:queue) do
|
121
|
+
[]
|
122
|
+
end
|
123
|
+
|
124
|
+
let(:client) do
|
125
|
+
Stud.try(5.times) { TCPSocket.new(host, port) }
|
126
|
+
end
|
127
|
+
|
128
|
+
after do
|
129
|
+
subject.do_stop
|
130
|
+
|
131
|
+
10.times do
|
132
|
+
break unless thread.alive?
|
133
|
+
sleep(0.1)
|
134
|
+
end
|
135
|
+
expect(thread).not_to be_alive
|
136
|
+
end
|
137
|
+
|
138
|
+
shared_examples "accept events from the network" do |fixture|
|
139
|
+
before do
|
140
|
+
thread # make the thread run
|
141
|
+
File.open(fixture, "rb") do |payload|
|
142
|
+
IO.copy_stream(payload, client)
|
143
|
+
end
|
144
|
+
client.close
|
145
|
+
|
146
|
+
Stud.try(5.times) do
|
147
|
+
throw StandardError.new("queue was empty, no data?") if queue.empty?
|
148
|
+
end
|
149
|
+
expect(queue.size).to be == 1
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should accept an event from the network" do
|
153
|
+
event = queue.first
|
154
|
+
expect(event.get("message")).to be == "Hello world"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context "default behavior" do
|
159
|
+
include_examples "accept events from the network", "spec/fixtures/log4j.payload"
|
160
|
+
end
|
161
|
+
|
162
|
+
context "with proxy enabled" do
|
163
|
+
let(:config) do
|
164
|
+
{
|
165
|
+
"host" => host,
|
166
|
+
"port" => port,
|
167
|
+
"proxy_protocol" => true
|
168
|
+
}
|
169
|
+
end
|
170
|
+
|
171
|
+
before do
|
172
|
+
client.write("PROXY TCP4 1.2.3.4 5.6.7.8 1234 5678\r\n")
|
173
|
+
end
|
174
|
+
|
175
|
+
include_examples "accept events from the network", "spec/fixtures/log4j.payload" do
|
176
|
+
it "should set proxy_host and proxy_port" do
|
177
|
+
event = queue.first
|
178
|
+
expect(event.get("host")).to be == "1.2.3.4"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
context "for safety" do
|
185
|
+
let(:input) { java.lang.Integer.new(Flores::Random.integer(0..1000)) }
|
186
|
+
let(:baos) { java.io.ByteArrayOutputStream.new }
|
187
|
+
let(:oos) { java.io.ObjectOutputStream.new(baos) }
|
188
|
+
let(:data) {
|
189
|
+
oos.writeObject(input)
|
190
|
+
baos.toByteArray()
|
191
|
+
}
|
192
|
+
|
193
|
+
let(:bais) { java.io.ByteArrayInputStream.new(data) }
|
194
|
+
let(:ois) { LogStash::Inputs::Log4j::Log4jInputStream.new(bais) }
|
195
|
+
|
196
|
+
it "should reject non-log4j objects" do
|
197
|
+
expect { ois.readObject }.to raise_error(java.io.InvalidObjectException)
|
198
|
+
end
|
199
|
+
end
|
87
200
|
end
|
metadata
CHANGED
@@ -1,30 +1,39 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logstash-input-log4j
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.
|
4
|
+
version: 3.0.4
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Elastic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-04-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
+
name: logstash-codec-plain
|
15
|
+
version_requirements: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
14
20
|
requirement: !ruby/object:Gem::Requirement
|
15
21
|
requirements:
|
16
22
|
- - ">="
|
17
23
|
- !ruby/object:Gem::Version
|
18
24
|
version: '0'
|
19
|
-
name: logstash-codec-plain
|
20
25
|
prerelease: false
|
21
26
|
type: :runtime
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: logstash-core-plugin-api
|
22
29
|
version_requirements: !ruby/object:Gem::Requirement
|
23
30
|
requirements:
|
24
31
|
- - ">="
|
25
32
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
27
|
-
-
|
33
|
+
version: '1.60'
|
34
|
+
- - "<="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '2.99'
|
28
37
|
requirement: !ruby/object:Gem::Requirement
|
29
38
|
requirements:
|
30
39
|
- - ">="
|
@@ -33,31 +42,36 @@ dependencies:
|
|
33
42
|
- - "<="
|
34
43
|
- !ruby/object:Gem::Version
|
35
44
|
version: '2.99'
|
36
|
-
name: logstash-core-plugin-api
|
37
45
|
prerelease: false
|
38
46
|
type: :runtime
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: logstash-devutils
|
39
49
|
version_requirements: !ruby/object:Gem::Requirement
|
40
50
|
requirements:
|
41
51
|
- - ">="
|
42
52
|
- !ruby/object:Gem::Version
|
43
|
-
version: '
|
44
|
-
- - "<="
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
version: '2.99'
|
47
|
-
- !ruby/object:Gem::Dependency
|
53
|
+
version: '0'
|
48
54
|
requirement: !ruby/object:Gem::Requirement
|
49
55
|
requirements:
|
50
56
|
- - ">="
|
51
57
|
- !ruby/object:Gem::Version
|
52
58
|
version: '0'
|
53
|
-
name: logstash-devutils
|
54
59
|
prerelease: false
|
55
60
|
type: :development
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: flores
|
56
63
|
version_requirements: !ruby/object:Gem::Requirement
|
57
64
|
requirements:
|
58
65
|
- - ">="
|
59
66
|
- !ruby/object:Gem::Version
|
60
67
|
version: '0'
|
68
|
+
requirement: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
prerelease: false
|
74
|
+
type: :development
|
61
75
|
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
|
62
76
|
email: info@elastic.co
|
63
77
|
executables: []
|
@@ -73,6 +87,7 @@ files:
|
|
73
87
|
- lib/logstash-input-log4j_jars.rb
|
74
88
|
- lib/logstash/inputs/log4j.rb
|
75
89
|
- logstash-input-log4j.gemspec
|
90
|
+
- spec/fixtures/log4j.payload
|
76
91
|
- spec/inputs/log4j_spec.rb
|
77
92
|
- vendor/jar-dependencies/runtime-jars/log4j-1.2.17.jar
|
78
93
|
homepage: http://www.elastic.co/guide/en/logstash/current/index.html
|
@@ -102,4 +117,5 @@ signing_key:
|
|
102
117
|
specification_version: 4
|
103
118
|
summary: Read events over a TCP socket from a Log4j SocketAppender
|
104
119
|
test_files:
|
120
|
+
- spec/fixtures/log4j.payload
|
105
121
|
- spec/inputs/log4j_spec.rb
|