vx-lib-logger 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1d28721e11382b1f0383f2cf07b19fc9098312b1
4
- data.tar.gz: 2d0c3698efe175863557797b011ba0d39c921ec1
3
+ metadata.gz: 3cd5b9c66ac3665ca6ec8c71115a1a0c92a75a49
4
+ data.tar.gz: 580fad2cf11354a4f0712482cf3a2074b04b2578
5
5
  SHA512:
6
- metadata.gz: ca6ce7efc36837cd479a8e54f724063014bba8eeacf66bb6dc26883e23f9e1af18366873292b9ad1632da06099ed869a0fcf58530b11e657fb9e2da004867409
7
- data.tar.gz: a8ef47f348bffb01310cb89c80d8a45bda8e0d537c3e0bb01779a304ed733b767895f92219ec7d994a8c57e17ba8a08091e45a9c1d4627cd7e7f166d63e8521e
6
+ metadata.gz: bdfabb6e1f5ea36ef83d26b3af50c44004174937bbd23a474c26cfb7b9f9d3c4422fe51d6182f7170e86d4803ae3f03e72719a1e545bd29bd6816e419b8f741a
7
+ data.tar.gz: f9610c3f29159002a469e8ab2edd16ca5fed2326bea24ed7f28cfdd52c579ed088fd5f8731c709b5ea5d46c1eb07266f1c2f1b283ee98b6a93876c66a8169138
data/lib/vx/lib/logger.rb CHANGED
@@ -5,17 +5,20 @@ module Vx ; module Lib
5
5
  module Logger
6
6
 
7
7
  autoload :Instance, File.expand_path("../logger/instance", __FILE__)
8
- autoload :JsonFormatter, File.expand_path("../logger/json_formatter", __FILE__)
9
- autoload :RawFormatter, File.expand_path("../logger/raw_formatter", __FILE__)
8
+ autoload :StdoutFormatter, File.expand_path("../logger/stdout_formatter", __FILE__)
9
+ autoload :JournalFormatter, File.expand_path("../logger/journal_formatter", __FILE__)
10
+ autoload :Logstash, File.expand_path("../logger/logstash", __FILE__)
11
+ autoload :Sanitizer, File.expand_path("../logger/sanitizer", __FILE__)
10
12
 
11
13
  module Rack
12
14
  autoload :HandleExceptions, File.expand_path("../logger/rack/handle_exceptions", __FILE__)
13
15
  end
14
16
 
15
- @@default = Instance.new(STDOUT)
17
+ @@logstash = Logstash.new
18
+ @@default = Instance.new(STDOUT, logstash: @@logstash)
16
19
 
17
20
  def self.get(io = nil, options = {})
18
- Instance.new(io || STDOUT, options)
21
+ Instance.new(io || STDOUT, options.merge(logstash: @@logstash))
19
22
  end
20
23
 
21
24
  def self.default
@@ -1,16 +1,33 @@
1
1
  require 'json'
2
2
  require 'thread'
3
+ require 'socket'
3
4
 
4
5
  module Vx ; module Lib ; module Logger
5
6
 
6
7
  class Instance
7
8
 
8
- attr_reader :params, :logger
9
+ attr_reader :params, :logger, :logstash
9
10
 
10
11
  def initialize(io, params = {})
11
- @params = params
12
+ @logstash = params.delete(:logstash)
13
+ @params = params
14
+
15
+ @progname = @params.delete(:progname)
16
+
17
+ @progname ||= begin
18
+ pname = $PROGRAM_NAME
19
+ pname && File.basename(pname)
20
+ end
21
+
22
+ @formatter = ->(_,_,_,m) { m }
23
+
12
24
  @logger = ::Logger.new(io, 7, 50_000_000)
13
- @logger.formatter = RawFormatter.new
25
+ @logger.formatter = @formatter
26
+ @logger.progname = @progname
27
+
28
+ @logstash_logger = ::Logger.new(logstash)
29
+ @logstash_logger.formatter = @formatter
30
+ @logstash_logger.progname = @progname
14
31
  end
