timberio 1.0.0.beta1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (157) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -2
  3. data/.rspec +2 -0
  4. data/.yardopts +6 -0
  5. data/Appraisals +4 -0
  6. data/Gemfile +9 -1
  7. data/LICENSE.md +15 -0
  8. data/README.md +170 -8
  9. data/circle.yml +11 -8
  10. data/lib/timber/config.rb +11 -18
  11. data/lib/timber/context.rb +9 -68
  12. data/lib/timber/contexts/custom.rb +27 -0
  13. data/lib/timber/contexts/http.rb +28 -0
  14. data/lib/timber/contexts/organization.rb +24 -22
  15. data/lib/timber/contexts/user.rb +25 -28
  16. data/lib/timber/contexts.rb +7 -20
  17. data/lib/timber/current_context.rb +26 -41
  18. data/lib/timber/event.rb +13 -0
  19. data/lib/timber/events/controller_call.rb +40 -0
  20. data/lib/timber/events/custom.rb +42 -0
  21. data/lib/timber/events/exception.rb +35 -0
  22. data/lib/timber/events/http_request.rb +50 -0
  23. data/lib/timber/events/http_response.rb +36 -0
  24. data/lib/timber/events/sql_query.rb +26 -0
  25. data/lib/timber/events/template_render.rb +26 -0
  26. data/lib/timber/events.rb +37 -0
  27. data/lib/timber/frameworks/rails.rb +3 -14
  28. data/lib/timber/frameworks.rb +8 -10
  29. data/lib/timber/log_devices/http.rb +72 -13
  30. data/lib/timber/log_devices.rb +8 -4
  31. data/lib/timber/log_entry.rb +59 -0
  32. data/lib/timber/logger.rb +136 -13
  33. data/lib/timber/probe.rb +6 -4
  34. data/lib/timber/probes/action_controller_log_subscriber/log_subscriber.rb +64 -0
  35. data/lib/timber/probes/action_controller_log_subscriber.rb +20 -0
  36. data/lib/timber/probes/action_dispatch_debug_exceptions.rb +59 -35
  37. data/lib/timber/probes/action_view_log_subscriber/log_subscriber.rb +62 -0
  38. data/lib/timber/probes/action_view_log_subscriber.rb +20 -0
  39. data/lib/timber/probes/active_record_log_subscriber/log_subscriber.rb +72 -0
  40. data/lib/timber/probes/active_record_log_subscriber.rb +20 -0
  41. data/lib/timber/probes/rack_http_context.rb +51 -0
  42. data/lib/timber/probes/rails_rack_logger.rb +76 -0
  43. data/lib/timber/probes.rb +13 -16
  44. data/lib/timber/util/active_support_log_subscriber.rb +33 -0
  45. data/lib/timber/util/hash.rb +14 -0
  46. data/lib/timber/util.rb +8 -0
  47. data/lib/timber/version.rb +2 -2
  48. data/lib/timber.rb +6 -11
  49. data/spec/spec_helper.rb +7 -4
  50. data/spec/support/rails.rb +9 -5
  51. data/spec/support/timber.rb +1 -20
  52. data/spec/timber/events_spec.rb +55 -0
  53. data/spec/timber/log_devices/http_spec.rb +62 -0
  54. data/spec/timber/logger_spec.rb +68 -0
  55. data/spec/timber/probes/action_controller_log_subscriber_spec.rb +70 -0
  56. data/spec/timber/probes/action_dispatch_debug_exceptions_spec.rb +24 -18
  57. data/spec/timber/probes/action_view_log_subscriber_spec.rb +61 -0
  58. data/spec/timber/probes/active_record_log_subscriber_spec.rb +49 -0
  59. data/spec/timber/probes/rack_http_context_spec.rb +54 -0
  60. data/spec/timber/probes/rails_rack_logger_spec.rb +46 -0
  61. data/timberio.gemspec +2 -0
  62. metadata +62 -123
  63. data/.codeclimate.yml +0 -34
  64. data/LICENSE +0 -38
  65. data/Rakefile +0 -4
  66. data/TODO +0 -4
  67. data/benchmark/README.md +0 -26
  68. data/benchmark/rails_request.rb +0 -68
  69. data/benchmark/support/rails.rb +0 -69
  70. data/docs/installation/rails_on_heroku.md +0 -31
  71. data/docs/installation/rails_over_http.md +0 -22
  72. data/gemfiles/rails_3.0.X.gemfile +0 -25
  73. data/gemfiles/rails_3.1.X.gemfile +0 -25
  74. data/gemfiles/rails_3.2.X.gemfile +0 -25
  75. data/gemfiles/rails_4.0.X.gemfile +0 -26
  76. data/gemfiles/rails_4.1.X.gemfile +0 -26
  77. data/gemfiles/rails_4.2.X.gemfile +0 -26
  78. data/gemfiles/rails_5.0.X.gemfile +0 -26
  79. data/gemfiles/rails_edge.gemfile +0 -27
  80. data/lib/timber/api_settings.rb +0 -17
  81. data/lib/timber/bootstrap.rb +0 -45
  82. data/lib/timber/context_snapshot.rb +0 -64
  83. data/lib/timber/contexts/dynamic_values.rb +0 -59
  84. data/lib/timber/contexts/exception.rb +0 -40
  85. data/lib/timber/contexts/http_request.rb +0 -22
  86. data/lib/timber/contexts/http_requests/action_controller_specific.rb +0 -48
  87. data/lib/timber/contexts/http_requests/rack/params.rb +0 -26
  88. data/lib/timber/contexts/http_requests/rack.rb +0 -105
  89. data/lib/timber/contexts/http_response.rb +0 -19
  90. data/lib/timber/contexts/http_responses/action_controller.rb +0 -76
  91. data/lib/timber/contexts/logger.rb +0 -33
  92. data/lib/timber/contexts/organizations/action_controller.rb +0 -34
  93. data/lib/timber/contexts/server.rb +0 -21
  94. data/lib/timber/contexts/servers/heroku_specific.rb +0 -48
  95. data/lib/timber/contexts/sql_queries/active_record.rb +0 -30
  96. data/lib/timber/contexts/sql_queries/active_record_specific/binds.rb +0 -37
  97. data/lib/timber/contexts/sql_queries/active_record_specific.rb +0 -59
  98. data/lib/timber/contexts/sql_query.rb +0 -18
  99. data/lib/timber/contexts/template_render.rb +0 -17
  100. data/lib/timber/contexts/template_renders/action_view.rb +0 -29
  101. data/lib/timber/contexts/template_renders/action_view_specific.rb +0 -51
  102. data/lib/timber/contexts/users/action_controller.rb +0 -34
  103. data/lib/timber/current_line_indexes.rb +0 -35
  104. data/lib/timber/internal_logger.rb +0 -35
  105. data/lib/timber/log_device.rb +0 -40
  106. data/lib/timber/log_devices/heroku_logplex/hybrid_formatter.rb +0 -14
  107. data/lib/timber/log_devices/heroku_logplex.rb +0 -14
  108. data/lib/timber/log_devices/http/log_pile.rb +0 -86
  109. data/lib/timber/log_devices/http/log_truck/delivery.rb +0 -116
  110. data/lib/timber/log_devices/http/log_truck.rb +0 -87
  111. data/lib/timber/log_devices/io/formatter.rb +0 -46
  112. data/lib/timber/log_devices/io/hybrid_formatter.rb +0 -41
  113. data/lib/timber/log_devices/io/hybrid_hidden_formatter.rb +0 -36
  114. data/lib/timber/log_devices/io/json_formatter.rb +0 -11
  115. data/lib/timber/log_devices/io/logfmt_formatter.rb +0 -11
  116. data/lib/timber/log_devices/io.rb +0 -41
  117. data/lib/timber/log_line.rb +0 -33
  118. data/lib/timber/macros/compactor.rb +0 -16
  119. data/lib/timber/macros/date_formatter.rb +0 -9
  120. data/lib/timber/macros/deep_merger.rb +0 -11
  121. data/lib/timber/macros/logfmt_encoder.rb +0 -77
  122. data/lib/timber/macros.rb +0 -4
  123. data/lib/timber/patterns/delegated_singleton.rb +0 -21
  124. data/lib/timber/patterns/to_json.rb +0 -22
  125. data/lib/timber/patterns/to_logfmt.rb +0 -9
  126. data/lib/timber/patterns.rb +0 -3
  127. data/lib/timber/probes/action_controller_base.rb +0 -31
  128. data/lib/timber/probes/active_support_log_subscriber/action_controller.rb +0 -15
  129. data/lib/timber/probes/active_support_log_subscriber/action_view.rb +0 -26
  130. data/lib/timber/probes/active_support_log_subscriber/active_record.rb +0 -13
  131. data/lib/timber/probes/active_support_log_subscriber.rb +0 -62
  132. data/lib/timber/probes/heroku.rb +0 -30
  133. data/lib/timber/probes/logger.rb +0 -31
  134. data/lib/timber/probes/rack.rb +0 -36
  135. data/lib/timber/probes/server.rb +0 -18
  136. data/spec/timber/bootstrap_spec.rb +0 -31
  137. data/spec/timber/context_snapshot_spec.rb +0 -10
  138. data/spec/timber/context_spec.rb +0 -4
  139. data/spec/timber/contexts/exception_spec.rb +0 -34
  140. data/spec/timber/contexts/organizations/action_controller_spec.rb +0 -49
  141. data/spec/timber/contexts/users/action_controller_spec.rb +0 -65
  142. data/spec/timber/current_line_indexes_spec.rb +0 -40
  143. data/spec/timber/frameworks/rails_spec.rb +0 -9
  144. data/spec/timber/log_devices/heroku_logplex_spec.rb +0 -45
  145. data/spec/timber/log_devices/http/log_truck/delivery_spec.rb +0 -66
  146. data/spec/timber/log_devices/http/log_truck_spec.rb +0 -65
  147. data/spec/timber/log_devices/io/hybrid_hidden_formatter_spec.rb +0 -28
  148. data/spec/timber/log_line_spec.rb +0 -49
  149. data/spec/timber/macros/compactor_spec.rb +0 -19
  150. data/spec/timber/macros/logfmt_encoder_spec.rb +0 -89
  151. data/spec/timber/patterns/to_json_spec.rb +0 -40
  152. data/spec/timber/probes/action_controller_base_spec.rb +0 -43
  153. data/spec/timber/probes/action_controller_log_subscriber/action_controller_spec.rb +0 -35
  154. data/spec/timber/probes/action_controller_log_subscriber/action_view_spec.rb +0 -44
  155. data/spec/timber/probes/action_controller_log_subscriber/active_record_spec.rb +0 -26
  156. data/spec/timber/probes/logger_spec.rb +0 -20
  157. data/spec/timber/probes/rack_spec.rb +0 -26
