timber 1.1.14 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -2
  3. data/.travis.yml +47 -0
  4. data/Gemfile +1 -28
  5. data/README.md +83 -298
  6. data/bin/timber +13 -0
  7. data/gemfiles/rails-3.0.gemfile +5 -0
  8. data/gemfiles/rails-3.1.gemfile +5 -0
  9. data/gemfiles/rails-3.2.gemfile +5 -0
  10. data/gemfiles/rails-4.0.gemfile +9 -0
  11. data/gemfiles/rails-4.1.gemfile +9 -0
  12. data/gemfiles/rails-4.2.gemfile +9 -0
  13. data/gemfiles/rails-5.0.gemfile +9 -0
  14. data/gemfiles/rails-edge.gemfile +7 -0
  15. data/lib/timber.rb +7 -7
  16. data/lib/timber/cli.rb +72 -0
  17. data/lib/timber/cli/api.rb +104 -0
  18. data/lib/timber/cli/application.rb +28 -0
  19. data/lib/timber/cli/install.rb +186 -0
  20. data/lib/timber/cli/io_helper.rb +58 -0
  21. data/lib/timber/cli/messages.rb +170 -0
  22. data/lib/timber/config.rb +47 -6
  23. data/lib/timber/contexts/http.rb +2 -2
  24. data/lib/timber/current_context.rb +1 -1
  25. data/lib/timber/event.rb +8 -0
  26. data/lib/timber/events.rb +2 -0
  27. data/lib/timber/events/controller_call.rb +12 -3
  28. data/lib/timber/events/exception.rb +4 -3
  29. data/lib/timber/events/http_client_request.rb +61 -0
  30. data/lib/timber/events/http_client_response.rb +47 -0
  31. data/lib/timber/events/http_server_request.rb +15 -23
  32. data/lib/timber/events/http_server_response.rb +9 -9
  33. data/lib/timber/events/sql_query.rb +2 -2
  34. data/lib/timber/events/template_render.rb +2 -2
  35. data/lib/timber/frameworks/rails.rb +31 -6
  36. data/lib/timber/integrations.rb +22 -0
  37. data/lib/timber/integrations/action_controller/log_subscriber.rb +25 -0
  38. data/lib/timber/integrations/action_controller/log_subscriber/timber_log_subscriber.rb +40 -0
  39. data/lib/timber/integrations/action_dispatch/debug_exceptions.rb +51 -0
  40. data/lib/timber/integrations/action_view/log_subscriber.rb +25 -0
  41. data/lib/timber/integrations/action_view/log_subscriber/timber_log_subscriber.rb +73 -0
  42. data/lib/timber/integrations/active_record/log_subscriber.rb +25 -0
  43. data/lib/timber/integrations/active_record/log_subscriber/timber_log_subscriber.rb +39 -0
  44. data/lib/timber/integrations/active_support/tagged_logging.rb +71 -0
  45. data/lib/timber/integrations/rack.rb +16 -0
  46. data/lib/timber/integrations/rack/exception_event.rb +28 -0
  47. data/lib/timber/integrations/rack/http_context.rb +25 -0
  48. data/lib/timber/integrations/rack/http_events.rb +46 -0
  49. data/lib/timber/integrations/rack/user_context.rb +59 -0
  50. data/lib/timber/integrations/rails/rack_logger.rb +49 -0
  51. data/lib/timber/integrator.rb +24 -0
  52. data/lib/timber/log_devices/http.rb +14 -21
  53. data/lib/timber/log_entry.rb +1 -1
  54. data/lib/timber/logger.rb +38 -12
  55. data/lib/timber/overrides.rb +9 -0
  56. data/lib/timber/overrides/lograge.rb +14 -0
  57. data/lib/timber/overrides/rails_server.rb +10 -0
  58. data/lib/timber/util.rb +2 -0
  59. data/lib/timber/util/active_support_log_subscriber.rb +13 -9
  60. data/lib/timber/util/http_event.rb +54 -0
  61. data/lib/timber/util/request.rb +44 -0
  62. data/lib/timber/version.rb +1 -1
  63. data/spec/README.md +5 -9
  64. data/spec/spec_helper.rb +1 -4
  65. data/spec/support/action_controller.rb +7 -3
  66. data/spec/support/active_record.rb +23 -19
  67. data/spec/support/rails.rb +56 -32
  68. data/spec/support/timber.rb +2 -3
  69. data/spec/support/webmock.rb +1 -0
  70. data/spec/timber/integrations/action_controller/log_subscriber_spec.rb +55 -0
  71. data/spec/timber/integrations/action_dispatch/debug_exceptions_spec.rb +53 -0
  72. data/spec/timber/integrations/action_view/log_subscriber_spec.rb +115 -0
  73. data/spec/timber/integrations/active_record/log_subscriber_spec.rb +46 -0
  74. data/spec/timber/integrations/rack/http_context_spec.rb +60 -0
  75. data/spec/timber/integrations/rails/rack_logger_spec.rb +58 -0
  76. data/spec/timber/logger_spec.rb +45 -9
  77. data/timber.gemspec +29 -3
  78. metadata +143 -46
  79. data/Appraisals +0 -41
  80. data/circle.yml +0 -33
  81. data/lib/timber/overrides/logger_add.rb +0 -38
  82. data/lib/timber/probe.rb +0 -23
  83. data/lib/timber/probes.rb +0 -23
  84. data/lib/timber/probes/action_controller_log_subscriber.rb +0 -20
  85. data/lib/timber/probes/action_controller_log_subscriber/log_subscriber.rb +0 -64
  86. data/lib/timber/probes/action_controller_user_context.rb +0 -52
  87. data/lib/timber/probes/action_dispatch_debug_exceptions.rb +0 -80
  88. data/lib/timber/probes/action_view_log_subscriber.rb +0 -20
  89. data/lib/timber/probes/action_view_log_subscriber/log_subscriber.rb +0 -69
  90. data/lib/timber/probes/active_record_log_subscriber.rb +0 -20
  91. data/lib/timber/probes/active_record_log_subscriber/log_subscriber.rb +0 -31
  92. data/lib/timber/probes/active_support_tagged_logging.rb +0 -63
  93. data/lib/timber/probes/rails_rack_logger.rb +0 -77
  94. data/lib/timber/rack_middlewares.rb +0 -12
  95. data/lib/timber/rack_middlewares/http_context.rb +0 -30
  96. data/spec/support/action_view.rb +0 -4
  97. data/spec/support/coveralls.rb +0 -2
  98. data/spec/support/simplecov.rb +0 -9
  99. data/spec/timber/overrides/logger_add_spec.rb +0 -26
  100. data/spec/timber/probes/action_controller_log_subscriber_spec.rb +0 -65
  101. data/spec/timber/probes/action_controller_user_context_spec.rb +0 -53
  102. data/spec/timber/probes/action_dispatch_debug_exceptions_spec.rb +0 -48
  103. data/spec/timber/probes/action_view_log_subscriber_spec.rb +0 -107
  104. data/spec/timber/probes/active_record_log_subscriber_spec.rb +0 -47
  105. data/spec/timber/probes/rails_rack_logger_spec.rb +0 -46
  106. data/spec/timber/rack_middlewares/http_context_spec.rb +0 -47