15
32
 
16
33
  [:fatal, :warn, :debug, :error, :info].each do |m|
@@ -32,7 +49,7 @@ module Vx ; module Lib ; module Logger
32
49
  end
33
50
 
34
51
  def progname=(new_val)
35
- params[:progname] = new_val
52
+ @progname = new_val
36
53
  end
37
54
 
38
55
  def handle(message, options = {})
@@ -64,29 +81,44 @@ module Vx ; module Lib ; module Logger
64
81
  def process_message(level, message, options = {})
65
82
 
66
83
  if options[:exception] && options[:exception].is_a?(Exception)
67
- ex = options[:exception]
84
+ ex = options.delete(:exception)
68
85
  options.merge!(
69
- exception: [ex.class.to_s, ex.message],
86
+ exception: [ex.class.to_s, ex.message].join(" - "),
70
87
  backtrace: (ex.backtrace || []).map(&:to_s).join("\n"),
71
88
  )
72
89
  end
73
90
 
74
91
  body = {
75
92
  thread_id: ::Thread.current.object_id,
76
- process_id: ::Process.pid,
77
93
  }
78
94
 
79
95
  if options && options != {}
96
+
97
+ duration = options.delete(:duration)
98
+ if duration && duration.respond_to?(:to_f)
99
+ body.merge!(duration: duration.to_f)
100
+ end
101
+
80
102
  body.merge!(
81
- fields: options
103
+ fields: Sanitizer.hash(options)
82
104
  )
83
105
  end
84
106
 
85
- @logger.public_send level, format_message(message, body)
107
+
108
+ @logger.public_send level, format_stdout(level, message, body)
109
+
110
+ if logstash.enabled?
111
+ @logstash_logger.public_send level, format_journal(level, message, body)
112
+ end
86
113
  end
87
114
 
88
- def format_message(message, payload)
89
- JsonFormatter.call(message, payload)
115
+ def format_stdout(level, message, payload)
116
+ StdoutFormatter.call(level, message, payload)
117
+ end
118
+
119
+ def format_journal(level, message, payload)
120
+ JournalFormatter.call(level, @progname, message, payload)
121
+
90
122
  end
91
123
 
92
124
  end