@@ -0,0 +1,76 @@
1
+ module Timber
2
+ module Probes
3
+ # Responsible for automatically tracking the http request events for applications
4
+ # that use `Rack`.
5
+ class RailsRackLogger < Probe
6
+ module InstanceMethods
7
+ def self.included(klass)
8
+ klass.class_eval do
9
+ protected
10
+ if klass.method_defined?(:started_request_message)
11
+ def started_request_message(request)
12
+ http_request_event(request)
13
+ end
14
+ elsif klass.method_defined?(:before_dispatch)
15
+ def before_dispatch(env)
16
+ request = ActionDispatch::Request.new(env)
17
+ info do
18
+ http_request_event(request)
19
+ end
20
+ end
21
+ end
22
+
23
+ def http_request_event(request)
24
+ # No idea why rails 3.X returns a "/" :/
25
+ referrer = request.referer == "/" ? nil : request.referer
26
+ Events::HTTPRequest.new(
27
+ host: request.host,
28
+ method: request.request_method,
29
+ path: request.filtered_path,
30
+ port: request.port,
31
+ query_params: request.GET,
32
+ content_type: request.content_type,
33
+ remote_addr: request.ip,
34
+ referrer: referrer,
35
+ request_id: request_id(request.env),
36
+ user_agent: request.user_agent
37
+ )
38
+ end
39
+
40
+ def request_id(env)
41
+ env["X-Request-ID"] ||
42
+ env["X-Request-Id"] ||
43
+ env["action_dispatch.request_id"]
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ module BeforeDispatchInstanceMethods
50
+ def self.included(klass)
51
+ klass.class_eval do
52
+ protected
53
+ def before_dispatch(env)
54
+ request = ActionDispatch::Request.new(env)
55
+ path = request.filtered_path
56
+
57
+ info "\n\nStarted #{request.request_method} \"#{path}\" " \
58
+ "for #{request.ip} at #{Time.now.to_default_s}"
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ def initialize
65
+ require "rails/rack/logger"
66
+ rescue LoadError => e
67
+ raise RequirementNotMetError.new(e.message)
68
+ end
69
+
70
+ def insert!
71
+ return true if ::Rails::Rack::Logger.include?(InstanceMethods)
72
+ ::Rails::Rack::Logger.send(:include, InstanceMethods)
73
+ end
74
+ end
75
+ end
76
+ end
data/lib/timber/probes.rb CHANGED
@@ -1,24 +1,21 @@
1
- require "timber/probes/action_controller_base"
1
+ require "timber/probes/action_controller_log_subscriber"
2
2
  require "timber/probes/action_dispatch_debug_exceptions"
