logfoo 0.0.7 → 0.0.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a9e14527e5a1943db7d50f730a72d3b5cf1ef318
4
- data.tar.gz: 1272b5aa0c45b0fcc479bc71bdc58661adf8d074
3
+ metadata.gz: ee42d936b037ecb79f2b85171ee812bcd76c8047
4
+ data.tar.gz: c2c96c656ee3bb5a9b616f8b08868927063295f7
5
5
  SHA512:
6
- metadata.gz: c5be6f234ee21b01a2a6ee246bdffa8a5adc8ea6f748d00f9975cae3793cd15b00b2c5cd26ab9b9f091536ec20adc8d4a6ce6d72325bcdec75294df88b1da011
7
- data.tar.gz: 35d6cdb08f4a95dc274aa8c7f298ef9c4f105fa672d667e4dca626fc2f0ef38a7d16b06ab7906c11674829cbff2dabb551f71ba0f0dc3fc582877a9449a30037
6
+ metadata.gz: ef45863c233793cc602e7ee1f5b2c6fff8de285e4b9f5b8a8979f9cf6b7293e26e4a8a50df71c82e01ada987077271f68dccfa11e5f1719b8ebb6470622c417a
7
+ data.tar.gz: 4bd410dfcb13c9b593b9b00f206daf0a9685b3fd661c4080f23d06b4947410d1b5e54aa7af780a1ea280f91f8dd5e70829fcc53213c78f807232bfe3a408a144
data/lib/logfoo/app.rb CHANGED
@@ -6,9 +6,13 @@ module Logfoo
6
6
 
7
7
  include Singleton
8
8
 
9
+ IGNORE_ME_ERROR = RuntimeError.new("ignore me")
10
+
9
11
  def initialize
10
12
  @queue = Queue.new
11
13
  @lock = Mutex.new
14
+
15
+ start
12
16
  end
13
17
 
14
18
  def start
@@ -38,15 +42,6 @@ module Logfoo
38
42
 
39
43
  private
40
44
 
41
- def new_entry(msg)
42
- Entry.new(
43
- :info,
44
- Time.now,
45
- self.class,
46
- msg
47
- )
48
- end
49
-
50
45
  def main_loop ; Thread.new do
51
46
  begin
52
47
  loop do
@@ -55,42 +50,44 @@ module Logfoo
55
50
  break
56
51
  end
57
52
  if entry == :boom
58
- raise RuntimeError.new("ignore me")
53
+ raise IGNORE_ME_ERROR
59
54
  end
60
- App.append(entry)
55
+ App._append(entry)
56
+ App._handle_exception(entry) if entry.is_a?(ExceptionEntry)
61
57
  end
62
58
  rescue Exception => ex
63
- App.handle_exception(ex, self.class)
59
+ entry = ExceptionEntry.build(self.class, ex)
60
+ App._handle_exception(entry)
64
61
  retry
65
62
  end
66
63
  end ; end
67
64
  end
68
65
 
69
- class App
70
- class << self
71
- @@appenders = [IoAppender.new]
72
- @@exception_handlers = [StderrExceptionHanlder.new]
66
+ class App ; class << self
67
+ @@appenders = []
68
+ @@exception_handlers = []
73
69
 
74
- def appenders(*fn)
75
- @@appenders = fn.flatten
76
- end
70
+ def appenders(*fn)
71
+ @@appenders = fn.flatten
72
+ end
77
73
 
78
- def exception_handlers(*fn)
79
- @@exception_handlers = fn.flatten
80
- end
74
+ def exception_handlers(*fn)
75
+ @@exception_handlers = fn.flatten
76
+ end
81
77
 
82
- def append(entry)
83
- @@appenders.each{|fn| fn.call(entry) }
84
- end
78
+ def _handle_exception(entry)
79
+ @@exception_handlers.each{|fn| fn.call(entry) }
80
+ end
85
81
 
86
- def handle_exception(ex, scope = nil, context = {})
87
- @@exception_handlers.each{|fn| fn.call(ex, scope, context) }
88
- end
82
+ def _append(entry)
83
+ @@appenders.each{|fn| fn.call(entry) }
84
+ end
89
85
 
90
- def reset!
91
- @@appenders = [IoAppender.new]
92
- @@exception_handlers = [StderrExceptionHanlder.new]
93
- end
86
+ def _reset!
87
+ appenders IoAppender.new
88
+ exception_handlers StderrExceptionHanlder.new
94
89
  end
95
- end
90
+ end ; end
91
+
92
+ App._reset!
96
93
  end
@@ -7,7 +7,11 @@ module Logfoo
7
7
  end
8
8
 