@@ -0,0 +1,31 @@
1
+ require 'oj'
2
+
3
+ module Vx ; module Lib ; module Logger
4
+
5
+ module JournalFormatter
6
+
7
+ @@exe = $PROGRAM_NAME
8
+ @@gid = ::Process.gid
9
+ @@uid = ::Process.uid
10
+ @@pid = ::Process.pid
11
+ @@host = ::Socket.gethostname
12
+
13
+ def self.call(level, progname, message, payload)
14
+ m = ::Oj.dump(
15
+ payload.merge(
16
+ MESSAGE: message.to_s,
17
+ SYSLOG_IDENTIFIER: progname,
18
+ _EXE: @@exe,
19
+ _GID: @@gid,
20
+ _UID: @@uid,
21
+ _PID: @@pid,
22
+ _HOSTNAME: @@host,
23
+ level: level,
24
+ ),
25
+ mode: :compat
26
+ )
27
+ m.encode("UTF-8", invalid: :replace, replace: "?") << "\n"
28
+ end
29
+ end
30
+
31
+ end ; end ; end
@@ -0,0 +1,87 @@
1
+ require 'socket'
2
+ require 'uri'
3
+ require 'thread'
4
+
5
+ module Vx ; module Lib ; module Logger
6
+
7
+ class Logstash
8
+
9
+ def initialize
10
+ @mutex = Mutex.new
11
+ end
12
+
13
+ def uri
14
+ @uri ||= begin
15
+ h = ENV['LOGSTASH_HOST']
16
+ URI("logstash://#{h}") if h
17
+ end
18
+ end
19
+
20
+ def enabled?
21
+ !!uri
22
+ end
23
+
24
+ def connected?
25
+ !!@io
26
+ end
27
+
28
+ def close
29
+ @io && @io.close
30
+ rescue Exception => e
31
+ warn "#{self.class} - #{e.class} - #{e.message}"
32
+ ensure
33
+ @io = nil
34
+ end
35
+
36
+ def write(message)
37
+ if enabled?
38
+ @mutex.synchronize do
39
+ with_connection do
40
+ @io.write message
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ def flush
47
+ @io && @io.flush
48
+ end
49
+
50
+ private
51
+
52
+ def host
53
+ uri.host
54
+ end
55
+
56
+ def port
57
+ @port ||= uri.port || 514
58
+ end
59
+
60
+ def warn(msg)
61
+ $stderr.puts msg
62
+ end
63
+
64
+ def with_connection(&block)
65
+ connect unless connected?
66
+ yield
67
+ rescue Exception => e
68
+ warn "#{self.class} - #{e.class} - #{e.message}"
69
+ close
70
+ @io = nil
71
+ end
72
+
73
+ def reconnect
74
+ @io = nil
75
+ connect
76
+ end
77
+
78
+ def connect
79
+ @io = TCPSocket.new(host, port).tap do |socket|
80
+ warn "#{self.class} - connect to #{uri.to_s}"
81
+ socket.sync = true
82
+ end
83
+ end
84
+
85
+ end
86
+
87
+ end ; end ; end
@@ -0,0 +1,20 @@
1
+ module Vx ; module Lib ; module Logger
2
+
3
+ module Sanitizer
4
+
5
+ def self.hash(payload)
6
+ payload = {} unless payload.is_a?(Hash)
7
+
8
+ payload.keys.each do |key_name|
9
+ value = payload[key_name]
10
+ unless value.is_a?(String)
11
+ payload[key_name] = value.to_s
12
+ end
13
+ end
14
+
15
+ payload
16
+ end
17
+
18
+ end
19
+
20
+ end ; end ; end
@@ -2,11 +2,11 @@ require 'oj'
2
2
 
3
3
  module Vx ; module Lib ; module Logger
4
4
 
5
- module JsonFormatter
5
+ module StdoutFormatter
6
6
 
7
- def self.call(message, payload)
7
+ def self.call(level, message, payload)
8
8
  payload = ::Oj.dump(payload, mode: :compat)
9
- "#{message} :--: #{payload}"
9
+ "[#{level}] #{message} :--: #{payload}\n"
10
10
  end
11
11
 
12
12
  end
@@ -1,7 +1,7 @@
1
1
  module Vx
2
2
  module Lib
3
3
  module Logger
4
- VERSION = "0.1.1"
4
+ VERSION = "0.2.0"
5
5
  end
6
6
  end
7
7
  end
@@ -4,77 +4,91 @@ require 'stringio'
4
4
 
5
5
  describe Vx::Lib::Logger::Instance do
6
6
 