3
- require "timber/probes/active_support_log_subscriber"
4
- require "timber/probes/heroku"
5
- require "timber/probes/logger"
6
- require "timber/probes/rack"
7
- require "timber/probes/server"
3
+ require "timber/probes/action_view_log_subscriber"
4
+ require "timber/probes/active_record_log_subscriber"
5
+ require "timber/probes/rack_http_context"
6
+ require "timber/probes/rails_rack_logger"
8
7
 
9
8
  module Timber
9
+ # Namespace for all probes.
10
+ # @private
10
11
  module Probes
11
12
  def self.insert!(middleware, insert_before)
12
- # Persistent probes. Order is relevant.
13
- Server.insert!
14
- Heroku.insert!
15
-
16
- # Transient probes, sorted alphabetically
17
- ActionControllerBase.insert!
13
+ ActionControllerLogSubscriber.insert!
18
14
  ActionDispatchDebugExceptions.insert!
19
- ActiveSupportLogSubscriber.insert!
20
- Logger.insert!
21
- Rack.insert!(middleware, insert_before)
15
+ ActionViewLogSubscriber.insert!
16
+ ActiveRecordLogSubscriber.insert!
17
+ RackHTTPContext.insert!(middleware, insert_before)
18
+ RailsRackLogger.insert!
22
19
  end