9
9
  def call(entry)
10
- @io.write @formatter.call(entry)
10
+ write @formatter.call(entry)
11
+ end
12
+
13
+ def write(body)
14
+ @io.write body
11
15
  @io.flush
12
16
  end
13
17
  end
@@ -41,14 +41,7 @@ module Logfoo
41
41
 
42
42
  entry =
43
43
  if message.is_a?(Exception)
44
- ExceptionEntry.new(
45
- level_id,
46
- Time.now,
47
- @scope,
48
- message,
49
- payload,
50
- Thread.current.object_id
51
- )
44
+ ExceptionEntry.build(@scope, message, payload, level: level_id)
52
45
  else
53
46
  Entry.new(
54
47
  level_id,
data/lib/logfoo/entry.rb CHANGED
@@ -4,7 +4,7 @@ module Logfoo
4
4
  {
5
5
  level: level || :info,
6
6
  time: time || Time.now,
7
- message: message,
7
+ msg: message,
8
8
  scope: scope,
9
9
  }.merge!(
10
10
  payload || {}
@@ -15,13 +15,26 @@ module Logfoo
15
15
  end
16
16
 
17
17
  ExceptionEntry = Struct.new(:level, :time, :scope, :exception, :payload, :thread) do
18
+ class << self
19
+ def build(scope, ex, payload = nil, options = {})
20
+ self.new(
21
+ options[:level],
22
+ Time.now,
23
+ scope,
24
+ ex,
25
+ payload,
26
+ Thread.current.object_id
27
+ )
28
+ end
29
+ end
30
+
18
31
  def to_h
19
32
  {
20
33
  level: level || :error,
21
34
  time: time || Time.now,
22
- message: exception.message,
35
+ msg: exception.message,
23
36
  scope: scope,
24
- exception: exception.class.to_s,
37
+ err: exception.class.to_s,
25
38
  }.merge!(
26
39
  payload || {}
27
40
  ).merge!(
@@ -1,13 +1,24 @@
1
1
  module Logfoo
2
2
 
3
3
  class StderrExceptionHanlder
4
+ BACKTRACE_LINE = "\t%s\n".freeze
5
+ EXCEPTION_LINE = "%s: %s\n".freeze
6
+
4
7
  def initialize(appender = nil)
5
8
  @appender = appender || IoAppender.new(STDERR)
6
9
  end
7
10
 
8
- def call(ex, scope, context)
9
- entry = ExceptionEntry.new(nil, nil, scope, ex, context)
10
- @appender.call(entry)
11
+ def call(entry)
12
+ @appender.write format(entry)
13
+ end
14
+
15
+ def format(entry)
16
+ values = []
17
+ values << (EXCEPTION_LINE % [entry.exception.class, entry.exception.message])
18
+ if entry.exception.backtrace.is_a?(Array)
19
+ values << entry.exception.backtrace.map{|l| BACKTRACE_LINE % l }.join
20
+ end
21
+ values.join
11
22
  end
12
23
  end
13
24
 
@@ -1,15 +1,15 @@
1
+ require 'time'
2
+
1
3
  module Logfoo
2
4
  class LogfmtFormatter
3
5
 
4
- UNESCAPED_STRING = /\A[\w\.\-\+\%\,\:\;\/]*\z/i
5
-
6
- IGNORED_FIELDS = [:time]
6
+ UNESCAPED_STRING = /\A[\w\.\-\+\%\,\:\;\/]*\z/i.freeze
7
+ IGNORED_KEYS = [:time]
8
+ FLOAT_FORMAT = '%0.4f'.freeze
7
9
 
8
10
  def call(entry)
9
11
  case entry
10
- when ExceptionEntry
11
- format_exception(entry)
12
- when Entry
12
+ when ExceptionEntry, Entry
13
13
  format_entry(entry)
14
14
  else
15
15
  entry.to_s
@@ -22,20 +22,9 @@ module Logfoo
22
22
  "#{format_hash(entry.to_h)}\n"
23
23
  end
24
24
 
25
- def format_exception(entry)
26
- values = ""
27
- values << "#{format_hash(entry.to_h)}\n"
28
- if entry.exception && entry.exception.backtrace.is_a?(Array)
29
- values <<
30
- "#{entry.exception.class}: #{entry.exception.message}\n" +
31
- entry.exception.backtrace.map{|l| "\t#{l}\n" }.join
32
- end
33
- values
34
- end
35
-
36
25
  def format_hash(attrs)
37
26
  attrs.inject([]) do |ac, (k,v)|
38
- if !IGNORED_FIELDS.include?(k) && !(v == nil || v == "")
27
+ if !IGNORED_KEYS.include?(k) && !(v == nil || v == "")
39
28
  new_value = sanitize(v)
40
29
  ac << "#{k}=#{new_value}"
41
30
  end
@@ -46,17 +35,19 @@ module Logfoo
46
35
  def sanitize(v)
47
36
  case v
48
37
  when ::Array
49
- may_quote v.join(",")
38
+ may_quote v.map{|i| i.to_s}.join(",")
50
39
  when ::Integer, ::Symbol
51
40
  v.to_s
52
41
  when ::Float
53
- "%0.4f" % v
54
- when ::TrueClass, ::FalseClass
55
- v ? "t" : "f"
56
- when Time
57
- quote v.utc.to_s
42
+ FLOAT_FORMAT % v
43
+ when ::TrueClass
44
+ :t
45
+ when ::FalseClass
46
+ :f
47
+ when ::Time
48
+ v.utc.iso8601
58
49
  else
59
- may_quote(v.to_s)
50
+ may_quote v.to_s
60
51
  end
61
52
  end
62
53
 
@@ -1,21 +1,23 @@
1
1
  module Logfoo
2
2
  class SimpleFormatter < LogfmtFormatter
3
3
 
4
+ FORMAT = "[%5s]: %s -%s%s".freeze
5
+
4
6
  private
5
7
 
6
8
  def format_hash(attrs)
7
9
  level = attrs.delete(:level)
8
- message = attrs.delete(:message)
10
+ message = attrs.delete(:msg)
9
11
  scope = attrs.delete(:scope)
10
12
 
11
- IGNORED_FIELDS.each do |f|
13
+ IGNORED_KEYS.each do |f|
12
14
  attrs.delete(f)
13
15
  end
14
16
 
15
17
  payload = super(attrs)
16
18
  payload = payload.empty? ? "" : " [#{payload}]"
17
19
  message = message.to_s.empty? ? "" : " #{message}"
18
- "[%5s]: #{scope} -#{message}#{payload}" % level.upcase
20
+ FORMAT % [level.upcase, scope, message, payload]
19
21
  end
20
22
  end
21
23
  end
@@ -0,0 +1,36 @@
1
+ require 'hutch'
2
+
3
+ module Logfoo ; module Hutch
4
+ class ErrorHandler
5
+ attr_reader :bunny_log, :hutch_log
6
+
7
+ class << self
8
+ attr_accessor :handler
9
+ end
10
+
11
+ ID = 'Hutch'.freeze
12
+
13
+ def initialize
14
+ @hutch_log = Logfoo.get_logger(ID)
15
+ @bunny_log = Logfoo.get_logger('Bunny')
16
+ @bunny_log.level = Logfoo::WARN
17
+ end
18
+
19
+ def handle(message_id, payload, consumer, ex)
20
+ entry = Context.build(
21
+ ID,
22
+ ex,
23
+ payload: payload,
24
+ consumer: consumer,
25
+ message_id: message_id
26
+ )
27
+ App.instance.append(entry)
28
+ end
29
+ end
30
+ end ; end
31
+
32
+ Logfoo::Hutch::ErrorHandler.handler = Logfoo::Hutch::ErrorHandler.new
33
+
34
+ Hutch::Logging.logger = Logfoo::Hutch::ErrorHandler.handler.hutch_log
35
+ Hutch::Config.set(:error_handlers, [Logfoo::Hutch::ErrorHandler.handler])
36
+ Hutch::Config.set(:client_logger, Logfoo::Hutch::ErrorHandler.handler.bunny_log)
@@ -0,0 +1,58 @@
1
+ module Logfoo::Rack
2
+
3
+ class Err
4
+
5
+ TEXT_PLAIN = 'text/plain'.freeze
6
+ CLEAN_RE = /\A(rack|puma)\./.freeze
7
+
8
+ FRAMEWORK_ERRORS = %w{ action_dispatch.exception sinatra.error }.freeze
9
+
10
+ INTERNAL_SERVER_ERROR = [
11
+ 500,
12
+ {
13
+ R::CONTENT_TYPE => TEXT_PLAIN,
14
+ R::CONTENT_LENGTH => Rack::Utils::HTTP_STATUS_CODES[500].bytesize
15
+ },
16
+ [Rack::Utils::HTTP_STATUS_CODES[500]],
17
+ ].freeze
18
+
19
+ def initialize(app, log = nil)
20
+ @app = app
21
+ end
22
+
23
+ def call(env)
24
+ response = @app.call(env)
25
+
26
+ if framework_error = FRAMEWORK_ERRORS.find { |k| env[k] }
27
+ append(framework_error, env)
28
+ end
29
+
30
+ response
31
+ rescue Exception => e
32
+ append(e, env)
33
+ INTERNAL_SERVER_ERROR
34
+ end
35
+
36
+ private
37
+
38
+ def append(e, env)
39
+ env = clean_env(env)
40
+ entry = Logfoo::ExceptionEntry.build(LOGGER_NAME, e, env)
41
+ Logfoo::App.instance.append(entry)
42
+ end
43
+
44
+ def clean_env(env)
45
+ env.inject({}) do |ac, (key, value) |
46
+ case
47
+ when key =~ CLEAN_RE
48
+ ac
49
+ else
50
+ ac.merge!(key => value)
51
+ end
52
+ end
53
+ end
54
+
55
+ end
56
+ end
57
+
58
+
@@ -0,0 +1,52 @@
1
+ module Logfoo::Rack
2
+
3
+ class Log
4
+
5
+ IGNORED = %w{ /health /_ping }.freeze
6
+ HTTP_X_FORWARDED_FOR = 'HTTP_X_FORWARDED_FOR'.freeze
7
+ REMOTE_ADDR = 'REMOTE_ADDR'.freeze
8
+
9
+ def initialize(app, log = nil)
10
+ @log = log || Logfoo.get_logger(LOGGER_NAME)
11
+ @app = app
12
+ end
13
+
14
+ def call(env)
15
+ began_at = Time.now
16
+ status, header, body = @app.call(env)
17
+ header = R::Utils::HeaderHash.new(header)
18
+ body = R::BodyProxy.new(body) { log(env, status, header, began_at, body) }
19
+ [status, header, body]
20
+ end
21
+
22
+ private
23
+
24
+ def log(env, status, header, began_at, body)
25
+ return if ignored?(status, env)
26
+
27
+ now = Time.now
28
+ addr = (env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR]).to_s.split(", ").first
29
+ method = env[R::REQUEST_METHOD]
30
+ path = env[R::PATH_INFO]
31
+
32
+ payload = {
33
+ method: method,
34
+ path: path,
35
+ query: env[R::QUERY_STRING],
36
+ status: status.to_s[0..3],
37
+ len: header[R::CONTENT_LENGTH] || 0,
38
+ addr: addr,
39
+ duration: now - began_at
40
+ }
41
+
42
+ @log.info [method, path].join(" "), payload
43
+ end
44
+
45
+ def ignored?(status, env)
46
+ status > 199 && status < 299 && IGNORED.include?(env[R::PATH_INFO])
47
+ end
48
+
49
+ end
50
+ end
51
+
52
+
@@ -0,0 +1,9 @@
1
+ require 'rack'
2
+
3
+ module Logfoo ; module Rack
4
+ R = ::Rack
5
+ LOGGER_NAME = 'Rack'.freeze
6
+ end ; end
7
+
8
+ require File.expand_path("../rack/err", __FILE__)
9
+ require File.expand_path("../rack/log", __FILE__)
@@ -1,3 +1,3 @@
1
1
  module Logfoo
2
- VERSION = '0.0.7'
2
+ VERSION = '0.0.8'
3
3
  end
data/lib/logfoo.rb CHANGED
@@ -8,18 +8,17 @@ module Logfoo
8
8
 
9
9
  LEVELS = ['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'].freeze
10
10
 
11
- def self.get_logger(scope, context = nil)
12
- App.instance.start unless App.instance.started?
11
+ autoload :Rack, File.expand_path("../logfoo/integrations/rack", __FILE__)
12
+
13
+ extend self
14
+
15
+ def get_logger(scope, context = nil)
13
16
  Context.new(App.instance, scope.to_s, context)
14
17
  end
15
18
 
16
- def self.stop
19
+ def stop
17
20
  App.instance.stop
18
21
  end
19
-
20
- def self.handle_exception(*args)
21
- App.handle_exception(*args)
22
- end
23
22
  end
24
23
 
25
24
  %w{
@@ -30,9 +29,8 @@ end
30
29
  exception_handlers/stderr_exception_handler
31
30
  app
32
31
  context
33
- middlewares/err_middleware
34
- middlewares/log_middleware
35
- middlewares/hutch_error_handler
36
32
  }.each do |f|
37
33
  require File.expand_path("../logfoo/#{f}", __FILE__)
38
34
  end
35
+
36
+ at_exit { Logfoo.stop }
data/logfoo.gemspec CHANGED
@@ -28,7 +28,8 @@ Gem::Specification.new do |spec|
28
28
  spec.require_paths = ["lib"]
29
29
 
30
30
  spec.add_development_dependency "bundler", "~> 1.11"
31
- spec.add_development_dependency "rake", "~> 10.0"
31
+ spec.add_development_dependency "rake", "~> 10.0"
32
+ spec.add_development_dependency 'rack-test'
32
33
  spec.add_development_dependency "minitest"
33
34
  spec.add_development_dependency "minitest-reporters"
34
35
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logfoo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Galinsky
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-02-17 00:00:00.000000000 Z
11
+ date: 2016-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rack-test
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: minitest
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -89,9 +103,10 @@ files:
89
103
  - lib/logfoo/exception_handlers/stderr_exception_handler.rb
90
104
  - lib/logfoo/formatters/logfmt_formatter.rb
91
105
  - lib/logfoo/formatters/simple_formatter.rb
92
- - lib/logfoo/middlewares/err_middleware.rb
93
- - lib/logfoo/middlewares/hutch_error_handler.rb
94
- - lib/logfoo/middlewares/log_middleware.rb
106
+ - lib/logfoo/integrations/hutch.rb
107
+ - lib/logfoo/integrations/rack.rb
108
+ - lib/logfoo/integrations/rack/err.rb
109
+ - lib/logfoo/integrations/rack/log.rb
95
110
  - lib/logfoo/version.rb
96
111
  - logfoo.gemspec
97
112
  homepage: https://github.com/dmexe/logfoo
@@ -1,48 +0,0 @@
1
- require 'rack'
2
-
3
- module Logfoo
4
- class ErrMiddleware
5
-
6
- def initialize(app, log = nil)
7
- @log = log || Logfoo.get_logger('Rack')
8
- @app = app
9
- end
10
-
11
- def call(env)
12
- @app.call(env)
13
- rescue Exception => e
14
- @log.error(e, clean_env(env))
15
- body = dump_exception(e)
16
- [
17
- 500,
18
- {
19
- Rack::CONTENT_TYPE => 'text/plain',
20
- Rack::CONTENT_LENGTH => Rack::Utils.bytesize(body).to_s,
21
- },
22
- [body],
23
- ]
24
- end
25
-
26
- private
27
-
28
- def clean_env(env)
29
- env.inject({}) do |ac, pair|
30
- case
31
- when pair[0] =~ /\A(rack|puma)\./
32
- ac
33
- else
34
- ac.merge! pair[0] => pair[1]
35
- end
36
- end
37
- end
38
-
39
- def dump_exception(exception)
40
- string = "#{exception.class}: #{exception.message}\n"
41
- string << exception.backtrace.map { |l| "\t#{l}" }.join("\n")
42
- string
43
- end
44
-
45
- end
46
- end
47
-
48
-
@@ -1,13 +0,0 @@
1
- module Logfoo
2
- class HutchErrorHandler
3
- def handle(message_id, payload, consumer, ex)
4
- Logfoo.handle_exception(
5
- ex,
6
- "Hutch",
7
- payload: payload,
8
- consumer: consumer,
9
- message_id: message_id
10
- )
11
- end
12
- end
13
- end
@@ -1,54 +0,0 @@
1
- require 'rack'
2
-
3
- module Logfoo
4
-
5
- class LogMiddleware
6
- FORMAT = %{%s "%s %s%s %s" %d %s %0.4f}
7
- IGNORED = %w{ /health /_ping }
8
-
9
- def initialize(app, log = nil)
10
- @log = log || Logfoo.get_logger('Rack')
11
- @app = app
12
- end
13
-
14
- def call(env)
15
- began_at = Time.now
16
- status, header, body = @app.call(env)
17
- header = Rack::Utils::HeaderHash.new(header)
18
- body = Rack::BodyProxy.new(body) { log(env, status, header, began_at) }
19
- [status, header, body]
20
- end
21
-
22
- private
23
-
24
- def log(env, status, header, began_at)
25
- return if ignored?(status, env)
26
-
27
- now = Time.now
28
- length = extract_content_length(header)
29
- peer = env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"]
30
-
31
- payload = {
32
- method: env[Rack::REQUEST_METHOD],
33
- path: env[Rack::PATH_INFO],
34
- query: env[Rack::QUERY_STRING],
35
- status: status.to_s[0..3],
36
- len: length,
37
- peer: peer,
38
- duration: now - began_at
39
- }
40
-
41
- @log.info "request", payload
42
- end
43
-
44
- def extract_content_length(headers)
45
- headers[Rack::CONTENT_LENGTH] || 0
46
- end
47
-
48
- def ignored?(status, env)
49
- status > 199 && status < 299 && IGNORED.include?(env[Rack::PATH_INFO])
50
- end
51
- end
52
- end
53
-
54
-