7
- before do
8
- @out = StringIO.new
9
- @log = Vx::Lib::Logger.get(@out)
10
- assert @log
11
- end
12
-
13
- [:fatal, :warn, :debug, :error, :info].each do |m|
14
- it "should write #{m} message" do
15
- @log.public_send(m, "send #{m}")
16
- text = "[#{m.upcase}] send #{m} :--: {\"thread_id\":#{tid},\"process_id\":#{pid}}\n"
17
- assert_equal get_out, text
18
- end
19
- end
20
-
21
- it "should write message with params" do
22
- @log.info "text message", param: :value
23
- text = "[INFO] text message :--: {\"thread_id\":#{tid},\"process_id\":#{pid},\"fields\":{\"param\":\"value\"}}\n"
24
- assert_equal get_out, text
25
- end
26
-
27
- it "should write message with object in params" do
28
- @log.info "text message", param: self
29
- assert get_out
30
- end
31
-
32
- it "should write message with exception in params" do
33
- @log.info "text message", exception: Exception.new("got!")
34
- text = "[INFO] text message :--: {\"thread_id\":#{tid},\"process_id\":#{pid},\"fields\":{\"exception\":[\"Exception\",\"got!\"],\"backtrace\":\"\"}}\n"
35
- assert_equal get_out, text
36
- end
37
-
38
- it "should handle block" do
39
- @log.handle "text message" do
40
- sleep 0.1
41
- end
42
- assert_match(/duration/, get_out)
43
-
44
- begin
45
- @log.handle "text message", key: :value do
46
- raise 'got!'
47
- end
48
- rescue Exception
49
- end
50
-
51
- body = get_out
52
- assert_match(/duration/, body)
53
- assert_match(/key/, body)
54
- assert_match(/value/, body)
55
- assert_match(/got\!/, body)
56
- assert_match(/backtrace/, body)
57
- end
58
-
59
- it "should dump invalid unicode key" do
60
- @log.info "Le Caf\xc3\xa9 \xa9", key: "Le Caf\xc3\xa9 \xa9"
61
- text = "[INFO] Le Café \xA9 :--: {\"thread_id\":#{tid},\"process_id\":#{pid},\"fields\":{\"key\":\"Le Café \xA9\"}}\n"
62
- assert_equal get_out, text
63
- end
64
-
65
- def get_out
66
- @out.rewind
67
- body = @out.read
68
- @out.rewind
69
- body
70
- end
71
-
72
- def tid
73
- Thread.current.object_id
74
- end
75
-
76
- def pid
77
- Process.pid
78
- end
7
+ before do
8
+ @out = StringIO.new
9
+ @log = Vx::Lib::Logger.get(@out)
10
+ assert @log
11
+ end
12
+
13
+ [:fatal, :warn, :debug, :error, :info].each do |m|
14
+ it "should write #{m} message" do
15
+ @log.public_send(m, "send #{m}")
16
+ text = "[#{m}] send #{m} :--: {\"thread_id\":#{tid}}\n"
17
+ assert_equal text, get_out
18
+ end
19
+ end
20
+
21
+ it "should write message with params" do
22
+ @log.info "text message", param: :value
23
+ text = "[info] text message :--: {\"thread_id\":#{tid},\"fields\":{\"param\":\"value\"}}\n"
24
+ assert_equal text, get_out
25
+ end
26
+
27
+ it "should write message with object in params" do
28
+ @log.info "object message", param: Exception.new("foo")
29
+ assert get_out
30
+ end
31
+
32
+ it "should write message with exception in params" do
33
+ @log.info "text message", exception: Exception.new("got!")
34
+ text = "[info] text message :--: {\"thread_id\":#{tid},\"fields\":{\"exception\":\"Exception - got!\",\"backtrace\":\"\"}}\n"
35
+ assert_equal text, get_out
36
+ end
37
+
38
+ it "should handle block" do
39
+ @log.handle "text message" do
40
+ sleep 0.1
41
+ end
42
+ assert_match(/duration/, get_out)
43
+
44
+ begin
45
+ @log.handle "text message", key: :value do
46
+ raise 'got!'
47
+ end
48
+ rescue Exception
49
+ end
50
+
51
+ body = get_out
52
+ assert_match(/duration/, body)
53
+ assert_match(/key/, body)
54
+ assert_match(/value/, body)
55
+ assert_match(/got\!/, body)
56
+ assert_match(/backtrace/, body)
57
+ end
58
+
59
+ it "should dump invalid unicode key" do
60
+ @log.info "Le Caf\xc3\xa9 \xa9", key: "Le Caf\xc3\xa9 \xa9"
61
+ text = "[info] Le Café \xA9 :--: {\"thread_id\":#{tid},\"fields\":{\"key\":\"Le Café \xA9\"}}\n"
62
+ assert_equal text, get_out
63
+ end
64
+
65
+ it "should write message with logstash" do
66
+ begin
67
+ ENV['LOGSTASH_HOST'] = 'localhost:9999'
68
+ log = Vx::Lib::Logger.get(@out)
69
+ re = with_socket do
70
+ log.info "Hello"
71
+ end
72
+ assert_match(/Hello/, re)
73
+ assert_match(/Hello/, get_out)
74
+ ensure
75
+ ENV['LOGSTASH_HOST'] = nil
76
+ end
77
+ end
78
+
79
+ def get_out
80
+ @out.rewind
81
+ body = @out.read
82
+ @out.rewind
83
+ body
84
+ end
85
+
86
+ def tid
87
+ Thread.current.object_id
88
+ end
89
+
90
+ def pid
91
+ Process.pid
92
+ end
79
93
 