23
20
  end
24
- end
21
+ end
@@ -0,0 +1,33 @@
1
+ module Timber
2
+ module Util
3
+ # @private
4
+ module ActiveSupportLogSubscriber
5
+ extend self
6
+
7
+ def find(component, type)
8
+ ActiveSupport::LogSubscriber.log_subscribers.find do |subscriber|
9
+ subscriber.class == type
10
+ end
11
+ end
12
+
13
+ def subscribed?(component, type)
14
+ !find(component, type).nil?
15
+ end
16
+
17
+ def unsubscribe(component, type)
18
+ subscriber = find(component, type)
19
+
20
+ if subscriber
21
+ events = subscriber.public_methods(false).reject { |method| method.to_s == 'call' }
22
+ events.each do |event|
23
+ ActiveSupport::Notifications.notifier.listeners_for("#{event}.#{component}").each do |listener|
24
+ if listener.instance_variable_get('@delegate') == subscriber
25
+ ActiveSupport::Notifications.unsubscribe listener
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,14 @@
1
+ module Timber
2
+ module Util
3
+ # @private
4
+ module Hash
5
+ extend self
6
+
7
+ def compact(hash)
8
+ hash.select do |k, v|
9
+ v != nil && v != {} && v != []
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ require "timber/util/active_support_log_subscriber"
2
+ require "timber/util/hash"
3
+
4
+ module Timber
5
+ # @private
6
+ module Util
7
+ end
8
+ end
@@ -1,3 +1,3 @@
1
1
  module Timber
2
- VERSION = "1.0.0.beta1"
3
- end
2
+ VERSION = "1.0.0"
3
+ end
data/lib/timber.rb CHANGED
@@ -2,26 +2,21 @@
2
2
  require "json" # brings to_json to the core classes
3
3
 
4
4
  # Base (must come first, order matters)
5
- require "timber/macros"
6
- require "timber/patterns"
7
5
  require "timber/config"
8
6
  require "timber/context"
9
- require "timber/log_device"
7
+ require "timber/event"
10
8
  require "timber/probe"
9
+ require "timber/util"
11
10
  require "timber/version"
12
11
 
13
12
  # Other (sorted alphabetically)
14
- require "timber/api_settings"
15
- require "timber/bootstrap"
16
- require "timber/context_snapshot"
17
13
  require "timber/contexts"
18
14
  require "timber/current_context"
19
- require "timber/current_line_indexes"
20
- require "timber/internal_logger"
15
+ require "timber/events"
21
16
  require "timber/log_devices"
22
- require "timber/log_line"
17
+ require "timber/log_entry"
23
18
  require "timber/logger"
24
19
  require "timber/probes"
25
20
 
26
- # Load frameworks last
27
- require "timber/frameworks"
21
+ # Load frameworks
22
+ require "timber/frameworks"
data/spec/spec_helper.rb CHANGED
@@ -15,10 +15,13 @@ require File.join(File.dirname(__FILE__), 'support', 'simplecov')
15
15
  require File.join(File.dirname(__FILE__), 'support', 'timecop')
16
16
  require File.join(File.dirname(__FILE__), 'support', 'webmock')
17
17
  require File.join(File.dirname(__FILE__), 'support', 'timber')
