logstash-logger 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rspec +3 -0
- data/.travis.yml +6 -3
- data/Appraisals +11 -0
- data/CHANGELOG.md +20 -13
- data/Gemfile +1 -7
- data/README.md +94 -35
- data/Rakefile +3 -5
- data/gemfiles/rails_3.2.gemfile +7 -0
- data/gemfiles/rails_4.0.gemfile +7 -0
- data/gemfiles/rails_4.1.gemfile +7 -0
- data/lib/logstash-logger.rb +4 -3
- data/lib/logstash-logger/device.rb +28 -0
- data/lib/logstash-logger/device/base.rb +30 -0
- data/lib/logstash-logger/device/socket.rb +56 -0
- data/lib/logstash-logger/device/stdout.rb +14 -0
- data/lib/logstash-logger/device/tcp.rb +43 -0
- data/lib/logstash-logger/device/udp.rb +11 -0
- data/lib/logstash-logger/formatter.rb +51 -0
- data/lib/logstash-logger/logger.rb +32 -46
- data/lib/logstash-logger/railtie.rb +23 -0
- data/lib/logstash-logger/tagged_logging.rb +40 -0
- data/lib/logstash-logger/version.rb +2 -4
- data/logstash-logger.gemspec +5 -1
- data/samples/example.crt +16 -0
- data/samples/example.key +15 -0
- data/samples/ssl.conf +15 -0
- data/samples/tcp.conf +11 -0
- data/samples/udp.conf +11 -0
- data/spec/device/socket_spec.rb +15 -0
- data/spec/device/stdout_spec.rb +14 -0
- data/spec/device/tcp_spec.rb +36 -0
- data/spec/device/udp_spec.rb +9 -0
- data/spec/device_spec.rb +11 -0
- data/spec/logger_spec.rb +16 -30
- data/spec/rails_spec.rb +52 -0
- data/spec/spec_helper.rb +38 -5
- data/spec/tagged_logging_spec.rb +32 -0
- metadata +78 -4
- data/lib/logstash-logger/socket.rb +0 -34
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module LogStashLogger
|
4
|
+
module Device
|
5
|
+
class TCP < Socket
|
6
|
+
attr_reader :ssl_certificate
|
7
|
+
|
8
|
+
def initialize(opts)
|
9
|
+
super
|
10
|
+
|
11
|
+
@ssl_certificate = opts[:ssl_certificate]
|
12
|
+
@use_ssl = !!(@ssl_certificate || opts[:use_ssl] || opts[:ssl_enable])
|
13
|
+
end
|
14
|
+
|
15
|
+
def use_ssl?
|
16
|
+
@use_ssl || !@ssl_certificate.nil?
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def connect
|
22
|
+
if use_ssl?
|
23
|
+
ssl_connect
|
24
|
+
else
|
25
|
+
non_ssl_connect
|
26
|
+
end
|
27
|
+
|
28
|
+
@io
|
29
|
+
end
|
30
|
+
|
31
|
+
def non_ssl_connect
|
32
|
+
@io = TCPSocket.new(@host, @port)
|
33
|
+
end
|
34
|
+
|
35
|
+
def ssl_connect
|
36
|
+
non_ssl_connect
|
37
|
+
#openssl_cert = OpenSSL::X509::Certificate.new(::File.read(@ssl_certificate))
|
38
|
+
@io = OpenSSL::SSL::SSLSocket.new(@io)
|
39
|
+
@io.connect
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'socket'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
module LogStashLogger
|
6
|
+
HOST = ::Socket.gethostname
|
7
|
+
|
8
|
+
class Formatter < ::Logger::Formatter
|
9
|
+
include TaggedLogging::Formatter
|
10
|
+
|
11
|
+
def call(severity, time, progname, message)
|
12
|
+
event = build_event(message, severity, time)
|
13
|
+
"#{event.to_json}\n"
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def build_event(message, severity, time)
|
19
|
+
data = message
|
20
|
+
if data.is_a?(String) && data.start_with?('{')
|
21
|
+
data = (JSON.parse(message) rescue nil) || message
|
22
|
+
end
|
23
|
+
|
24
|
+
event = case data
|
25
|
+
when LogStash::Event
|
26
|
+
data.clone
|
27
|
+
when Hash
|
28
|
+
event_data = data.merge("@timestamp" => time)
|
29
|
+
LogStash::Event.new(event_data)
|
30
|
+
when String
|
31
|
+
LogStash::Event.new("message" => data, "@timestamp" => time)
|
32
|
+
end
|
33
|
+
|
34
|
+
event['severity'] ||= severity
|
35
|
+
#event.type = progname
|
36
|
+
|
37
|
+
event['host'] ||= HOST
|
38
|
+
|
39
|
+
current_tags.each do |tag|
|
40
|
+
event.tag(tag)
|
41
|
+
end
|
42
|
+
|
43
|
+
# In case Time#to_json has been overridden
|
44
|
+
if event.timestamp.is_a?(Time)
|
45
|
+
event.timestamp = event.timestamp.iso8601(3)
|
46
|
+
end
|
47
|
+
|
48
|
+
event
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -1,54 +1,40 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
def
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
1
|
+
require 'logger'
|
2
|
+
require 'logstash-logger/tagged_logging'
|
3
|
+
|
4
|
+
module LogStashLogger
|
5
|
+
def self.new(*args)
|
6
|
+
opts = extract_opts(*args)
|
7
|
+
@device = Device.new(opts)
|
8
|
+
|
9
|
+
::Logger.new(@device).tap do |logger|
|
10
|
+
logger.extend(self)
|
11
|
+
logger.extend(TaggedLogging)
|
12
|
+
logger.formatter = Formatter.new
|
13
13
|
end
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.included(base)
|
17
|
+
base.instance_eval do
|
18
|
+
attr_reader :device
|
19
|
+
|
20
|
+
def flush
|
21
|
+
!!@device.flush
|
21
22
|
end
|
22
23
|
end
|
23
|
-
@logdev.write(
|
24
|
-
format_message(format_severity(severity), Time.now, progname, message))
|
25
|
-
true
|
26
24
|
end
|
27
|
-
|
28
|
-
def format_message(severity, time, progname, message)
|
29
|
-
data = message
|
30
|
-
if data.is_a?(String) && data.start_with?('{')
|
31
|
-
data = (JSON.parse(message) rescue nil) || message
|
32
|
-
end
|
33
|
-
|
34
|
-
event = case data
|
35
|
-
when LogStash::Event
|
36
|
-
data.clone
|
37
|
-
when Hash
|
38
|
-
event_data = data.merge("@timestamp" => time)
|
39
|
-
LogStash::Event.new(event_data)
|
40
|
-
when String
|
41
|
-
LogStash::Event.new("message" => data, "@timestamp" => time)
|
42
|
-
end
|
43
|
-
|
44
|
-
event['severity'] ||= severity
|
45
|
-
#event.type = progname
|
46
25
|
|
47
|
-
|
48
|
-
|
49
|
-
|
26
|
+
protected
|
27
|
+
|
28
|
+
def self.extract_opts(*args)
|
29
|
+
if args.length > 1
|
30
|
+
puts "[LogStashLogger] (host, port, type) constructor is deprecated. Please use an options hash instead."
|
31
|
+
host, port, type = *args
|
32
|
+
{host: host, port: port, type: type}
|
33
|
+
elsif Hash === args[0]
|
34
|
+
args[0]
|
35
|
+
else
|
36
|
+
fail ArgumentError, "Invalid LogStashLogger options"
|
50
37
|
end
|
51
|
-
|
52
|
-
event
|
53
38
|
end
|
39
|
+
|
54
40
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rails/railtie'
|
2
|
+
|
3
|
+
module LogStashLogger
|
4
|
+
def self.setup(app)
|
5
|
+
return unless app.config.logstash.present?
|
6
|
+
|
7
|
+
logger_options = app.config.logstash
|
8
|
+
|
9
|
+
logger = LogStashLogger.new(logger_options)
|
10
|
+
|
11
|
+
logger.level = ::Logger.const_get(app.config.log_level.to_s.upcase)
|
12
|
+
|
13
|
+
app.config.logger = logger
|
14
|
+
end
|
15
|
+
|
16
|
+
class Railtie < ::Rails::Railtie
|
17
|
+
config.logstash = ActiveSupport::OrderedOptions.new
|
18
|
+
|
19
|
+
initializer :logstash_logger, before: :initialize_logger do |app|
|
20
|
+
LogStashLogger.setup(app)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module LogStashLogger
|
2
|
+
# Shamelessly copied from ActiveSupport::TaggedLogging
|
3
|
+
module TaggedLogging
|
4
|
+
def tagged(*tags)
|
5
|
+
formatter.tagged(*tags) { yield self }
|
6
|
+
end
|
7
|
+
|
8
|
+
def flush
|
9
|
+
formatter.clear_tags!
|
10
|
+
super if defined?(super)
|
11
|
+
end
|
12
|
+
|
13
|
+
module Formatter
|
14
|
+
def tagged(*tags)
|
15
|
+
new_tags = push_tags(*tags)
|
16
|
+
yield self
|
17
|
+
ensure
|
18
|
+
pop_tags(new_tags.size)
|
19
|
+
end
|
20
|
+
|
21
|
+
def push_tags(*tags)
|
22
|
+
tags.flatten.reject(&:empty?).tap do |new_tags|
|
23
|
+
current_tags.concat new_tags
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def pop_tags(size = 1)
|
28
|
+
current_tags.pop size
|
29
|
+
end
|
30
|
+
|
31
|
+
def clear_tags!
|
32
|
+
current_tags.clear
|
33
|
+
end
|
34
|
+
|
35
|
+
def current_tags
|
36
|
+
Thread.current[:logstash_logger_tags] ||= []
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/logstash-logger.gemspec
CHANGED
@@ -19,7 +19,11 @@ Gem::Specification.new do |gem|
|
|
19
19
|
gem.require_paths = ["lib"]
|
20
20
|
|
21
21
|
gem.add_runtime_dependency 'logstash-event', '~> 1.2'
|
22
|
-
|
22
|
+
|
23
|
+
gem.add_development_dependency 'rails'
|
24
|
+
gem.add_development_dependency 'rspec', '>= 3'
|
23
25
|
gem.add_development_dependency 'rake'
|
26
|
+
gem.add_development_dependency 'pry'
|
24
27
|
gem.add_development_dependency 'wwtd'
|
28
|
+
gem.add_development_dependency 'appraisal'
|
25
29
|
end
|
data/samples/example.crt
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIICdTCCAd4CCQDLMpdv+B+sYDANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJV
|
3
|
+
UzETMBEGA1UECBMKQ2FsaWZvcm5pYTERMA8GA1UEBxMIUGFzYWRlbmExDzANBgNV
|
4
|
+
BAoTBkJ1dGxlcjEVMBMGA1UEAxMMRGF2aWQgQnV0bGVyMSAwHgYJKoZIhvcNAQkB
|
5
|
+
FhFkd2J1dGxlckB1Y2xhLmVkdTAeFw0xNDA2MDkwNjIyMTZaFw0xNTA2MDkwNjIy
|
6
|
+
MTZaMH8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMREwDwYDVQQH
|
7
|
+
EwhQYXNhZGVuYTEPMA0GA1UEChMGQnV0bGVyMRUwEwYDVQQDEwxEYXZpZCBCdXRs
|
8
|
+
ZXIxIDAeBgkqhkiG9w0BCQEWEWR3YnV0bGVyQHVjbGEuZWR1MIGfMA0GCSqGSIb3
|
9
|
+
DQEBAQUAA4GNADCBiQKBgQDbBJ1t2m8RA72GZZ8XOUCsLI1EhRqZqWx0zYHpZbc4
|
10
|
+
kVbfMeb5uyfpx1gd5QngZrsD0cvTf46F9z9Ng805Ns9jE5QBJxefl1b6iJ6f/Y9s
|
11
|
+
W+WYQJgtNOcJahWWLeiBXqApPUllFI2ME/52+/umuSYVtSVAW7QmPx3YSLPpVjkQ
|
12
|
+
3wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAKiZm6yNzRXKJ9LBLL+Nep87WPlkdFmx
|
13
|
+
YUoEtTF8XpgMNfOdzF8DlD4FmoNz/SD7uVOk+tARUoEQFVK+iqpd3/dLDVa/pslX
|
14
|
+
a5v6AcmgfSFUHgLvKQZNvgY50jhLdqruf8PCgVnq+4L0ItguJx/viYMxF0p0fJTG
|
15
|
+
oGX8W4ul0W5F
|
16
|
+
-----END CERTIFICATE-----
|
data/samples/example.key
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIICWwIBAAKBgQDbBJ1t2m8RA72GZZ8XOUCsLI1EhRqZqWx0zYHpZbc4kVbfMeb5
|
3
|
+
uyfpx1gd5QngZrsD0cvTf46F9z9Ng805Ns9jE5QBJxefl1b6iJ6f/Y9sW+WYQJgt
|
4
|
+
NOcJahWWLeiBXqApPUllFI2ME/52+/umuSYVtSVAW7QmPx3YSLPpVjkQ3wIDAQAB
|
5
|
+
AoGAYaFXBAchB3ahX22hU1rkJ1vcxTSIPQM3I4IQbRg4anDvRqMaESyKiD2iXAEj
|
6
|
+
O/LPXs6Ai5EK2VDz2Pvt2ZlDLFX3GKMEChtIa2mpiQqIdoJS+42ibqfHrv3nlnTK
|
7
|
+
OTeTcw+SbAAFTKFPJO94lGcBXhsYYW/LAFH08bVqp+Jhi1ECQQDzkz4O0eYH05Wb
|
8
|
+
1bAwjNcjj+X6JKIRGgWYb1kkk3VyAFuD6qCEKBfS2t4nZs1yy83Ch4Bk9YmDHx1Q
|
9
|
+
gdYCjPsLAkEA5jCwo51KqboxAz6m0pZwdiKlCav94TMdHMKTFcUE5fDoWrwzrIyH
|
10
|
+
8rr7q7cl3culX7pUcPdKMl78Nw6/4JFF/QJARwNfrWxut0ttq+BSHOWC98BFWXeC
|
11
|
+
tJ+0j+uuvqYrMJCAHeay47TYtUXQTQaA0X4vwA5HVafsbokMv+MKpPW7XwJAF713
|
12
|
+
yjgDpkOMoIAKpndbe+OQz9GMKniiDQBIORuLqMdSv2Dfl3Ea6D6+i/QklJ5XHOtT
|
13
|
+
oB7w6QcAzhDYWynAZQJAX0UF9alkX8CQq1rvsAG6zzh3bD+Udy7cU0Yonx/byZoz
|
14
|
+
K6G/seM5YFTMVrnbY9V23aIhjJSxGWOB5fDNPG5p4w==
|
15
|
+
-----END RSA PRIVATE KEY-----
|
data/samples/ssl.conf
ADDED
data/samples/tcp.conf
ADDED
data/samples/udp.conf
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'logstash-logger'
|
2
|
+
|
3
|
+
describe LogStashLogger::Device::Socket do
|
4
|
+
include_context 'device'
|
5
|
+
|
6
|
+
it "defaults host to 0.0.0.0" do
|
7
|
+
expect(device_with_port.host).to eq("0.0.0.0")
|
8
|
+
end
|
9
|
+
|
10
|
+
context "when port is not specified" do
|
11
|
+
it "raises an exception" do
|
12
|
+
expect { described_class.new }.to raise_error
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'logstash-logger'
|
2
|
+
|
3
|
+
describe LogStashLogger::Device::Stdout do
|
4
|
+
it "writes to $stdout" do
|
5
|
+
expect(subject.to_io).to eq($stdout)
|
6
|
+
expect($stdout).to receive(:write).once
|
7
|
+
subject.write("test")
|
8
|
+
end
|
9
|
+
|
10
|
+
it "ignores #close" do
|
11
|
+
expect($stdout).not_to receive(:close)
|
12
|
+
subject.close
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'logstash-logger'
|
2
|
+
|
3
|
+
describe LogStashLogger::Device::TCP do
|
4
|
+
include_context 'device'
|
5
|
+
|
6
|
+
let(:tcp_socket) { double('TCPSocket') }
|
7
|
+
let(:ssl_socket) { double('SSLSocket') }
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
allow(TCPSocket).to receive(:new) { tcp_socket }
|
11
|
+
allow(OpenSSL::SSL::SSLSocket).to receive(:new) { ssl_socket }
|
12
|
+
allow(ssl_socket).to receive(:connect)
|
13
|
+
end
|
14
|
+
|
15
|
+
context "when not using SSL" do
|
16
|
+
it "writes to a TCP socket" do
|
17
|
+
expect(tcp_socket).to receive(:write)
|
18
|
+
tcp_device.write('test')
|
19
|
+
end
|
20
|
+
|
21
|
+
it "returns false for #use_ssl?" do
|
22
|
+
expect(tcp_device.use_ssl?).to be_falsey
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "when using SSL" do
|
27
|
+
it "writes to an SSL TCP socket" do
|
28
|
+
expect(ssl_socket).to receive(:write)
|
29
|
+
ssl_tcp_device.write('test')
|
30
|
+
end
|
31
|
+
|
32
|
+
it "returns false for #use_ssl?" do
|
33
|
+
expect(ssl_tcp_device.use_ssl?).to be_truthy
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|