80
94
  end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe Vx::Lib::Logger::Logstash do
4
+
5
+ before do
6
+ ENV['LOGSTASH_HOST'] = 'localhost:9999'
7
+ end
8
+
9
+ after do
10
+ ENV['LOGSTASH_HOST'] = nil
11
+ end
12
+
13
+ it "should successfuly connect" do
14
+ re = with_socket do
15
+ log = Vx::Lib::Logger::Logstash.new
16
+ log.write("Hello\n")
17
+ log.close
18
+ end
19
+ assert_equal re, "Hello\n"
20
+ end
21
+
22
+ it "should successfuly lost connection" do
23
+ log = Vx::Lib::Logger::Logstash.new
24
+
25
+ re = with_socket do
26
+ log.write("Hello\n")
27
+ log.close
28
+ end
29
+
30
+ log.write("Lost\n")
31
+
32
+ re << with_socket do
33
+ log.write("World\n")
34
+ end
35
+
36
+ log.close
37
+
38
+ assert_equal re, "Hello\nWorld\n"
39
+ end
40
+
41
+
42
+ end
data/spec/spec_helper.rb CHANGED
@@ -2,3 +2,25 @@ require File.expand_path("../../lib/vx/lib/logger", __FILE__)
2
2
 
3
3
  require 'minitest/spec'
4
4
  require 'minitest/autorun'
5
+
6
+ def with_socket
7
+ out = ""
8
+ server = TCPServer.new 9999
9
+
10
+ th = Thread.new do
11
+ loop do
12
+ client = server.accept
13
+ out << client.gets
14
+ client.close
15
+ end
16
+ end
17
+
18
+ begin
19
+ yield
20
+ sleep 0.2
21
+ out
22
+ ensure
23
+ th.kill
24
+ server.close
25
+ end
26
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vx-lib-logger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Galinsky
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-02 00:00:00.000000000 Z
11
+ date: 2015-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oj
@@ -66,12 +66,15 @@ files:
66
66
  - Rakefile
67
67
  - lib/vx/lib/logger.rb
68
68
  - lib/vx/lib/logger/instance.rb
69
- - lib/vx/lib/logger/json_formatter.rb
69
+ - lib/vx/lib/logger/journal_formatter.rb
70
+ - lib/vx/lib/logger/logstash.rb
70
71
  - lib/vx/lib/logger/rack/handle_exceptions.rb
71
- - lib/vx/lib/logger/raw_formatter.rb
72
+ - lib/vx/lib/logger/sanitizer.rb
73
+ - lib/vx/lib/logger/stdout_formatter.rb
72
74
  - lib/vx/lib/logger/version.rb
73
75
  - spec/lib/instance_spec.rb
74
76
  - spec/lib/logger_spec.rb
77
+ - spec/lib/logstash_spec.rb
75
78
  - spec/lib/rack_handle_exceptions_spec.rb
76
79
  - spec/spec_helper.rb
77
80
  - vexor.yml
@@ -103,6 +106,6 @@ summary: summary
103
106
  test_files:
104
107
  - spec/lib/instance_spec.rb
105
108
  - spec/lib/logger_spec.rb
109
+ - spec/lib/logstash_spec.rb
106
110
  - spec/lib/rack_handle_exceptions_spec.rb
107
111
  - spec/spec_helper.rb
108
- has_rdoc:
@@ -1,10 +0,0 @@
1
- module Vx ; module Lib ; module Logger
2
-
3
- RawFormatter = Struct.new(:parent) do
4
-
5
- def call(severity, datetime, progname, msg)
6
- "[#{severity}] #{msg.to_s}\n"
7
- end
8
- end
9
-
10
- end ; end ; end