18
- require File.join(File.dirname(__FILE__), 'support', 'rails')
19
- require File.join(File.dirname(__FILE__), 'support', 'action_controller')
20
- require File.join(File.dirname(__FILE__), 'support', 'action_view')
21
- require File.join(File.dirname(__FILE__), 'support', 'active_record')
18
+
19
+ if !ENV["RAILS_23"]
20
+ require File.join(File.dirname(__FILE__), 'support', 'rails')
21
+ require File.join(File.dirname(__FILE__), 'support', 'action_controller')
22
+ require File.join(File.dirname(__FILE__), 'support', 'action_view')
23
+ require File.join(File.dirname(__FILE__), 'support', 'active_record')
24
+ end
22
25
 
23
26
  RSpec.configure do |config|
24
27
  config.color = true
@@ -1,8 +1,9 @@
1
1
  require "rails"
2
2
 
3
- log_dev = Timber::LogDevices::IO.new
4
- Rails.logger = Timber::Logger.new(log_dev)
5
- Rails.logger.level = ::Logger::FATAL
3
+ # Defualt the rails logger to nothing, each test shoould be
4
+ # responsible for setting up the logger
5
+ logger = ::Logger.new(nil)
6
+ Rails.logger = logger
6
7
 
7
8
  class RailsApp < Rails::Application
8
9
  if ::Rails.version =~ /^3\./
@@ -12,17 +13,20 @@ class RailsApp < Rails::Application
12
13
  end
13
14
  config.active_support.deprecation = :stderr
14
15
  config.eager_load = false
15
- config.log_level = :fatal
16
16
  end
17
17
 
18
18
  RailsApp.initialize!
19
19
 
20
20
  module Support
21
21
  module Rails
22
- def dispatch_rails_request(path)
22
+ def dispatch_rails_request(path, additional_env_options = {})
23
23
  application = ::Rails.application
24
24
  env = application.respond_to?(:env_config) ? application.env_config.clone : application.env_defaults.clone
25
25
  env["rack.request.cookie_hash"] = {}.with_indifferent_access
26
+ env["REMOTE_ADDR"] = "123.456.789.10"
27
+ env["X-Request-Id"] = "unique-request-id-1234"
28
+ env["action_dispatch.request_id"] = env["X-Request-Id"]
29
+ env = env.merge(additional_env_options)
26
30
  ::Rack::MockRequest.new(application).get(path, env)
27
31
  end
28
32
  end
@@ -1,23 +1,4 @@
1
1
  # Must require last in order to be mocked via webmock
2
2
  require 'timber'
3
3
 
