logstash-input-log4j 1.0.0-java → 2.0.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2 -0
- data/README.md +4 -4
- data/lib/logstash/inputs/log4j.rb +62 -35
- data/logstash-input-log4j.gemspec +3 -3
- data/spec/inputs/log4j_spec.rb +73 -2
- metadata +18 -26
- data/.gitignore +0 -3
- data/Rakefile +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 774f1acdc7c060981ab35fe6c00004b554363589
|
4
|
+
data.tar.gz: 9b7193a589bac96a74407fe3f89acba434dbe103
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d3f400870d90165e36b6ae6ae52182ec0bffbaff788ede37ae2ab0331c68ff8b2c1a0a5dd9264818e4ea80961df25b44de149ff047b6bb94e635e974791643a
|
7
|
+
data.tar.gz: 14871fb9b6d28ff05e85b596be48669b6db190da5d1af8c3687bdb1668d159708f39cc053c5c8cb000f8f07704f8beaa82c3bb06adc03ccc63f456dd2c851483
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# Logstash Plugin
|
2
2
|
|
3
|
-
This is a plugin for [Logstash](https://github.com/
|
3
|
+
This is a plugin for [Logstash](https://github.com/elastic/logstash).
|
4
4
|
|
5
5
|
It is fully free and fully open source. The license is Apache 2.0, meaning you are pretty much free to use it however you want in whatever way.
|
6
6
|
|
7
7
|
## Documentation
|
8
8
|
|
9
|
-
Logstash provides infrastructure to automatically generate documentation for this plugin. We use the asciidoc format to write documentation so any comments in the source code will be first converted into asciidoc and then into html. All plugin documentation are placed under one [central location](http://www.
|
9
|
+
Logstash provides infrastructure to automatically generate documentation for this plugin. We use the asciidoc format to write documentation so any comments in the source code will be first converted into asciidoc and then into html. All plugin documentation are placed under one [central location](http://www.elastic.co/guide/en/logstash/current/).
|
10
10
|
|
11
11
|
- For formatting code or config example, you can use the asciidoc `[source,ruby]` directive
|
12
|
-
- For more asciidoc formatting tips, see the excellent reference here https://github.com/
|
12
|
+
- For more asciidoc formatting tips, see the excellent reference here https://github.com/elastic/docs#asciidoc-guide
|
13
13
|
|
14
14
|
## Need Help?
|
15
15
|
|
@@ -83,4 +83,4 @@ Programming is not a required skill. Whatever you've seen about open source and
|
|
83
83
|
|
84
84
|
It is more important to the community that you are able to contribute.
|
85
85
|
|
86
|
-
For more information about contributing, see the [CONTRIBUTING](https://github.com/
|
86
|
+
For more information about contributing, see the [CONTRIBUTING](https://github.com/elastic/logstash/blob/master/CONTRIBUTING.md) file.
|
@@ -14,6 +14,21 @@ require 'logstash-input-log4j_jars'
|
|
14
14
|
# depending on `mode`. Depending on which `mode` is configured,
|
15
15
|
# you need a matching SocketAppender or a SocketHubAppender
|
16
16
|
# on the remote side.
|
17
|
+
#
|
18
|
+
# One event is created per received log4j LoggingEvent with the following schema:
|
19
|
+
#
|
20
|
+
# * `timestamp` => the number of milliseconds elapsed from 1/1/1970 until logging event was created.
|
21
|
+
# * `path` => the name of the logger
|
22
|
+
# * `priority` => the level of this event
|
23
|
+
# * `logger_name` => the name of the logger
|
24
|
+
# * `thread` => the thread name making the logging request
|
25
|
+
# * `class` => the fully qualified class name of the caller making the logging request.
|
26
|
+
# * `file` => the source file name and line number of the caller making the logging request in a colon-separated format "fileName:lineNumber".
|
27
|
+
# * `method` => the method name of the caller making the logging request.
|
28
|
+
# * `NDC` => the NDC string
|
29
|
+
# * `stack_trace` => the multi-line stack-trace
|
30
|
+
#
|
31
|
+
# Also if the original log4j LoggingEvent contains MDC hash entries, they will be merged in the event as fields.
|
17
32
|
class LogStash::Inputs::Log4j < LogStash::Inputs::Base
|
18
33
|
|
19
34
|
config_name "log4j"
|
@@ -42,7 +57,6 @@ class LogStash::Inputs::Log4j < LogStash::Inputs::Base
|
|
42
57
|
|
43
58
|
public
|
44
59
|
def register
|
45
|
-
# LogStash::Environment.load_elasticsearch_jars!
|
46
60
|
require "java"
|
47
61
|
require "jruby/serialization"
|
48
62
|
|
@@ -59,41 +73,45 @@ class LogStash::Inputs::Log4j < LogStash::Inputs::Base
|
|
59
73
|
@logger.info("Log4j input")
|
60
74
|
end # def register
|
61
75
|
|
76
|
+
public
|
77
|
+
def create_event(log4j_obj)
|
78
|
+
# NOTE: log4j_obj is org.apache.log4j.spi.LoggingEvent
|
79
|
+
event = LogStash::Event.new("message" => log4j_obj.getRenderedMessage)
|
80
|
+
event["timestamp"] = log4j_obj.getTimeStamp
|
81
|
+
event["path"] = log4j_obj.getLoggerName
|
82
|
+
event["priority"] = log4j_obj.getLevel.toString
|
83
|
+
event["logger_name"] = log4j_obj.getLoggerName
|
84
|
+
event["thread"] = log4j_obj.getThreadName
|
85
|
+
event["class"] = log4j_obj.getLocationInformation.getClassName
|
86
|
+
event["file"] = log4j_obj.getLocationInformation.getFileName + ":" + log4j_obj.getLocationInformation.getLineNumber
|
87
|
+
event["method"] = log4j_obj.getLocationInformation.getMethodName
|
88
|
+
event["NDC"] = log4j_obj.getNDC if log4j_obj.getNDC
|
89
|
+
event["stack_trace"] = log4j_obj.getThrowableStrRep.to_a.join("\n") if log4j_obj.getThrowableInformation
|
90
|
+
|
91
|
+
# Add the MDC context properties to event
|
92
|
+
if log4j_obj.getProperties
|
93
|
+
log4j_obj.getPropertyKeySet.each do |key|
|
94
|
+
event[key] = log4j_obj.getProperty(key)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
return event
|
98
|
+
end # def create_event
|
99
|
+
|
62
100
|
private
|
63
101
|
def handle_socket(socket, output_queue)
|
64
102
|
begin
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
# NOTE: log4j_obj is org.apache.log4j.spi.LoggingEvent
|
69
|
-
log4j_obj = ois.readObject
|
70
|
-
event = LogStash::Event.new("message" => log4j_obj.getRenderedMessage)
|
71
|
-
decorate(event)
|
103
|
+
ois = socket_to_inputstream(socket)
|
104
|
+
while !stop?
|
105
|
+
event = create_event(ois.readObject)
|
72
106
|
event["host"] = socket.peer
|
73
|
-
event
|
74
|
-
event["priority"] = log4j_obj.getLevel.toString
|
75
|
-
event["logger_name"] = log4j_obj.getLoggerName
|
76
|
-
event["thread"] = log4j_obj.getThreadName
|
77
|
-
event["class"] = log4j_obj.getLocationInformation.getClassName
|
78
|
-
event["file"] = log4j_obj.getLocationInformation.getFileName + ":" + log4j_obj.getLocationInformation.getLineNumber
|
79
|
-
event["method"] = log4j_obj.getLocationInformation.getMethodName
|
80
|
-
event["NDC"] = log4j_obj.getNDC if log4j_obj.getNDC
|
81
|
-
event["stack_trace"] = log4j_obj.getThrowableStrRep.to_a.join("\n") if log4j_obj.getThrowableInformation
|
82
|
-
|
83
|
-
# Add the MDC context properties to '@fields'
|
84
|
-
if log4j_obj.getProperties
|
85
|
-
log4j_obj.getPropertyKeySet.each do |key|
|
86
|
-
event[key] = log4j_obj.getProperty(key)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
107
|
+
decorate(event)
|
90
108
|
output_queue << event
|
91
109
|
end # loop do
|
92
110
|
rescue => e
|
93
|
-
@logger.debug("Closing connection", :client => socket.peer,
|
111
|
+
@logger.debug? && @logger.debug("Closing connection", :client => socket.peer,
|
94
112
|
:exception => e)
|
95
113
|
rescue Timeout::Error
|
96
|
-
@logger.debug("Closing connection after read timeout",
|
114
|
+
@logger.debug? && @logger.debug("Closing connection after read timeout",
|
97
115
|
:client => socket.peer)
|
98
116
|
end # begin
|
99
117
|
ensure
|
@@ -104,6 +122,12 @@ class LogStash::Inputs::Log4j < LogStash::Inputs::Base
|
|
104
122
|
end # begin
|
105
123
|
end
|
106
124
|
|
125
|
+
private
|
126
|
+
def socket_to_inputstream(socket)
|
127
|
+
# JRubyObjectInputStream uses JRuby class path to find the class to de-serialize to
|
128
|
+
JRubyObjectInputStream.new(java.io.BufferedInputStream.new(socket.to_inputstream))
|
129
|
+
end
|
130
|
+
|
107
131
|
private
|
108
132
|
def server?
|
109
133
|
@mode == "server"
|
@@ -114,26 +138,29 @@ class LogStash::Inputs::Log4j < LogStash::Inputs::Base
|
|
114
138
|
line = socket.readline
|
115
139
|
end # def readline
|
116
140
|
|
141
|
+
public
|
142
|
+
# method used to stop the plugin and unblock
|
143
|
+
# pending blocking operatings like sockets and others.
|
144
|
+
def stop
|
145
|
+
@server_socket.close if @server_socket && !@server_socket.closed?
|
146
|
+
end
|
147
|
+
|
117
148
|
public
|
118
149
|
def run(output_queue)
|
119
150
|
if server?
|
120
|
-
|
121
|
-
# Start a new thread for each connection.
|
151
|
+
while !stop?
|
122
152
|
Thread.start(@server_socket.accept) do |s|
|
123
|
-
# TODO(sissel): put this block in its own method.
|
124
|
-
|
125
|
-
# monkeypatch a 'peer' method onto the socket.
|
126
153
|
s.instance_eval { class << self; include ::LogStash::Util::SocketPeer end }
|
127
|
-
@logger.debug("Accepted connection", :client => s.peer,
|
154
|
+
@logger.debug? && @logger.debug("Accepted connection", :client => s.peer,
|
128
155
|
:server => "#{@host}:#{@port}")
|
129
156
|
handle_socket(s, output_queue)
|
130
157
|
end # Thread.start
|
131
158
|
end # loop
|
132
159
|
else
|
133
|
-
|
160
|
+
while !stop?
|
134
161
|
client_socket = TCPSocket.new(@host, @port)
|
135
162
|
client_socket.instance_eval { class << self; include ::LogStash::Util::SocketPeer end }
|
136
|
-
@logger.debug("Opened connection", :client => "#{client_socket.peer}")
|
163
|
+
@logger.debug? && @logger.debug("Opened connection", :client => "#{client_socket.peer}")
|
137
164
|
handle_socket(client_socket, output_queue)
|
138
165
|
end # loop
|
139
166
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
|
3
3
|
s.name = 'logstash-input-log4j'
|
4
|
-
s.version = '
|
4
|
+
s.version = '2.0.0'
|
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/plugin install gemname. This gem is not a stand-alone program"
|
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.require_paths = ["lib"]
|
12
12
|
|
13
13
|
# Files
|
14
|
-
s.files =
|
14
|
+
s.files = Dir['lib/**/*','spec/**/*','vendor/**/*','*.gemspec','*.md','CONTRIBUTORS','Gemfile','LICENSE','NOTICE.TXT']
|
15
15
|
|
16
16
|
# Tests
|
17
17
|
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
|
|
23
23
|
|
24
24
|
s.add_runtime_dependency 'logstash-codec-plain'
|
25
25
|
|
26
|
-
s.add_runtime_dependency "logstash-core",
|
26
|
+
s.add_runtime_dependency "logstash-core", "~> 2.0.0.snapshot"
|
27
27
|
|
28
28
|
s.add_development_dependency 'logstash-devutils'
|
29
29
|
end
|
data/spec/inputs/log4j_spec.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
2
|
+
require "logstash/devutils/rspec/spec_helper"
|
3
|
+
require "logstash/inputs/log4j"
|
3
4
|
require "logstash/plugin"
|
4
5
|
|
5
|
-
describe
|
6
|
+
describe LogStash::Inputs::Log4j do
|
6
7
|
|
7
8
|
it "should register" do
|
8
9
|
input = LogStash::Plugin.lookup("input", "log4j").new("mode" => "client")
|
@@ -10,4 +11,74 @@ describe "inputs/log4j" do
|
|
10
11
|
# register will try to load jars and raise if it cannot find jars or if org.apache.log4j.spi.LoggingEvent class is not present
|
11
12
|
expect {input.register}.to_not raise_error
|
12
13
|
end
|
14
|
+
|
15
|
+
context "when interrupting the plugin" do
|
16
|
+
|
17
|
+
it_behaves_like "an interruptible input plugin" do
|
18
|
+
let(:config) { { "mode" => "server" } }
|
19
|
+
end
|
20
|
+
|
21
|
+
it_behaves_like "an interruptible input plugin" do
|
22
|
+
let(:config) { { "mode" => "client" } }
|
23
|
+
let(:socket) { double("socket") }
|
24
|
+
let(:ois) { double("ois") }
|
25
|
+
before(:each) do
|
26
|
+
allow(socket).to receive(:peer).and_return("localhost")
|
27
|
+
allow(ois).to receive(:readObject).and_return({})
|
28
|
+
allow(TCPSocket).to receive(:new).and_return(socket)
|
29
|
+
expect(subject).to receive(:socket_to_inputstream).with(socket).and_return(ois)
|
30
|
+
expect(subject).to receive(:create_event).and_return(LogStash::Event.new).at_least(:once)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "reading general information from a org.apache.log4j.spi.LoggingEvent" do
|
36
|
+
let (:input) { LogStash::Plugin.lookup("input", "log4j").new("mode" => "client") }
|
37
|
+
let (:log_obj) {
|
38
|
+
org.apache.log4j.spi.LoggingEvent.new(
|
39
|
+
"org.apache.log4j.Logger",
|
40
|
+
org.apache.log4j.Logger.getLogger("org.apache.log4j.LayoutTest"),
|
41
|
+
1426366971,
|
42
|
+
org.apache.log4j.Level::INFO,
|
43
|
+
"Hello, World",
|
44
|
+
nil
|
45
|
+
)
|
46
|
+
}
|
47
|
+
|
48
|
+
let (:log_obj_with_stacktrace) {
|
49
|
+
org.apache.log4j.spi.LoggingEvent.new(
|
50
|
+
"org.apache.log4j.Logger",
|
51
|
+
org.apache.log4j.Logger.getLogger("org.apache.log4j.LayoutTest"),
|
52
|
+
1426366971,
|
53
|
+
org.apache.log4j.Level::INFO,
|
54
|
+
"Hello, World",
|
55
|
+
java.lang.IllegalStateException.new()
|
56
|
+
)
|
57
|
+
}
|
58
|
+
|
59
|
+
it "creates event with general information" do
|
60
|
+
subject = input.create_event(log_obj)
|
61
|
+
expect(subject["timestamp"]).to eq(1426366971)
|
62
|
+
expect(subject["path"]).to eq("org.apache.log4j.LayoutTest")
|
63
|
+
expect(subject["priority"]).to eq("INFO")
|
64
|
+
expect(subject["logger_name"]).to eq("org.apache.log4j.LayoutTest")
|
65
|
+
expect(subject["thread"]).to eq("main")
|
66
|
+
expect(subject["message"]).to eq("Hello, World")
|
67
|
+
# checks locationInformation is collected, but testing exact values is not meaningful in jruby
|
68
|
+
expect(subject["class"]).not_to be_empty
|
69
|
+
expect(subject["file"]).not_to be_empty
|
70
|
+
expect(subject["method"]).not_to be_empty
|
71
|
+
end
|
72
|
+
|
73
|
+
it "creates event without stacktrace" do
|
74
|
+
subject = input.create_event(log_obj)
|
75
|
+
expect(subject["stack_trace"]).to be_nil
|
76
|
+
end
|
77
|
+
|
78
|
+
it "creates event with stacktrace" do
|
79
|
+
subject = input.create_event(log_obj_with_stacktrace)
|
80
|
+
#checks stack_trace is collected, exact value is too monstruous
|
81
|
+
expect(subject["stack_trace"]).not_to be_empty
|
82
|
+
end
|
83
|
+
end
|
13
84
|
end
|
metadata
CHANGED
@@ -1,77 +1,69 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logstash-input-log4j
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Elastic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-09-23 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'
|
20
14
|
requirement: !ruby/object:Gem::Requirement
|
21
15
|
requirements:
|
22
16
|
- - '>='
|
23
17
|
- !ruby/object:Gem::Version
|
24
18
|
version: '0'
|
19
|
+
name: logstash-codec-plain
|
25
20
|
prerelease: false
|
26
21
|
type: :runtime
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: logstash-core
|
29
22
|
version_requirements: !ruby/object:Gem::Requirement
|
30
23
|
requirements:
|
31
24
|
- - '>='
|
32
25
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
34
|
-
|
35
|
-
- !ruby/object:Gem::Version
|
36
|
-
version: 2.0.0
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
37
28
|
requirement: !ruby/object:Gem::Requirement
|
38
29
|
requirements:
|
39
|
-
- -
|
30
|
+
- - ~>
|
40
31
|
- !ruby/object:Gem::Version
|
41
|
-
version:
|
42
|
-
|
43
|
-
- !ruby/object:Gem::Version
|
44
|
-
version: 2.0.0
|
32
|
+
version: 2.0.0.snapshot
|
33
|
+
name: logstash-core
|
45
34
|
prerelease: false
|
46
35
|
type: :runtime
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
|
-
name: logstash-devutils
|
49
36
|
version_requirements: !ruby/object:Gem::Requirement
|
50
37
|
requirements:
|
51
|
-
- -
|
38
|
+
- - ~>
|
52
39
|
- !ruby/object:Gem::Version
|
53
|
-
version:
|
40
|
+
version: 2.0.0.snapshot
|
41
|
+
- !ruby/object:Gem::Dependency
|
54
42
|
requirement: !ruby/object:Gem::Requirement
|
55
43
|
requirements:
|
56
44
|
- - '>='
|
57
45
|
- !ruby/object:Gem::Version
|
58
46
|
version: '0'
|
47
|
+
name: logstash-devutils
|
59
48
|
prerelease: false
|
60
49
|
type: :development
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
61
55
|
description: This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program
|
62
56
|
email: info@elastic.co
|
63
57
|
executables: []
|
64
58
|
extensions: []
|
65
59
|
extra_rdoc_files: []
|
66
60
|
files:
|
67
|
-
- .gitignore
|
68
61
|
- CHANGELOG.md
|
69
62
|
- CONTRIBUTORS
|
70
63
|
- Gemfile
|
71
64
|
- LICENSE
|
72
65
|
- NOTICE.TXT
|
73
66
|
- README.md
|
74
|
-
- Rakefile
|
75
67
|
- lib/logstash-input-log4j_jars.rb
|
76
68
|
- lib/logstash/inputs/log4j.rb
|
77
69
|
- logstash-input-log4j.gemspec
|
@@ -99,7 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
99
91
|
version: '0'
|
100
92
|
requirements: []
|
101
93
|
rubyforge_project:
|
102
|
-
rubygems_version: 2.
|
94
|
+
rubygems_version: 2.4.8
|
103
95
|
signing_key:
|
104
96
|
specification_version: 4
|
105
97
|
summary: Read events over a TCP socket from a Log4j SocketAppender
|
data/.gitignore
DELETED