@@ -1,4 +1,3 @@
1
- # Must require last in order to be mocked via webmock
2
- require 'timber'
1
+ require "timber"
3
2
 
4
- Timber::Config.instance.logger = ::Logger.new(STDOUT)
3
+ Timber::Config.instance.debug_logger = ::Logger.new(STDOUT)
@@ -1,2 +1,3 @@
1
1
  require 'webmock/rspec'
2
+
2
3
  WebMock.disable_net_connect!
@@ -0,0 +1,55 @@
1
+ require "spec_helper"
2
+
3
+ if defined?(::ActionController)
4
+ describe Timber::Integrations::ActionController::LogSubscriber do
5
+ let(:time) { Time.utc(2016, 9, 1, 12, 0, 0) }
6
+ let(:io) { StringIO.new }
7
+ let(:logger) do
8
+ logger = Timber::Logger.new(io)
9
+ logger.level = ::Logger::INFO
10
+ logger
11
+ end
12
+
13
+ describe "#insert!" do
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
+ with_rails_logger(logger) do
32
+ Timecop.freeze(time) { example.run }
33
+ end
34
+
35
+ Object.send(:remove_const, :LogSubscriberController)
36
+ end
37
+
38
+ it "should log a controller_call event once" do
39
+ # Rails uses this to calculate the view runtime below
40
+ allow(Benchmark).to receive(:ms).and_return(1).and_yield
41
+
42
+ dispatch_rails_request("/log_subscriber?query=value")
43
+ lines = clean_lines(io.string.split("\n"))
44
+ expect(lines.length).to eq(3)
45
+ expect(lines[1]).to start_with('Processing by LogSubscriberController#index as HTML\n Parameters: {"query"=>"value"} @metadata {"level":"info","dt":"2016-09-01T12:00:00.000000Z"')
46
+ expect(lines[1]).to include('"event":{"server_side_app":{"controller_call":{"controller":"LogSubscriberController","action":"index","params_json":"{\"query\":\"value\"}"}}}')
47
+ end
48
+
49
+ # Remove blank lines since Rails does this to space out requests in the logs
50
+ def clean_lines(lines)
51
+ lines.select { |line| !line.start_with?(" @metadat") }
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,53 @@
1
+ require "spec_helper"
2
+
3
+ if defined?(::ActionDispatch)
4
+ describe Timber::Integrations::ActionDispatch::DebugExceptions do
5
+ let(:time) { Time.utc(2016, 9, 1, 12, 0, 0) }
6
+ let(:io) { StringIO.new }
7
+ let(:logger) do
8
+ logger = Timber::Logger.new(io)
9
+ logger.level = ::Logger::DEBUG
10
+ logger
11
+ end
12
+
13
+ describe "#insert!" do
14
+ around(:each) do |example|
15
+ class ExceptionController < ActionController::Base
16
+ layout nil
17
+
18
+ def index
19
+ raise "boom"
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 'exception' => 'exception#index'
29
+ end
30
+
31
+ with_rails_logger(logger) do
32
+ Timecop.freeze(time) { example.run }
33
+ end
34
+
35
+ Object.send(:remove_const, :ExceptionController)
36
+ end
37
+
38
+ it "should log an exception event once" do
39
+ expect { dispatch_rails_request("/exception") }.to raise_error(RuntimeError)
40
+
41
+ lines = clean_lines(io.string.split("\n"))
42
+ expect(lines.length).to eq(3)
43
+ expect(lines[2]).to start_with('RuntimeError (boom) @metadata {"level":"fatal",')
44
+ expect(lines[2]).to include("\"event\":{\"server_side_app\":{\"exception\":{\"name\":\"RuntimeError\",\"message\":\"boom\",\"backtrace\":[")
45
+ end
46
+
47
+ # Remove blank lines since Rails does this to space out requests in the logs
48
+ def clean_lines(lines)
49
+ lines.select { |line| !line.start_with?(" @metadat") }
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,115 @@
1
+ require "spec_helper"
2
+
3
+ if defined?(::ActionView)
4
+ describe Timber::Integrations::ActionView::LogSubscriber do
5
+ let(:time) { Time.utc(2016, 9, 1, 12, 0, 0) }
6
+ let(:io) { StringIO.new }
7
+ let(:logger) do
8
+ logger = Timber::Logger.new(io)
9
+ logger.level = ::Logger::WARN
10
+ logger
11
+ end
12
+
13
+ describe "insert!" do
14
+ around(:each) do |example|
15
+ class ActionViewLogSubscriberController < ActionController::Base
16
+ layout nil
17
+
18
+ def index
19
+ render template: "template"
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 'action_view_log_subscriber' => 'action_view_log_subscriber#index'
29
+ end
30
+
31
+ with_rails_logger(logger) do
32
+ Timecop.freeze(time) { example.run }
33
+ end
34
+
35
+ Object.send(:remove_const, :ActionViewLogSubscriberController)
36
+ end
37
+
38
+ describe "#render_template" do
39
+ it "should not log if the level is not sufficient" do
40
+ dispatch_rails_request("/action_view_log_subscriber")
41
+ expect(io.string).to eq("")
42
+ end
43
+
44
+ context "with an info level" do
45
+ around(:each) do |example|
46
+ old_level = logger.level
47
+ logger.level = ::Logger::INFO
48
+ example.run
49
+ logger.level = old_level
50
+ end
51
+
52
+ it "should log a template render event once" do
53
+ dispatch_rails_request("/action_view_log_subscriber")
54
+ lines = clean_lines(io.string.split("\n"))
55
+ expect(lines[2].strip).to start_with("Rendered spec/support/rails/templates/template.html (0.0ms) @metadata {\"level\":\"info\"")
56
+ expect(lines[2]).to include("\"event\":{\"server_side_app\":{\"template_render\":{\"name\":\"spec/support/rails/templates/template.html\",\"time_ms\":0.0}}},")
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ if defined?(described_class::TimberLogSubscriber)
63
+ describe described_class::TimberLogSubscriber do
64
+ let(:event) do
65
+ event = Struct.new(:duration, :payload)
66
+ event.new(2, identifier: "path/to/template.html")
67
+ end
68
+
69
+ around(:each) do |example|
70
+ old_level = logger.level
71
+ logger.level = ::Logger::INFO
72
+ example.run
73
+ logger.level = old_level
74
+ end
75
+
76
+ describe "#render_template" do
77
+ it "should render the collection" do
78
+ log_subscriber = described_class.new
79
+ allow(log_subscriber).to receive(:logger).and_return(logger)
80
+ log_subscriber.render_template(event)
81
+ expect(io.string.strip).to start_with("Rendered path/to/template.html (2.0ms) @metadata")
82
+ end
83
+ end
84
+
85
+ describe "#render_partial" do
86
+ it "should render the collection" do
87
+ log_subscriber = described_class.new
88
+ allow(log_subscriber).to receive(:logger).and_return(logger)
89
+ log_subscriber.render_partial(event)
90
+ expect(io.string.strip).to start_with("Rendered path/to/template.html (2.0ms) @metadata")
91
+ end
92
+ end
93
+
94
+ describe "#render_collection" do
95
+ it "should render the collection" do
96
+ log_subscriber = described_class.new
97
+ allow(log_subscriber).to receive(:logger).and_return(logger)
98
+ log_subscriber.render_collection(event)
99
+
100
+ if log_subscriber.respond_to?(:render_count, true)
101
+ expect(io.string.strip).to start_with("Rendered collection of path/to/template.html [ times] (2.0ms) @metadata ")
102
+ else
103
+ expect(io.string.strip).to start_with("Rendered path/to/template.html (2.0ms) @metadata")
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ # Remove blank lines since Rails does this to space out requests in the logs
111
+ def clean_lines(lines)
112
+ lines.select { |line| !line.start_with?(" @metadata") }
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,46 @@
1
+ require "spec_helper"
2
+
3
+ if defined?(::ActiveRecord)
4
+ describe Timber::Integrations::ActiveRecord::LogSubscriber do
5
+ let(:time) { Time.utc(2016, 9, 1, 12, 0, 0) }
6
+ let(:io) { StringIO.new }
7
+ let(:logger) do
8
+ logger = Timber::Logger.new(io)
9
+ logger.level = ::Logger::INFO
10
+ logger
11
+ end
12
+
13
+ describe "#insert!" do
14
+ around(:each) do |example|
15
+ with_rails_logger(logger) do
16
+ Timecop.freeze(time) { example.run }
17
+ end
18
+ end
19
+
20
+ it "should not log if the level is not sufficient" do
21
+ ActiveRecord::Base.connection.execute("select * from users")
22
+ expect(io.string).to eq("")
23
+ end
24
+
25
+ context "with an info level" do
26
+ around(:each) do |example|
27
+ old_level = logger.level
28
+ logger.level = ::Logger::DEBUG
29
+ example.run
30
+ logger.level = old_level
31
+ end
32
+
33
+ it "should log the sql query" do
34
+ ActiveRecord::Base.connection.execute("select * from users")
35
+ # Rails 4.X adds random spaces :/
36
+ string = io.string.gsub(" ORDER BY", " ORDER BY")
37
+ string = string.gsub(" ORDER BY", " ORDER BY")
38
+ expect(string).to include("select * from users")
39
+ expect(string).to include("@metadata")
40
+ expect(string).to include("\"level\":\"debug\"")
41
+ expect(string).to include("\"event\":{\"server_side_app\":{\"sql_query\"")
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,60 @@
1
+ require "spec_helper"
2
+
3
+ if defined?(::Rack)
4
+ describe Timber::Integrations::Rack::HTTPContext do
5
+ let(:time) { Time.utc(2016, 9, 1, 12, 0, 0) }
6
+ let(:io) { StringIO.new }
7
+ let(:logger) do
8
+ logger = Timber::Logger.new(io)
9
+ logger.level = ::Logger::INFO
10
+ logger
11
+ end
12
+
13
+ around(:each) do |example|
14
+ class RackHttpController < ActionController::Base
15
+ layout nil
16
+
17
+ def index
18
+ Thread.current[:_timber_context_snapshot] = Timber::CurrentContext.instance.snapshot
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 '/rack_http' => 'rack_http#index'
29
+ end
30
+
31
+ with_rails_logger(logger) do
32
+ Timecop.freeze(time) { example.run }
33
+ end
34
+
35
+ Object.send(:remove_const, :RackHttpController)
36
+ end
37
+
38
+ describe "#process" do
39
+ it "should set the context" do
40
+ allow(Benchmark).to receive(:ms).and_return(1).and_yield
41
+
42
+ dispatch_rails_request("/rack_http")
43
+ http_context = Thread.current[:_timber_context_snapshot][:http]
44
+
45
+ expect(http_context).to eq({:method=>"GET", :path=>"/rack_http", :remote_addr=>"123.456.789.10", :request_id=>"unique-request-id-1234"})
46
+
47
+ lines = clean_lines(io.string.split("\n"))
48
+ expect(lines.length).to eq(3)
49
+ lines.each do |line|
50
+ expect(line).to include("\"http\":{\"method\":\"GET\",\"path\":\"/rack_http\",\"remote_addr\":\"123.456.789.10\",\"request_id\":\"unique-request-id-1234\"}")
51
+ end
52
+ end
53
+ end
54
+
55
+ # Remove blank lines since Rails does this to space out requests in the logs
56
+ def clean_lines(lines)
57
+ lines.select { |line| !line.start_with?(" @metadat") }
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,58 @@
1
+ require "spec_helper"
2
+
3
+ if defined?(::Rails)
4
+ describe Timber::Integrations::Rails::RackLogger do
5
+ describe described_class::InstanceMethods 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::INFO
11
+ logger
12
+ end
13
+
14
+ around(:each) do |example|
15
+ class RailsRackLoggerController < 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 '/rails_rack_logger' => 'rails_rack_logger#index'
29
+ end
30
+
31
+ with_rails_logger(logger) do
32
+ Timecop.freeze(time) { example.run }
33
+ end
34
+
35
+ Object.send(:remove_const, :RailsRackLoggerController)
36
+ end
37
+
38
+ describe "#started_request_message" do
39
+ it "should mute the default rails logs" do
40
+ allow(::Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new("production")) # Rails 3.2.X
41
+
42
+ dispatch_rails_request("/rails_rack_logger")
43
+
44
+ lines = clean_lines(io.string.split("\n"))
45
+ expect(lines.length).to eq(3)
46
+ expect(lines[0]).to start_with("Started GET \"/rails_rack_logger\" @metadata")
47
+ expect(lines[1]).to start_with("Processing by RailsRackLoggerController#index as HTML @metadata")
48
+ expect(lines[2]).to start_with("Completed 200 OK in 0.0ms @metadata")
49
+ end
50
+ end
51
+ end
52
+
53
+ # Remove blank lines since Rails does this to space out requests in the logs
54
+ def clean_lines(lines)
55
+ lines.select { |line| !line.start_with?(" @metadat") }
56
+ end
57
+ end
58
+ end
@@ -58,17 +58,17 @@ describe Timber::Logger, :rails_23 => true do
58
58
  end