4
- # Config
5
- Timber::Config.tap do |config|
6
- config.application_key = "my_key"
7
- config.logger.level = ::Logger::FATAL
8
- end
9
-
10
- RSpec.configure do |config|
11
- config.after(:each) do
12
- Timber::CurrentLineIndexes.reset!
13
- Timber::LogDevices::HTTP::LogPile.each { |log_pile| log_pile.empty }
14
-
15
- # Reset permanent context caches since we mock, etc.
16
- Timber::CurrentContext.send(:stack).each do |context|
17
- context.instance_variable_set(:"@as_json", nil)
18
- context.instance_variable_set(:"@json_payload", nil)
19
- context.instance_variable_set(:"@to_json", nil)
20
- context.instance_variable_set(:"@to_logfmt", nil)
21
- end
22
- end
23
- end
4
+ Timber::Config.instance.logger = ::Logger.new(nil)
@@ -0,0 +1,55 @@
1
+ require "spec_helper"
2
+
3
+ describe Timber::Events, :rails_23 => true do
4
+ describe ".build" do
5
+ it "should build a Timber::Event" do
6
+ event = Timber::Events::Custom.new(
7
+ type: :payment_rejected,
8
+ message: "Payment rejected",
9
+ data: {customer_id: "abcd1234", amount: 100}
10
+ )
11
+ built_event = Timber::Events.build(event)
12
+ expect(built_event).to eq(event)
13
+ end
14
+
15
+ it "should use #to_timber_event" do
16
+ PaymentRejectedEvent = Struct.new(:customer_id, :amount) do
17
+ def to_timber_event
18
+ Timber::Events::Custom.new(
19
+ type: :payment_rejected,
20
+ message: "Payment rejected for #{customer_id}",
21
+ data: respond_to?(:hash) ? hash : to_hash
22
+ )
23
+ end
24
+ end
25
+ built_event = Timber::Events.build(PaymentRejectedEvent.new("abcd1234", 100))
26
+ expect(built_event).to be_kind_of(Timber::Events::Custom)
27
+ expect(built_event.type).to eq(:payment_rejected)
28
+ expect(built_event.message).to eq("Payment rejected for abcd1234")
29
+ Object.send(:remove_const, :PaymentRejectedEvent)
30
+ end
31
+
32
+ it "should accept a properly structured hash" do
33
+ built_event = Timber::Events.build({type: :payment_rejected, message: "Payment rejected", data: {customer_id: "abcd1234", amount: 100}})
34
+ expect(built_event).to be_kind_of(Timber::Events::Custom)
35
+ expect(built_event.type).to eq(:payment_rejected)
36
+ expect(built_event.message).to eq("Payment rejected")
37
+ end
38
+
39
+ it "should accept a struct" do
40
+ PaymentRejectedEvent = Struct.new(:customer_id, :amount) do
41
+ def message; "Payment rejected for #{customer_id}"; end
42
+ def type; :payment_rejected; end
43
+ end
44
+ built_event = Timber::Events.build(PaymentRejectedEvent.new("abcd1234", 100))
45
+ expect(built_event).to be_kind_of(Timber::Events::Custom)
46
+ expect(built_event.type).to eq(:payment_rejected)
47
+ expect(built_event.message).to eq("Payment rejected for abcd1234")
48
+ Object.send(:remove_const, :PaymentRejectedEvent)
49
+ end
50
+
51
+ it "should return nil for unsupported" do
52
+ expect(Timber::Events.build(1)).to be_nil
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,62 @@
1
+ # require "spec_helper"
2
+
3
+ # describe Timber::LogDevices::HTTP do
4
+ # # We have to define our own at_exit method, because the mocks and
5
+ # # everything are stripped out before. Otherwise it tries to issue
6
+ # # a request :(
7
+ # before(:each) do
8
+ # described_class.class_eval do
9
+ # def at_exit; true; end
10
+ # end
11
+ # end
12
+
13
+ # describe "#initialize" do
14
+ # it "should start a thread for delivery" do
15
+ # allow_any_instance_of(described_class).to receive(:at_exit).exactly(1).times.and_return(true)
16
+ # expect_any_instance_of(described_class).to receive(:deliver).exactly(2).times.and_return(true)
17
+ # http = described_class.new("MYKEY", frequency_seconds: 0.1)
18
+ # thread = http.instance_variable_get(:@delivery_thread)
19
+ # expect(thread).to be_alive
20
+ # sleep 0.25 # allow 2 iterations
21
+ # http.close
22
+ # end
23
+ # end
24
+
25
+ # describe "#write" do
26
+ # let(:http) { described_class.new("MYKEY") }
27
+ # let(:buffer) { http.instance_variable_get(:@buffer) }
28
+
29
+ # after(:each) { http.close }
30
+
31
+ # it "should buffer the messages" do
32
+ # http.write("test log message")
33
+ # expect(buffer.read).to eq("test log message")
34
+ # end
35
+ # end
36
+
37
+ # describe "#deliver" do
38
+ # let(:http) { described_class.new("MYKEY") }
39
+ # let(:buffer) { http.instance_variable_get(:@buffer) }
40
+
41
+ # after(:each) { http.close }
42
+
43
+ # it "should delivery properly and flush the buffer" do
44
+ # expect_any_instance_of(described_class).to receive(:at_exit).exactly(1).times.and_return(true)
45
+ # stub = stub_request(:post, "https://api.timber.io/http_frames").
46
+ # with(
47
+ # :body => "test log message",
48
+ # :headers => {'Authorization'=>'Basic TVlLRVk=', 'Connection'=>'keep-alive', 'Content-Type'=>'application/json', 'User-Agent'=>'Timber Ruby Gem/1.0.0'}
49
+ # ).
50
+ # to_return(:status => 200, :body => "", :headers => {})
51
+
52
+ # http.write("test log message")
53
+
54
+ # expect(buffer).to_not be_empty
55
+
56
+ # http.send(:deliver)
57
+
58
+ # expect(stub).to have_been_requested.times(1)
59
+ # expect(buffer).to be_empty
60
+ # end
61
+ # end
62
+ # end
@@ -0,0 +1,68 @@
1
+ require "spec_helper"
2
+
3
+ describe Timber::Logger, :rails_23 => true do
4
+ describe "#add" do
5
+ let(:time) { Time.utc(2016, 9, 1, 12, 0, 0) }
6
+ let(:io) { StringIO.new }
7
+ let(:logger) { Timber::Logger.new(io) }
8
+
9
+ around(:each) do |example|
10
+ Timecop.freeze(time) { example.run }
11
+ end
12
+
13
+ context "with the :hybrid format" do
14
+ before(:each) { logger.formatter = Timber::Logger::HybridFormatter.new }
15
+
16
+ it "should accept strings" do
17
+ logger.info("this is a test")
18
+ expect(io.string).to eq("this is a test @timber.io {\"level\":\"info\",\"dt\":\"2016-09-01T12:00:00.000000Z\"}\n")
19
+ end
20
+
21
+ context "with a context" do
22
+ let(:http_context) do
23
+ Timber::Contexts::HTTP.new(
24
+ method: "POST",
25
+ path: "/checkout",
26
+ remote_addr: "123.456.789.10",
27
+ request_id: "abcd1234"
28
+ )
29
+ end
30
+
31
+ around(:each) do |example|
32
+ Timber::CurrentContext.instance.with(http_context) do
33
+ example.run
34
+ end
35
+ end
36
+
37
+ it "should snapshot and include the context" do
38
+ expect(Timber::CurrentContext.instance).to receive(:snapshot).and_call_original
39
+ logger.info("this is a test")
40
+ expect(io.string).to eq("this is a test @timber.io {\"level\":\"info\",\"dt\":\"2016-09-01T12:00:00.000000Z\",\"context\":{\"http\":{\"method\":\"POST\",\"path\":\"/checkout\",\"remote_addr\":\"123.456.789.10\",\"request_id\":\"abcd1234\"}}}\n")
41
+ end
42
+ end
43
+
44
+ it "should call and use Timber::Events.build" do
45
+ message = {message: "payment rejected", type: :payment_rejected, data: {customer_id: "abcde1234", amount: 100}}
46
+ expect(Timber::Events).to receive(:build).with(message).and_call_original
47
+ logger.info(message)
48
+ expect(io.string).to eq("payment rejected @timber.io {\"level\":\"info\",\"dt\":\"2016-09-01T12:00:00.000000Z\",\"event\":{\"custom\":{\"payment_rejected\":{\"customer_id\":\"abcde1234\",\"amount\":100}}}}\n")
49
+ end
50
+
51
+ it "should allow functions" do
52
+ logger.info do
53
+ {message: "payment rejected", type: :payment_rejected, data: {customer_id: "abcde1234", amount: 100}}
54
+ end
55
+ expect(io.string).to eq("payment rejected @timber.io {\"level\":\"info\",\"dt\":\"2016-09-01T12:00:00.000000Z\",\"event\":{\"custom\":{\"payment_rejected\":{\"customer_id\":\"abcde1234\",\"amount\":100}}}}\n")
56
+ end
57
+ end
58
+
59
+ context "with the :json format" do
60
+ before(:each) { logger.formatter = Timber::Logger::JSONFormatter.new }
61
+
62
+ it "should log in the correct format" do
63
+ logger.info("this is a test")
64
+ expect(io.string).to eq("{\"level\":\"info\",\"dt\":\"2016-09-01T12:00:00.000000Z\",\"message\":\"this is a test\"}\n")
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,70 @@
1
+ require "spec_helper"
2
+
3
+ describe Timber::Probes::ActionControllerLogSubscriber do
4
+ if defined?(described_class::LogSubscriber)
5
+ describe described_class::LogSubscriber do
6
+ let(:time) { Time.utc(2016, 9, 1, 12, 0, 0) }
7
+ let(:io) { StringIO.new }
8
+ let(:logger) do
9
+ logger = Timber::Logger.new(io)
10
+ logger.level = ::Logger::WARN
11
+ logger
12
+ end
13
+
14
+ around(:each) do |example|
15
+ class LogSubscriberController < ActionController::Base
16
+ layout nil
17
+
18
+ def index
19
+ render json: {}
20
+ end
21
+
22
+ def method_for_action(action_name)
23
+ action_name
24
+ end
25
+ end
26
+
27
+ ::RailsApp.routes.draw do
28
+ get 'log_subscriber' => 'log_subscriber#index'
29
+ end
30
+
31
+ old_logger = ::ActionController::Base.logger
32
+ ::ActionController::Base.logger = logger
33
+
34
+ Timecop.freeze(time) { example.run }
35
+
36
+ Object.send(:remove_const, :LogSubscriberController)
37
+ ::ActionController::Base.logger = old_logger
38
+ end
39
+
40
+ describe "#start_processing, #process_action" do
41
+ it "should not log if the level is not sufficient" do
42
+ dispatch_rails_request("/log_subscriber")
43
+ expect(io.string).to eq("")
44
+ end
45
+
46
+ context "with an info level" do
47
+ around(:each) do |example|
48
+ old_level = logger.level
49
+ logger.level = ::Logger::INFO
50
+ example.run
51
+ logger.level = old_level
52
+ end
53
+
54
+ it "should log the controller call event" do
55
+ # Rails uses this to calculate the view runtime below
56
+ allow(Benchmark).to receive(:ms).and_return(1).and_yield
57
+ dispatch_rails_request("/log_subscriber")
58
+ message1 = <<-MSG
59
+ Processing by LogSubscriberController#index as HTML @timber.io {"level":"info","dt":"2016-09-01T12:00:00.000000Z","event":{"controller_call":{"controller":"LogSubscriberController","action":"index"}},"context":{"http":{"method":"GET","path":"/log_subscriber","remote_addr":"123.456.789.10","request_id":"unique-request-id-1234"}}}
60
+ MSG
61
+ message2 = <<-MSG
62
+ Completed 200 OK in 0.0ms (Views: 1.0ms) @timber.io {"level":"info","dt":"2016-09-01T12:00:00.000000Z","event":{"http_response":{"status":200,"time_ms":0.0}},"context":{"http":{"method":"GET","path":"/log_subscriber","remote_addr":"123.456.789.10","request_id":"unique-request-id-1234"}}}
63
+ MSG
64
+ expect(io.string).to eq(message1.strip + "\n" + message2.strip + "\n")
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -1,9 +1,17 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Timber::Probes::ActionDispatchDebugExceptions do
4
- describe described_class::InstanceMethods do
5
- describe ".log_error" do
6
- before(:each) do
4
+ describe "#{described_class}::*InstanceMethods" do
5
+ describe "#log_error" do
6
+ let(:time) { Time.utc(2016, 9, 1, 12, 0, 0) }
7
+ let(:io) { StringIO.new }
8
+ let(:logger) do
9
+ logger = Timber::Logger.new(io)
10
+ logger.level = ::Logger::DEBUG
11
+ logger
12
+ end
13
+
14
+ around(:each) do |example|
7
15
  class ExceptionController < ActionController::Base
