loggerator 0.0.2 → 0.1.1

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: 147fb6d7ba2b85f8916e9b8e5e96ac088baa8fa2
4
- data.tar.gz: 699170130066ddbc05ba5ba8f33e1bfccfc2ddda
3
+ metadata.gz: 9e9a65b92ed9e08a20614a02d8b8abf5ea970ef1
4
+ data.tar.gz: 783ea749f4dfe30ffa92af44b8f9f95e09d79e70
5
5
  SHA512:
6
- metadata.gz: e85021a1d1bc361b85a764e8ec65c9ec097eeee232f77ff05cc7ad1f3850afef012dee300b16b2cbae99e9a1aca90edeb03c810b424dd0e27ddea785cd298daf
7
- data.tar.gz: 61852b4aada68c3c5c931876d310a777092ee3809fcc424ae7f555667345e8149065548d2fe1f358e0daf0f3d8170af7320c56f346a8e5d2340ab7da05776bc6
6
+ metadata.gz: 4b5565c2404a379837a6c9a429c167cc66f42179272b2af74f555a75e1c989e2c575a15227cc3b8b2e34b305557e79b87eacef59495112c780550834c4585f70
7
+ data.tar.gz: 897661dec2f7d5ba388aee5002585ddf17481ce0cf230934bcbb726d793b9cf78cb5e35de117a63e3945728e912abab1f9ab75092994f17cbbb525587206daad
@@ -1,4 +1,34 @@
1
- Loggerator::Log.default_context = { app: "<%= app_name %>" }
1
+ Loggerator.config do |config|
2
+ # Set loggerator's default context. These are the key/value pairs
3
+ # defining your application, which are prepended to every log line.
4
+ config.default_context = { app: "<%= app_name %>" }
2
5
  <% if defined?(Loggerator::Metrics) -%>
3
- Loggerator::Metrics.name = "<%= app_name %>"
6
+
7
+ # Set loggerator's metrics name. This is the name to be included as
8
+ # part of the metric key when emitting metrics.
9
+ config.metrics_app_name = "<%= app_name %>"
4
10
  <% end -%>
