logstash-logger 0.4.1 → 0.5.0

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.
@@ -0,0 +1,14 @@
1
+ module LogStashLogger
2
+ module Device
3
+ class Stdout < Base
4
+ def initialize(opts={})
5
+ @io = $stdout
6
+ end
7
+
8
+ def close
9
+ # no-op
10
+ # Calling $stdout.close would be a bad idea
11
+ end
12
+ end
13
+ end
14
+ end
@@ -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,11 @@
1
+ module LogStashLogger
2
+ module Device
3
+ class UDP < Socket
4
+ def connect
5
+ @io = UDPSocket.new.tap do |socket|
6
+ socket.connect(@host, @port)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ 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
- class LogStashLogger < ::Logger
2
-
3
- HOST = ::Socket.gethostname
4
-
5
- def initialize(host, port, socket_type=:udp)
6
- super(::LogStashLogger::Socket.new(host, port, socket_type))
7
- end
8
-
9
- def add(severity, message = nil, progname = nil, &block)
10
- severity ||= UNKNOWN
11
- if severity < @level
12
- return true
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
- progname ||= @progname
15
- if message.nil?
16
- if block_given?
17
- message = yield
18
- else
19
- message = progname
20
- progname = @progname
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
- event['source'] ||= HOST
48
- if event['source'] == 'unknown'
49
- event['source'] = HOST
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
@@ -1,5 +1,3 @@
1
- require 'logger'
2
-
3
- class LogStashLogger < ::Logger
4
- VERSION = "0.4.1"
1
+ module LogStashLogger
2
+ VERSION = "0.5.0"
5
3
  end
@@ -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
- gem.add_development_dependency 'rspec'
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
@@ -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-----
@@ -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
@@ -0,0 +1,15 @@
1
+ input {
2
+ tcp {
3
+ port => 8443
4
+ ssl_enable => true
5
+ ssl_cert => "samples/example.crt"
6
+ ssl_key => "samples/example.key"
7
+ codec => json_lines
8
+ }
9
+ }
10
+ output {
11
+ stdout {
12
+ codec => json_lines
13
+ }
14
+ }
15
+
data/samples/tcp.conf ADDED
@@ -0,0 +1,11 @@
1
+ input {
2
+ tcp {
3
+ port => 5228
4
+ codec => json_lines
5
+ }
6
+ }
7
+ output {
8
+ stdout {
9
+ codec => json_lines
10
+ }
11
+ }
data/samples/udp.conf ADDED
@@ -0,0 +1,11 @@
1
+ input {
2
+ udp {
3
+ port => 5228
4
+ codec => json_lines
5
+ }
6
+ }
7
+ output {
8
+ stdout {
9
+ codec => json_lines
10
+ }
11
+ }
@@ -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