8
16
  layout nil
9
17
 
@@ -19,27 +27,25 @@ describe Timber::Probes::ActionDispatchDebugExceptions do
19
27
  ::RailsApp.routes.draw do
20
28
  get 'exception' => 'exception#index'
21
29
  end
22
- end
23
30
 
24
- after(:each) do
31
+ Timecop.freeze(time) { example.run }
32
+
25
33
  Object.send(:remove_const, :ExceptionController)
26
34
  end
27
35
 
28
- let(:logger_context_class) { Timber::Contexts::Logger }
29
- let(:rack_request_context_class) { Timber::Contexts::HTTPRequests::Rack }
30
- let(:request_context_class) { Timber::Contexts::HTTPRequests::ActionControllerSpecific }
31
- let(:organization_context_class) { Timber::Contexts::Organizations::ActionController }
32
- let(:user_context_class) { Timber::Contexts::Users::ActionController }
33
- let(:response_context_class) { Timber::Contexts::HTTPResponses::ActionController }
34
- let(:exception_context_class) { Timber::Contexts::Exception }
35
-
36
36
  it "should set the context" do
37
- expect(Timber::CurrentContext).to receive(:add).with(kind_of(logger_context_class)).and_yield.at_least(:once)
38
- expect(Timber::CurrentContext).to receive(:add).with(kind_of(rack_request_context_class)).and_yield.once
39
- expect(Timber::CurrentContext).to receive(:add).with(kind_of(request_context_class), kind_of(organization_context_class), kind_of(user_context_class), kind_of(response_context_class)).and_yield.once
40
- expect(Timber::CurrentContext).to receive(:add).with(kind_of(exception_context_class)).and_yield
37
+ mock_class
41
38
  dispatch_rails_request("/exception")
39
+ # Because constantly updating the line numbers sucks :/
40
+ expect(io.string).to include("RuntimeError (boom):\n\n")
41
+ expect(io.string).to include("@timber.io")
42
+ expect(io.string).to include("\"event\":{\"exception\":{\"name\":\"RuntimeError\",\"message\":\"boom\",\"backtrace\":[\"")
43
+ end
44
+
45
+ def mock_class
46
+ klass = defined?(::ActionDispatch::DebugExceptions) ? ::ActionDispatch::DebugExceptions : ::ActionDispatch::ShowExceptions
47
+ allow_any_instance_of(klass).to receive(:logger).and_return(logger)
42
48
  end
43
49
  end
44
50
  end
45
- end
51
+ end