11
+ <% if defined?(Loggerator::Railtie) -%>
12
+
13
+ # Requiring 'loggerator/rails' automatically overrides Rails' log subscribers
14
+ # for controller handling with it's own log subscriber.
15
+ #
16
+ # In case you may need to disable this functionality, the following is
17
+ # a simple method for turning this off, causing the default logging to be
18
+ # modified.
19
+ #
20
+ # Additionally, the line below could be added only to config/environments/development.rb
21
+ # to disable Loggerator's controller logging in development only.
22
+ #
23
+ #config.rails_default_subscribers = true
24
+ <% end -%>
25
+ end
26
+
27
+ # Beyond the above configuration method, you can also set loggerator's config via
28
+ # a hash.
29
+ #
30
+ #Loggerator.config = { default_context: { app: "<%= app_name %>" } }
31
+ #
32
+ # Or parsed from a yaml file.
33
+ #
34
+ #Loggerator.config = YAML.load_file(File.expand_path("../../loggerator.yml", __FILE__))
@@ -0,0 +1,22 @@
1
+ module Loggerator
2
+ class Configuration
3
+ attr_accessor :default_context, :metrics_app_name, :rails_default_subscribers, :stdout, :stderr
4
+
5
+ def initialize(h = {})
6
+ @default_context = h[:default_context] || {}
7
+ @rails_default_subscribers = h[:rails_default_subscribers] || false
8
+ @metrics_app_name = h[:metrics_app_name] || "loggerator"
9
+
10
+ @stdout = h[:stdout] || $stdout
11
+ @stderr = h[:stderr] || $stderr
12
+ end
13
+
14
+ def to_h
15
+ {
16
+ default_context: default_context,
17
+ metrics_app_name: metrics_app_name,
18
+ rails_default_subscribers: rails_default_subscribers
19
+ }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,94 @@
1
+ module Loggerator
2
+ # Don't expose internals into included modules so name-collisions are reduced
3
+ module Log
4
+ extend self
5
+
6
+ def local_context
7
+ RequestStore.store[:local_context] ||= {}
8
+ end
9
+
10
+ def local_context=(h)
11
+ RequestStore.store[:local_context] = h
12
+ end
13
+
14
+ def stdout=(stream)
15
+ Loggerator.config.stdout = stream
16
+ end
17
+
18
+ def stdout
19
+ Loggerator.config.stdout
20
+ end
21
+
22
+ def stderr=(stream)
23
+ Loggerator.config.stderr = stream
24
+ end
25
+
26
+ def stderr
27
+ Loggerator.config.stderr
28
+ end
29
+
30
+ def contexts(data)
31
+ Loggerator.config.default_context.merge(request_context.merge(local_context.merge(data)))
32
+ end
33
+
34
+ def to_stream(stream, data, &block)
35
+ unless block
36
+ str = unparse(data)
37
+ stream.print(str + "\n")
38
+ else
39
+ data = data.dup
40
+ start = Time.now
41
+ to_stream(stream, data.merge(at: 'start'))
42
+ begin
43
+ res = yield
44
+
45
+ to_stream(stream, data.merge(
46
+ at: 'finish', elapsed: (Time.now - start).to_f))
47
+ res
48
+ rescue
49
+ to_stream(stream, data.merge(
50
+ at: 'exception', elapsed: (Time.now - start).to_f))
51
+ raise $!
52
+ end
53
+ end
54
+ end
55
+
56
+ private
57
+ def request_context
58
+ RequestStore.store[:request_context] || {}
59
+ end
60
+
61
+ def unparse(attrs)
62
+ attrs.map { |k, v| unparse_pair(k, v) }.compact.join(" ")
63
+ end
64
+
65
+ def unparse_pair(k, v)
66
+ v = v.call if v.is_a?(Proc)
67
+ # only quote strings if they include whitespace
68
+ if v == nil
69
+ nil
70
+ elsif v == true
71
+ k
72
+ elsif v.is_a?(Float)
73
+ "#{k}=#{format("%.3f", v)}"
74
+ elsif v.is_a?(String) && v =~ /\s/
75
+ quote_string(k, v)
76
+ elsif v.is_a?(Time)
77
+ "#{k}=#{v.iso8601}"
78
+ else
79
+ "#{k}=#{v}"
80
+ end
81
+ end
82
+
83
+ def quote_string(k, v)
84
+ # try to find a quote style that fits
85
+ if !v.include?('"')
86
+ %{#{k}="#{v}"}
87
+ elsif !v.include?("'")
88
+ %{#{k}='#{v}'}
89
+ else
90
+ %{#{k}="#{v.gsub(/"/, '\\"')}"}
91
+ end
92
+ end
93
+ end
94
+ end
@@ -1,22 +1,35 @@
1
- require_relative 'request_store'
2
- require_relative 'middleware'
1
+ require "loggerator/log"
2
+ require "loggerator/configuration"
3
+ require "loggerator/request_store"
4
+ require "loggerator/middleware"
3
5
 
4
6
  module Loggerator
5
-
6
7
  def self.included(mod)
7
8
  mod.extend self
8
9
  end
9
10
 
11
+ def self.config
12
+ @config ||= Configuration.new
13
+
14
+ return @config unless block_given?
15
+
16
+ yield(@config)
17
+ end
18
+
19
+ def self.config=(cfg)
20
+ @config = Configuration.new(cfg)
21
+ end
22
+
10
23
  def log(data, &block)
11
24
  Log.to_stream(Log.stdout, Log.contexts(data), &block)
12
25
  end
13
26
 
14
- def log_error(e=$!, data = {})
15
- exception_id = e.object_id
27
+ def log_error(error, data = {})
28
+ exception_id = error.object_id
16
29
 
17
30
  # Log backtrace in reverse order for easier digestion.
18
- if e.backtrace
19
- e.backtrace.reverse.each do |backtrace|
31
+ if error.backtrace
32
+ error.backtrace.reverse.each do |backtrace|
20
33
  Log.to_stream(Log.stderr, Log.contexts(
21
34
  exception_id: exception_id,
22
35
  backtrace: backtrace
@@ -28,12 +41,12 @@ module Loggerator
28
41
  # a log trace as possible
29
42
  data.merge!(
30
43
  exception: true,
31
- class: e.class.name,
32
- message: e.message,
44
+ class: error.class.name,
45
+ message: error.message,
33
46
  exception_id: exception_id
34
47
  )
35
48
 
36
- data[:status] = e.status if e.respond_to?(:status)
49
+ data[:status] = error.status if error.respond_to?(:status)
37
50
 
38
51
  Log.to_stream(Log.stderr, Log.contexts(data))
39
52
  end
@@ -46,105 +59,4 @@ module Loggerator
46
59
  Log.local_context = old
47
60
  res
48
61
  end
49
-
50
- # Don't expose internals into included modules so name-collisions are reduced
51
- module Log
52
- extend self
53
-
54
- def default_context=(default_context)
55
- @@default_context = default_context
56
- end
57
-
58
- def default_context
59
- @@default_context ||= {}
60
- end
61
-
62
- def local_context
63
- RequestStore.store[:local_context] ||= {}
64
- end
65
-
66
- def local_context=(h)
67
- RequestStore.store[:local_context] = h
68
- end
69
-
70
- def stdout=(stream)
71
- @@stdout = stream
72
- end
73
-
74
- def stdout
75
- @@stdout ||= $stdout
76
- end
77
-
78
- def stderr=(stream)
79
- @@stderr = stream
80
- end
81
-
82
- def stderr
83
- @@stderr ||= $stderr
84
- end
85
-
86
- def contexts(data)
87
- default_context.merge(request_context.merge(local_context.merge(data)))
88
- end
89
-
90
- def to_stream(stream, data, &block)
91
- unless block
92
- str = unparse(data)
93
- stream.print(str + "\n")
94
- else
95
- data = data.dup
96
- start = Time.now
97
- to_stream(stream, data.merge(at: 'start'))
98
- begin
99
- res = yield
100
-
101
- to_stream(stream, data.merge(
102
- at: 'finish', elapsed: (Time.now - start).to_f))
103
- res
104
- rescue
105
- to_stream(stream, data.merge(
106
- at: 'exception', elapsed: (Time.now - start).to_f))
107
- raise $!
108
- end
109
- end
110
- end
111
-
112
- private
113
- def request_context
114
- RequestStore.store[:request_context] || {}
115
- end
116
-
117
- def unparse(attrs)
118
- attrs.map { |k, v| unparse_pair(k, v) }.compact.join(" ")
119
- end
120
-
121
- def unparse_pair(k, v)
122
- v = v.call if v.is_a?(Proc)
123
- # only quote strings if they include whitespace
124
- if v == nil
125
- nil
126
- elsif v == true
127
- k
128
- elsif v.is_a?(Float)
129
- "#{k}=#{format("%.3f", v)}"
130
- elsif v.is_a?(String) && v =~ /\s/
131
- quote_string(k, v)
132
- elsif v.is_a?(Time)
133
- "#{k}=#{v.iso8601}"
134
- else
135
- "#{k}=#{v}"
136
- end
137
- end
138
-
139
- def quote_string(k, v)
140
- # try to find a quote style that fits
141
- if !v.include?('"')
142
- %{#{k}="#{v}"}
143
- elsif !v.include?("'")
144
- %{#{k}='#{v}'}
145
- else
146
- %{#{k}="#{v.gsub(/"/, '\\"')}"}
147
- end
148
- end
149
- end
150
62
  end
@@ -1,34 +1,24 @@
1
- require_relative 'loggerator'
1
+ require "loggerator"
2
2
 
3
3
  module Loggerator
4
4
  module Metrics
5
5
  include Loggerator
6
6
  extend self
7
7
 
8
- @@metrics_name = 'loggerator'
9
-
10
- def name=(name)
11
- @@metrics_name = name
12
- end
13
-
14
- def name
15
- @@metrics_name
16
- end
17
-
18
8
  def count(key, value=1)
19
- log("count##{name}.#{key}" => value)
9
+ log("count##{Loggerator.config.metrics_app_name}.#{key}" => value)
20
10
  end
21
11
 
22
12
  def sample(key, value)
23
- log("sample##{name}.#{key}" => value)
13
+ log("sample##{Loggerator.config.metrics_app_name}.#{key}" => value)
24
14
  end
25
15
 
26
16
  def unique(key, value)
27
- log("unique##{name}.#{key}" => value)
17
+ log("unique##{Loggerator.config.metrics_app_name}.#{key}" => value)
28
18
  end
29
19
 
30
- def measure(key, value, units='s')
31
- log("measure##{name}.#{key}" => "#{value}#{units}")
20
+ def measure(key, value, units="s")
21
+ log("measure##{Loggerator.config.metrics_app_name}.#{key}" => "#{value}#{units}")
32
22
  end
33
23
  end
34
24
 
@@ -1,5 +1,5 @@
1
- require_relative 'middleware/request_store'
2
- require_relative 'middleware/request_id'
1
+ require "loggerator/middleware/request_store"
2
+ require "loggerator/middleware/request_id"
3
3
 
4
4
  module Loggerator
5
5
  module Middleware; end
@@ -1,4 +1,4 @@
1
- require_relative 'loggerator'
1
+ require "loggerator"
2
2
 
3
3
  module Loggerator
4
4
  module Namespace
@@ -1,22 +1,8 @@
1
- require_relative "loggerator"
1
+ require "loggerator"
2
+ require "loggerator/railtie/log_subscriber"
3
+ require "loggerator/railtie/helper"
4
+ require "loggerator/railtie/adapter"
2
5
 
3
6
  module Loggerator
4
- class Railtie < Rails::Railtie
5
-
6
- config.before_configuration do
7
- Rails.application.middleware.insert_after ActionDispatch::RequestId, Loggerator::Middleware::RequestStore
8
- Rails.application.middleware.swap ActionDispatch::RequestId, Loggerator::Middleware::RequestID
9
- end
10
-
11
- config.before_initialize do
12
- [ ActionView::Base,
13
- ActiveRecord::Base,
14
- ActionMailer::Base,
15
- ActionController::Base ].each do |c|
16
-
17
- c.include Loggerator
18
- end
19
- end
20
-
21
- end
7
+ module Railtie; end
22
8
  end
@@ -0,0 +1,24 @@
1
+ module Loggerator
2
+ module Railtie
3
+ class Adapter < ::Rails::Railtie
4
+ config.before_configuration do
5
+ Rails.application.middleware.insert_after ActionDispatch::RequestId, Loggerator::Middleware::RequestStore
6
+ Rails.application.middleware.swap ActionDispatch::RequestId, Loggerator::Middleware::RequestID
7
+ end
8
+
9
+ config.before_initialize do
10
+ [ ActionView::Base,
11
+ ActiveRecord::Base,
12
+ ActionMailer::Base,
13
+ ActionController::Base ].each do |c|
14
+
15
+ c.include Loggerator
16
+ end
17
+ end
18
+
19
+ config.after_initialize do
20
+ Loggerator::Railtie::Helper.setup(Rails.application)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,48 @@
1
+ module Loggerator
2
+ module Railtie
3
+ module Helper
4
+ # Implementation respectfully borrowed from:
5
+ # https://github.com/minefold/scrolls-rails
6
+ extend self
7
+
8
+ def setup(_app)
9
+ return unless subscribe?
10
+
11
+ detach_existing_subscribers
12
+ Loggerator::Railtie::LogSubscriber.attach_to(:action_controller)
13
+ end
14
+
15
+ def detach_existing_subscribers
16
+ ActiveSupport::LogSubscriber.log_subscribers.each do |subscriber|
17
+ case subscriber
18
+ when ActionView::LogSubscriber
19
+ unsubscribe(:action_view, subscriber)
20
+ when ActionController::LogSubscriber
21
+ unsubscribe(:action_controller, subscriber)
22
+ end
23
+ end
24
+ end
25
+
26
+ def subscribe?
27
+ !Loggerator.config.rails_default_subscribers
28
+ end
29
+
30
+ def unsubscribe(component, subscriber)
31
+ events = events_for_subscriber(subscriber)
32
+
33
+ events.each do |event|
34
+ notifier = ActiveSupport::Notifications.notifier
35
+ notifier.listeners_for("#{event}.#{component}").each do |listener|
36
+ if listener.instance_variable_get('@delegate') == subscriber
37
+ ActiveSupport::Notifications.unsubscribe(listener)
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ def events_for_subscriber(subscriber)
44
+ subscriber.public_methods(false).reject {|method| method.to_s == 'call' }
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,85 @@
1
+ # Implementation respectfully borrowed from:
2
+ # https://github.com/minefold/scrolls-rails
3
+ require "loggerator"
4
+ require "active_support/core_ext/class/attribute"
5
+ require "active_support/log_subscriber"
6
+
7
+ module Loggerator
8
+ module Railtie
9
+ class LogSubscriber < ActiveSupport::LogSubscriber
10
+ include Loggerator
11
+
12
+ FIELDS = [:method, :path, :format, :controller, :action, :status,
13
+ :error, :duration, :view, :db, :location].freeze
14
+
15
+ def process_action(event)
16
+ exception = event.payload[:exception]
17
+ if exception.present?
18
+ # In Rails 3.2.9 event.payload[:exception] was changed from an
19
+ # Exception object to an Array containing the e.class.name and
20
+ # e.message. Adding handling for this case here.
21
+ if exception.is_a?(Array)
22
+ exception_class_name, exception_message = exception
23
+ exception = exception_class_name.constantize.new(exception_message)
24
+ end
25
+
26
+ self.log_error(exception, status: 500)
27
+ else
28
+ self.log(extract_request_data_from_event(event))
29
+ end
30
+ end
31
+
32
+ def redirect_to(event)
33
+ Thread.current[:scrolls_rails_location] = event.payload[:location]
34
+ end
35
+
36
+ private
37
+
38
+ def extract_request_data_from_event(event)
39
+ data = extract_request(event.payload)
40
+ data[:status] = extract_status(event.payload)
41
+
42
+ data.merge!(runtimes(event))
43
+ data.merge!(location(event))
44
+ end
45
+
46
+ def extract_request(payload)
47
+ {
48
+ method: payload[:method],
49
+ path: payload[:path],
50
+ format: payload[:format],
51
+ controller: payload[:params]["controller"],
52
+ action: payload[:params]["action"]
53
+ }
54
+ end
55
+
56
+ def extract_status(payload)
57
+ if payload[:status]
58
+ payload[:status].to_i
59
+ else
60
+ 0
61
+ end
62
+ end
63
+
64
+ def runtimes(event)
65
+ { duration: event.duration,
66
+ view: event.payload[:view_runtime],
67
+ db: event.payload[:db_runtime]
68
+ }.inject({}) do |runtimes, (name, runtime)|
69
+ runtimes[name] = runtime.to_f.round(2) if runtime
70
+ runtimes
71
+ end
72
+ end
73
+
74
+ def location(_event)
75
+ if location = Thread.current[:scrolls_rails_location]
76
+ Thread.current[:scrolls_rails_location] = nil
77
+
78
+ { location: location }
79
+ else
80
+ {}
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,3 @@
1
+ test:
2
+ adapter: sqlite3
3
+ database: db/combustion_test.sqlite
@@ -0,0 +1,3 @@
1
+ Rails.application.routes.draw do
2
+ #
3
+ end
@@ -0,0 +1,3 @@
1
+ ActiveRecord::Schema.define do
2
+ #
3
+ end
@@ -0,0 +1 @@
1
+ *.log
File without changes
@@ -0,0 +1,64 @@
1
+ require_relative "../test_helper"
2
+ require "loggerator/metrics"
3
+
4
+ class TestLoggeratorMetrics < Minitest::Test
5
+ include Loggerator
6
+ include Loggerator::Metrics
7
+
8
+ def config
9
+ Loggerator.config
10
+ end
11
+
12
+ def test_name_equals
13
+ config.metrics_app_name = "test_name_equals"
14
+
15
+ assert_equal "test_name_equals", config.metrics_app_name
16
+ end
17
+
18
+ def test_name
19
+ config.metrics_app_name = "test_name"
20
+
21
+ assert_equal "test_name", self.name
22
+ end
23
+
24
+ def test_count
25
+ config.metrics_app_name = "test_count"
26
+ out, err = capture_subprocess_io do
27
+ self.count(:foo, 99)
28
+ end
29
+
30
+ assert_empty err
31
+ assert_match(/count#test_count\.foo=99/, out)
32
+ end
33
+
34
+ def test_sample
35
+ config.metrics_app_name = "test_sample"
36
+ out, err = capture_subprocess_io do
37
+ self.sample(:foo, :bar)
38
+ end
39
+
40
+ assert_empty err
41
+ assert_match(/sample#test_sample\.foo=bar/, out)
42
+ end
43
+
44
+ def test_unique
45
+ config.metrics_app_name = "test_unique"
46
+ out, err = capture_subprocess_io do
47
+ self.unique(:foo, :bar)
48
+ end
49
+
50
+ assert_empty err
51
+ assert_match(/unique#test_unique\.foo=bar/, out)
52
+ end
53
+
54
+ def test_measure
55
+ config.metrics_app_name = "test_measure"
56
+ out, err = capture_subprocess_io do
57
+ self.measure(:foo, 60, "ms")
58
+ end
59
+
60
+ assert_empty err
61
+ assert_match(/measure#test_measure\.foo=60ms/, out)
62
+ end
63
+ end
64
+
@@ -1,9 +1,4 @@
1
- require 'rack/test'
2
- require 'minitest/mock'
3
- require 'minitest/autorun'
4
- require 'logger'
5
-
6
- require_relative '../../../lib/loggerator'
1
+ require_relative "../../test_helper"
7
2
 
8
3
  class Loggerator::Middleware::TestRequestID < Minitest::Test
9
4
  include Rack::Test::Methods
@@ -13,21 +8,21 @@ class Loggerator::Middleware::TestRequestID < Minitest::Test
13
8
  use Rack::Lint
14
9
  use Loggerator::Middleware::RequestID
15
10
 
16
- run ->(env) { [ 200, { }, [ 'hi' ] ] }
11
+ run ->(env) { [ 200, { }, [ "hi" ] ] }
17
12
  end
18
13
  end
19
14
 
20
15
  def test_sets_request_id
21
- get '/'
16
+ get "/"
22
17
 
23
18
  assert_match ::Loggerator::Middleware::RequestID::UUID_PATTERN,
24
- last_request.env['REQUEST_ID']
19
+ last_request.env["REQUEST_ID"]
25
20
  end
26
21
 
27
22
  def test_sets_request_ids
28
- get '/'
23
+ get "/"
29
24
 
30
25
  assert_match ::Loggerator::Middleware::RequestID::UUID_PATTERN,
31
- last_request.env['REQUEST_IDS'].first
26
+ last_request.env["REQUEST_IDS"].first
32
27
  end
33
28
  end
@@ -1,9 +1,4 @@
1
- require 'rack/test'
2
- require 'minitest/mock'
3
- require 'minitest/autorun'
4
- require 'logger'
5
-
6
- require_relative '../../../lib/loggerator'
1
+ require_relative "../../test_helper"
7
2
 
8
3
  class Loggerator::Middleware::TestRequestStore < Minitest::Test
9
4
  include Rack::Test::Methods
@@ -13,14 +8,14 @@ class Loggerator::Middleware::TestRequestStore < Minitest::Test
13
8
  use Rack::Lint
14
9
  use Loggerator::Middleware::RequestStore
15
10
 
16
- run ->(env) { [ 200, { }, [ 'hi' ] ] }
11
+ run ->(env) { [ 200, { }, [ "hi" ] ] }
17
12
  end
18
13
  end
19
14
 
20
15
  def test_clears_the_store
21
- Thread.current[:request_store] = { something_added_before: 'bar' }
16
+ Thread.current[:request_store] = { something_added_before: "bar" }
22
17
 
23
- get '/'
18
+ get "/"
24
19
 
25
20
  assert_nil Thread.current[:request_store][:something_added_before]
26
21
  end
@@ -28,7 +23,7 @@ class Loggerator::Middleware::TestRequestStore < Minitest::Test
28
23
  def test_seeds_the_store
29
24
  Thread.current[:request_store] = {}
30
25
 
31
- get '/'
26
+ get "/"
32
27
 
33
28
  assert_equal Thread.current[:request_store], {
34
29
  request_id: nil,
@@ -0,0 +1,60 @@
1
+ require_relative "../test_helper"
2
+
3
+ class TestLoggeratorRails < Minitest::Test
4
+ def test_middleware_modifications
5
+ # This ensures that the middlewares list includes our middlewares and that
6
+ # the default request id handler has been replaced.
7
+ middlewares = Rails.application.middleware
8
+
9
+ assert middlewares.include?(Loggerator::Middleware::RequestStore)
10
+ assert middlewares.include?(Loggerator::Middleware::RequestID)
11
+ refute middlewares.include?(ActionDispatch::RequestId)
12
+ end
13
+
14
+ def test_loggerator_included
15
+ # This ensures that Loggerator has been included in each of the classes below
16
+ # by checking to ensure that it's included in each classes "ancestors" list.
17
+ [ ActionView::Base,
18
+ ActiveRecord::Base,
19
+ ActionMailer::Base,
20
+ ActionController::Base
21
+ ].each do |c|
22
+ assert c.ancestors.include?(Loggerator)
23
+ end
24
+ end
25
+
26
+ def test_log_subscriber_attached
27
+ # This sets subscribers to a unique list of all log subscribers. Our
28
+ # LogSubscriber class should be included in this list.
29
+ subscribers = \
30
+ ActiveSupport::Notifications.notifier
31
+ .instance_variable_get("@subscribers")
32
+ .map { |subscriber|
33
+ subscriber.instance_variable_get("@delegate").class
34
+ }.uniq
35
+
36
+ assert subscribers.include?(Loggerator::Railtie::LogSubscriber)
37
+ end
38
+
39
+ def test_detach_existing_subscribers
40
+ # This sets subscribed_classes to the unique list of classes contstants
41
+ # which are currently subscribed to either "process_action.action_controller"
42
+ # or "redirect_to.action_controller". Given that only our LogSubscriber
43
+ # should be subscribed to these two events, only our LogSubscriber should
44
+ # be in the resulting list.
45
+ subscribed_classes = \
46
+ ActiveSupport::Notifications.notifier
47
+ .instance_variable_get("@subscribers")
48
+ .map { |subscriber|
49
+ subscriber.instance_variable_get("@delegate")
50
+ }.select { |delegate|
51
+ patterns = delegate.instance_variable_get("@patterns")
52
+ patterns && (
53
+ patterns.include?("process_action.action_controller") ||
54
+ patterns.include?("redirect_to.action_controller")
55
+ )
56
+ }.map(&:class).uniq
57
+
58
+ assert_equal subscribed_classes, [Loggerator::Railtie::LogSubscriber]
59
+ end
60
+ end
@@ -0,0 +1,44 @@
1
+ require_relative "../../test_helper"
2
+
3
+ class FakeEvent
4
+ attr_accessor :payload
5
+ end
6
+
7
+ class LoggeratorRailtieLogSubscriber < Minitest::Test
8
+ def setup
9
+ @sub = Loggerator::Railtie::LogSubscriber.new
10
+ @evt = FakeEvent.new
11
+ end
12
+
13
+ def test_process_action_with_array
14
+ out, err = capture_subprocess_io do
15
+ @evt.payload = {
16
+ exception: ["Exception", "Test array"]
17
+ }
18
+
19
+ @sub.process_action(@evt)
20
+ end
21
+
22
+ assert_empty(out)
23
+ assert_match(
24
+ /status=500 exception class=Exception message=\"Test array\" exception_id=\d+\n$/,
25
+ err
26
+ )
27
+ end
28
+
29
+ def test_process_action_with_exception
30
+ out, err = capture_subprocess_io do
31
+ @evt.payload = {
32
+ exception: Exception.new("Test exception")
33
+ }
34
+
35
+ @sub.process_action(@evt)
36
+ end
37
+
38
+ assert_empty(out)
39
+ assert_match(
40
+ /status=500 exception class=Exception message=\"Test exception\" exception_id=\d+\n$/,
41
+ err
42
+ )
43
+ end
44
+ end
@@ -1,7 +1,4 @@
1
- require 'minitest/autorun'
2
- require 'logger'
3
-
4
- require_relative '../../lib/loggerator'
1
+ require_relative "../test_helper"
5
2
 
6
3
  class Loggerator::TestRequestStore < Minitest::Test
7
4
  def setup
@@ -9,21 +6,21 @@ class Loggerator::TestRequestStore < Minitest::Test
9
6
  Thread.current[:request_store] = {}
10
7
 
11
8
  @env = {
12
- 'REQUEST_ID' => 'abc',
13
- 'REQUEST_IDS' => %w[ abc def ]
9
+ "REQUEST_ID" => "abc",
10
+ "REQUEST_IDS" => %w[ abc def ]
14
11
  }
15
12
  end
16
13
 
17
14
  def test_seeds_request_id
18
15
  Loggerator::RequestStore.seed(@env)
19
16
 
20
- assert_equal 'abc,def', Loggerator::RequestStore.store[:request_id]
17
+ assert_equal "abc,def", Loggerator::RequestStore.store[:request_id]
21
18
  end
22
19
 
23
20
  def test_seeds_request_context
24
21
  Loggerator::RequestStore.seed(@env)
25
22
 
26
- assert_equal 'abc,def', Loggerator::RequestStore.store[:request_context][:request_id]
23
+ assert_equal "abc,def", Loggerator::RequestStore.store[:request_context][:request_id]
27
24
  end
28
25
 
29
26
  def test_is_cleared_by_clear!
@@ -1,16 +1,44 @@
1
- require 'minitest/autorun'
2
- require 'logger'
3
-
4
- require_relative '../lib/loggerator'
1
+ require_relative "test_helper"
5
2
 
6
3
  class TestLoggerator < Minitest::Test
7
4
  include Loggerator
5
+ include Loggerator::Log
8
6
 
9
7
  def setup
10
8
  # flush request store
11
9
  Thread.current[:request_store] = {}
12
10
 
13
- self.default_context = {}
11
+ Loggerator.config.default_context = {}
12
+ end
13
+
14
+ def test_config_from_block
15
+ Loggerator.config do |c|
16
+ c.default_context = { foo: :bar }
17
+ c.metrics_app_name = "foo_bar"
18
+ c.rails_default_subscribers = true
19
+ end
20
+
21
+ expected = {
22
+ default_context: { foo: :bar },
23
+ metrics_app_name: "foo_bar",
24
+ rails_default_subscribers: true
25
+ }
26
+
27
+ assert_equal expected, Loggerator.config.to_h
28
+ end
29
+
30
+ def test_config_from_hash
31
+ expected = {
32
+ default_context: { foo: :bar },
33
+ metrics_app_name: "foo_bar",
34
+ rails_default_subscribers: true
35
+ }
36
+
37
+ refute_equal expected, Loggerator.config.to_h
38
+
39
+ Loggerator.config = expected
40
+
41
+ assert_equal expected, Loggerator.config.to_h
14
42
  end
15
43
 
16
44
  def test_logs_in_structured_format
@@ -41,7 +69,7 @@ class TestLoggerator < Minitest::Test
41
69
 
42
70
  def test_merges_default_context_with_eq
43
71
  # testing both methods
44
- self.default_context = { app: 'my_app' }
72
+ Loggerator.config.default_context = { app: "my_app" }
45
73
 
46
74
  out, _ = capture_subprocess_io do
47
75
  log(foo: 'bar')
@@ -62,7 +90,7 @@ class TestLoggerator < Minitest::Test
62
90
 
63
91
  def test_log_context_merged_with_default_context
64
92
  out, _ = capture_subprocess_io do
65
- self.default_context = { app: 'my_app' }
93
+ Loggerator.config.default_context = { app: 'my_app' }
66
94
  self.log_context(foo: 'bar') do
67
95
  log(bah: 'boo')
68
96
  end
@@ -70,4 +98,29 @@ class TestLoggerator < Minitest::Test
70
98
 
71
99
  assert_equal out, "app=my_app foo=bar bah=boo\n"
72
100
  end
101
+
102
+ def test_log_error
103
+ _, err = capture_subprocess_io do
104
+ begin
105
+ raise "an error"
106
+ rescue => ex
107
+ self.log_error(ex, foo: :bar_bah_boo)
108
+ end
109
+ end
110
+
111
+ assert_match(/message="an error"/, err)
112
+ assert_match(/foo=bar_bah_boo/, err)
113
+ end
114
+
115
+ def test_log_error_without_data
116
+ _, err = capture_subprocess_io do
117
+ begin
118
+ raise "another error"
119
+ rescue => ex
120
+ self.log_error(ex)
121
+ end
122
+ end
123
+
124
+ assert_match(/message="another error"/, err)
125
+ end
73
126
  end
@@ -0,0 +1,21 @@
1
+ # like running `ruby -W0`
2
+ $VERBOSE = nil
3
+
4
+ require "rack/test"
5
+ require "minitest/mock"
6
+ require "minitest/autorun"
7
+ require "pry"
8
+ require "pp"
9
+
10
+ require "logger"
11
+
12
+ require "combustion"
13
+ Combustion.path = "test/internal"
14
+ Combustion.initialize! :all
15
+
16
+ # Unfreeze rails middleware
17
+ Rails.application.middleware.instance_variable_set(:@middlewares,
18
+ Rails.application.middleware.instance_variable_get(:@middlewares).dup)
19
+
20
+ require "loggerator"
21
+ require "loggerator/rails"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loggerator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Mervine
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-10-12 00:00:00.000000000 Z
12
+ date: 2017-03-03 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Simple web application extension for logging, following the 12factor
15
15
  pattern.
@@ -23,6 +23,8 @@ files:
23
23
  - lib/generators/loggerator/log_generator.rb
24
24
  - lib/generators/templates/log.rb.erb
25
25
  - lib/loggerator.rb
26
+ - lib/loggerator/configuration.rb
27
+ - lib/loggerator/log.rb
26
28
  - lib/loggerator/loggerator.rb
27
29
  - lib/loggerator/metrics.rb
28
30
  - lib/loggerator/middleware.rb
@@ -30,12 +32,24 @@ files:
30
32
  - lib/loggerator/middleware/request_store.rb
31
33
  - lib/loggerator/namespace.rb
32
34
  - lib/loggerator/rails.rb
35
+ - lib/loggerator/railtie/adapter.rb
36
+ - lib/loggerator/railtie/helper.rb
37
+ - lib/loggerator/railtie/log_subscriber.rb
33
38
  - lib/loggerator/request_store.rb
34
39
  - lib/loggerator/test.rb
40
+ - test/internal/config/database.yml
41
+ - test/internal/config/routes.rb
42
+ - test/internal/db/schema.rb
43
+ - test/internal/log/.gitignore
44
+ - test/internal/public/favicon.ico
45
+ - test/loggerator/metrics_test.rb
35
46
  - test/loggerator/middleware/request_id_test.rb
36
47
  - test/loggerator/middleware/request_store_test.rb
48
+ - test/loggerator/rails_test.rb
49
+ - test/loggerator/railtie/log_subscriber_test.rb
37
50
  - test/loggerator/request_store_test.rb
38
51
  - test/loggerator_test.rb
52
+ - test/test_helper.rb
39
53
  homepage: https://github.com/heroku/loggerator
40
54
  licenses:
41
55
  - MIT
@@ -61,7 +75,16 @@ signing_key:
61
75
  specification_version: 4
62
76
  summary: 'loggerator: A Log Helper'
63
77
  test_files:
78
+ - test/internal/config/database.yml
79
+ - test/internal/config/routes.rb
80
+ - test/internal/db/schema.rb
81
+ - test/internal/log/.gitignore
82
+ - test/internal/public/favicon.ico
83
+ - test/loggerator/metrics_test.rb
64
84
  - test/loggerator/middleware/request_id_test.rb
65
85
  - test/loggerator/middleware/request_store_test.rb
86
+ - test/loggerator/rails_test.rb
87
+ - test/loggerator/railtie/log_subscriber_test.rb
66
88
  - test/loggerator/request_store_test.rb
67
89
  - test/loggerator_test.rb
90
+ - test/test_helper.rb