59
59
 
60
60
  it "should allow :time_ms" do
61
- logger.info(message: "event complete", time_ms: 54.5)
61
+ logger.info("event complete", time_ms: 54.5)
62
62
  expect(io.string).to include("\"time_ms\":54.5")
63
63
  end
64
64
 
65
65
  it "should allow :tag" do
66
- logger.info(message: "event complete", tag: "tag1")
66
+ logger.info("event complete", tag: "tag1")
67
67
  expect(io.string).to include("\"tags\":[\"tag1\"]")
68
68
  end
69
69
 
70
70
  it "should allow :tags" do
71
- logger.info(message: "event complete", tags: ["tag1", "tag2"])
71
+ logger.info("event complete", tags: ["tag1", "tag2"])
72
72
  expect(io.string).to include("\"tags\":[\"tag1\",\"tag2\"]")
73
73
  end
74
74
 
@@ -119,17 +119,53 @@ describe Timber::Logger, :rails_23 => true do
119
119
  end
120
120
 
121
121
  describe "#formatter=" do
122
+ it "should not allow changing the formatter when the device is HTTP" do
123
+ http_device = Timber::LogDevices::HTTP.new("api_key")
124
+ logger = Timber::Logger.new(http_device)
125
+ expect { logger.formatter = ::Logger::Formatter.new }.to raise_error(ArgumentError)
126
+ end
127
+
128
+ it "should set the formatter" do
129
+ logger = Timber::Logger.new(STDOUT)
130
+ formatter = ::Logger::Formatter.new
131
+ logger.formatter = formatter
132
+ expect(logger.formatter).to eq(formatter)
133
+ end
134
+ end
135
+
136
+ describe "#info" do
137
+ let(:io) { StringIO.new }
138
+ let(:logger) { Timber::Logger.new(io) }
139
+
140
+ it "should allow default usage" do
141
+ logger.info("message")
142
+ expect(io.string).to start_with("message @metadata")
143
+ expect(io.string).to include('"level":"info"')
144
+ end
145
+
146
+ it "should allow messages with options" do
147
+ logger.info("message", tag: "tag")
148
+ expect(io.string).to start_with("message @metadata")
149
+ expect(io.string).to include('"level":"info"')
150
+ expect(io.string).to include('"tags":["tag"]')
151
+ end
152
+ end
153
+
154
+ describe "#error" do
122
155
  let(:io) { StringIO.new }
123
156
  let(:logger) { Timber::Logger.new(io) }
124
157
 
125
- it "should not allow non Timber::Logger::Formatter formatters" do
126
- logger.formatter = ::Logger::Formatter.new
127
- expect(logger.formatter).to be_kind_of(::Timber::Logger::HybridFormatter)
158
+ it "should allow default usage" do
159
+ logger.error("message")
160
+ expect(io.string).to start_with("message @metadata")
161
+ expect(io.string).to include('"level":"error"')
128
162
  end
129
163
 
130
- it "should allow Timber::Logger::Formatter formatters" do
131
- logger.formatter = ::Timber::Logger::JSONFormatter.new
132
- expect(logger.formatter).to be_kind_of(::Timber::Logger::JSONFormatter)
164
+ it "should allow messages with options" do
165
+ logger.error("message", tag: "tag")
166
+ expect(io.string).to start_with("message @metadata")
167
+ expect(io.string).to include('"level":"error"')
168
+ expect(io.string).to include('"tags":["tag"]')
133
169
  end
134
170
  end
135
171
  end