timber 1.0.13 → 1.1.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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +278 -121
  3. data/lib/timber.rb +1 -0
  4. data/lib/timber/context.rb +1 -1
  5. data/lib/timber/contexts.rb +29 -2
  6. data/lib/timber/contexts/runtime.rb +24 -0
  7. data/lib/timber/contexts/system.rb +19 -0
  8. data/lib/timber/current_context.rb +14 -5
  9. data/lib/timber/event.rb +1 -1
  10. data/lib/timber/events.rb +11 -6
  11. data/lib/timber/events/controller_call.rb +1 -1
  12. data/lib/timber/events/custom.rb +1 -1
  13. data/lib/timber/events/exception.rb +1 -1
  14. data/lib/timber/events/{http_request.rb → http_server_request.rb} +1 -1
  15. data/lib/timber/events/{http_response.rb → http_server_response.rb} +2 -1
  16. data/lib/timber/events/sql_query.rb +2 -1
  17. data/lib/timber/events/template_render.rb +2 -1
  18. data/lib/timber/frameworks/rails.rb +12 -1
  19. data/lib/timber/log_devices/http.rb +29 -24
  20. data/lib/timber/log_entry.rb +23 -9
  21. data/lib/timber/logger.rb +20 -6
  22. data/lib/timber/probes.rb +1 -3
  23. data/lib/timber/probes/active_support_tagged_logging.rb +0 -43
  24. data/lib/timber/probes/rails_rack_logger.rb +1 -1
  25. data/lib/timber/rack_middlewares.rb +12 -0
  26. data/lib/timber/rack_middlewares/http_context.rb +30 -0
  27. data/lib/timber/util.rb +1 -0
  28. data/lib/timber/util/struct.rb +16 -0
  29. data/lib/timber/version.rb +1 -1
  30. data/spec/README.md +23 -0
  31. data/spec/support/timber.rb +1 -1
  32. data/spec/timber/contexts_spec.rb +49 -0
  33. data/spec/timber/events_spec.rb +1 -1
  34. data/spec/timber/log_devices/http_spec.rb +7 -7
  35. data/spec/timber/log_entry_spec.rb +15 -0
  36. data/spec/timber/logger_spec.rb +14 -10
  37. data/spec/timber/probes/action_controller_log_subscriber_spec.rb +6 -7
  38. data/spec/timber/probes/action_dispatch_debug_exceptions_spec.rb +1 -1
  39. data/spec/timber/probes/action_view_log_subscriber_spec.rb +2 -2
  40. data/spec/timber/probes/rails_rack_logger_spec.rb +3 -3
  41. data/spec/timber/rack_middlewares/http_context_spec.rb +47 -0
  42. data/timber.gemspec +1 -0
  43. metadata +31 -8
  44. data/lib/timber/contexts/tags.rb +0 -22
  45. data/lib/timber/probes/rack_http_context.rb +0 -51
  46. data/spec/timber/probes/rack_http_context_spec.rb +0 -50
@@ -13,12 +13,15 @@ module Timber
13
13
  # @example Basic example (the original ::Logger interface remains untouched):
14
14
  # logger.info "Payment rejected for customer #{customer_id}"
15
15
  #
16
- # @example Using a map
17
- # # The :message, :type, and :data keys are required
18
- # logger.info message: "Payment rejected", type: :payment_rejected, data: {customer_id: customer_id, amount: 100}
16
+ # @example Using a Hash
17
+ # # The :message key is required, the other additional key is your event type and data
18
+ # # :type is the namespace used in timber for the :data
19
+ # logger.info message: "Payment rejected", payment_rejected: {customer_id: customer_id, amount: 100}
19
20
  #
20
21
  # @example Using a Struct (a simple, more structured way, to define events)
21
22
  # PaymentRejectedEvent = Struct.new(:customer_id, :amount, :reason) do
23
+ # # `#message` and `#type` are required, otherwise they will not be logged properly.
24
+ # # `#type` is the namespace used in timber for the struct data
22
25
  # def message; "Payment rejected for #{customer_id}"; end
23
26
  # def type; :payment_rejected; end
24
27
  # end
@@ -72,14 +75,25 @@ module Timber
72
75
  private
73
76
  def build_log_entry(severity, time, progname, msg)
74
77
  level = SEVERITY_MAP.fetch(severity)
75
- context = CurrentContext.instance.snapshot
78
+ context_snapshot = CurrentContext.instance.snapshot
79
+ tags = extract_active_support_tagged_logging_tags
80
+ tags += [msg.delete(:tag)] if msg.is_a?(Hash) && msg.key?(:tag)
81
+ tags += msg.delete(:tags) if msg.is_a?(Hash) && msg.key?(:tags)
76
82
  event = Events.build(msg)
83
+
77
84
  if event
78
- LogEntry.new(level, time, progname, event.message, context, event)
85
+ LogEntry.new(level, time, progname, event.message, context_snapshot, event, tags)
79
86
  else
80
- LogEntry.new(level, time, progname, msg, context, nil)
87
+ LogEntry.new(level, time, progname, msg, context_snapshot, nil, tags)
81
88
  end
82
89
  end
90
+
91
+ # Because of all the crazy ways Rails has attempted this we need this crazy method.
92
+ def extract_active_support_tagged_logging_tags
93
+ Thread.current[:activesupport_tagged_logging_tags] ||
94
+ Thread.current["activesupport_tagged_logging_tags:#{object_id}"] ||
95
+ []
96
+ end
83
97
  end
84
98
 
85
99
  # Structures your log messages into Timber's hybrid format, which makes
@@ -3,20 +3,18 @@ require "timber/probes/action_dispatch_debug_exceptions"
3
3
  require "timber/probes/action_view_log_subscriber"
4
4
  require "timber/probes/active_record_log_subscriber"
5
5
  require "timber/probes/active_support_tagged_logging"
6
- require "timber/probes/rack_http_context"
7
6
  require "timber/probes/rails_rack_logger"
8
7
 
9
8
  module Timber
10
9
  # Namespace for all probes.
11
10
  # @private
12
11
  module Probes
13
- def self.insert!(middleware, insert_before)
12
+ def self.insert!
14
13
  ActionControllerLogSubscriber.insert!
15
14
  ActionDispatchDebugExceptions.insert!
16
15
  ActionViewLogSubscriber.insert!
17
16
  ActiveRecordLogSubscriber.insert!
18
17
  ActiveSupportTaggedLogging.insert!
19
- RackHTTPContext.insert!(middleware, insert_before)
20
18
  RailsRackLogger.insert!
21
19
  end
22
20
  end
@@ -17,26 +17,6 @@ module Timber
17
17
  super(severity, timestamp, progname, "#{tags_text}#{msg}")
18
18
  end
19
19
  end
20
-
21
- def push_tags(*tags)
22
- _timber_original_push_tags(*tags).tap do
23
- if current_tags.size > 0
24
- context = Contexts::Tags.new(values: current_tags)
25
- CurrentContext.add(context)
26
- end
27
- end
28
- end
29
-
30
- def pop_tags(size = 1)
31
- _timber_original_pop_tags(size).tap do
32
- if current_tags.size == 0
33
- CurrentContext.remove(Contexts::Tags)
34
- else
35
- context = Contexts::Tags.new(values: current_tags)
36
- CurrentContext.add(context)
37
- end
38
- end
39
- end
40
20
  end
41
21
  end
42
22
  end
@@ -44,29 +24,6 @@ module Timber
44
24
  module LoggerMethods
45
25
  def self.included(klass)
46
26
  klass.class_eval do
47
- alias_method :_timber_original_push_tags, :push_tags
48
- alias_method :_timber_original_pop_tags, :pop_tags
49
-
50
- def push_tags(*tags)
51
- _timber_original_push_tags(*tags).tap do
52
- if current_tags.size > 0
53
- context = Contexts::Tags.new(values: current_tags)
54
- CurrentContext.add(context)
55
- end
56
- end
57
- end
58
-
59
- def pop_tags(size = 1)
60
- _timber_original_pop_tags(size).tap do
61
- if current_tags.size == 0
62
- CurrentContext.remove(Contexts::Tags)
63
- else
64
- context = Contexts::Tags.new(values: current_tags)
65
- CurrentContext.add(context)
66
- end
67
- end
68
- end
69
-
70
27
  def add(severity, message = nil, progname = nil, &block)
71
28
  if message.nil?
72
29
  if block_given?
@@ -7,7 +7,7 @@ module Timber
7
7
  def self.included(klass)
8
8
  klass.class_eval do
9
9
  protected
10
- if klass.method_defined?(:started_request_message)
10
+ if klass.private_instance_methods.include?(:started_request_message) || klass.method_defined?(:started_request_message)
11
11
  def started_request_message(request)
12
12
  http_request_event(request)
13
13
  end
@@ -0,0 +1,12 @@
1
+ require "timber/rack_middlewares/http_context"
2
+
3
+ module Timber
4
+ # Namespace for all Rack middlewares.
5
+ module RackMiddlewares
6
+ # A list containing *all* `Timber::RackMiddlewares::*` sub classes. This makes it easy to
7
+ # achieve forward compatibility as middlewares are modified.
8
+ def self.middlewares
9
+ @middlewares ||= [HTTPContext]
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,30 @@
1
+ module Timber
2
+ module RackMiddlewares
3
+ # Reponsible for adding the HTTP context for applications that use `Rack`.
4
+ class HTTPContext
5
+ def initialize(app)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ request = ::Rack::Request.new(env)
11
+ context = Contexts::HTTP.new(
12
+ method: request.request_method,
13
+ path: request.path,
14
+ remote_addr: request.ip,
15
+ request_id: request_id(env)
16
+ )
17
+ CurrentContext.with(context) do
18
+ @app.call(env)
19
+ end
20
+ end
21
+
22
+ private
23
+ def request_id(env)
24
+ env["X-Request-ID"] ||
25
+ env["X-Request-Id"] ||
26
+ env["action_dispatch.request_id"]
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,5 +1,6 @@
1
1
  require "timber/util/active_support_log_subscriber"
2
2
  require "timber/util/hash"
3
+ require "timber/util/struct"
3
4
 
4
5
  module Timber
5
6
  # @private
@@ -0,0 +1,16 @@
1
+ module Timber
2
+ module Util
3
+ # @private
4
+ module Struct
5
+ extend self
6
+
7
+ def to_hash(struct)
8
+ h = {}
9
+ struct.each_pair do |k ,v|
10
+ h[k] = v
11
+ end
12
+ h
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,3 +1,3 @@
1
1
  module Timber
2
- VERSION = "1.0.13"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -0,0 +1,23 @@
1
+ # Testing
2
+
3
+ Testing Timber uses [`appraisal`](https://github.com/thoughtbot/appraisal). This allows us to
4
+ test across multiple versions and combinations of libraries.
5
+
6
+ To get started:
7
+
8
+ ```shell
9
+ bundle install
10
+ bundle exec appraisal install
11
+ ```
12
+
13
+ To see all appraisal commands:
14
+
15
+ ```shell
16
+ appraisal --help
17
+ ```
18
+
19
+ You can run tests with:
20
+
21
+ ```shell
22
+ appraisal rails-3.2.X rspec
23
+ ```
@@ -1,4 +1,4 @@
1
1
  # Must require last in order to be mocked via webmock
2
2
  require 'timber'
3
3
 
4
- Timber::Config.instance.logger = ::Logger.new(nil)
4
+ Timber::Config.instance.logger = ::Logger.new(STDOUT)
@@ -0,0 +1,49 @@
1
+ require "spec_helper"
2
+
3
+ describe Timber::Contexts, :rails_23 => true do
4
+ describe ".build" do
5
+ it "should build a Timber::Context" do
6
+ context = Timber::Contexts::Custom.new(
7
+ type: :build,
8
+ data: {version: "1.0.0"}
9
+ )
10
+ built_context = Timber::Contexts.build(context)
11
+ expect(built_context).to eq(context)
12
+ end
13
+
14
+ it "should use #to_timber_context" do
15
+ BuildContext = Struct.new(:version) do
16
+ def to_timber_context
17
+ Timber::Contexts::Custom.new(
18
+ type: :build,
19
+ data: respond_to?(:to_h) ? to_h : Timber::Util::Struct.to_hash(self)
20
+ )
21
+ end
22
+ end
23
+ built_context = Timber::Contexts.build(BuildContext.new("1.0.0"))
24
+ expect(built_context).to be_kind_of(Timber::Contexts::Custom)
25
+ expect(built_context.type).to eq(:build)
26
+ Object.send(:remove_const, :BuildContext)
27
+ end
28
+
29
+ it "should accept a properly structured hash" do
30
+ built_context = Timber::Contexts.build(build: {version: "1.0.0"})
31
+ expect(built_context).to be_kind_of(Timber::Contexts::Custom)
32
+ expect(built_context.type).to eq(:build)
33
+ end
34
+
35
+ it "should accept a struct" do
36
+ BuildContext = Struct.new(:version) do
37
+ def type; :build; end
38
+ end
39
+ built_context = Timber::Contexts.build(BuildContext.new("1.0.0"))
40
+ expect(built_context).to be_kind_of(Timber::Contexts::Custom)
41
+ expect(built_context.type).to eq(:build)
42
+ Object.send(:remove_const, :BuildContext)
43
+ end
44
+
45
+ it "should return nil for unsupported" do
46
+ expect(Timber::Contexts.build(1)).to be_nil
47
+ end
48
+ end
49
+ end
@@ -30,7 +30,7 @@ describe Timber::Events, :rails_23 => true do
30
30
  end
31
31
 
32
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}})
33
+ built_event = Timber::Events.build({message: "Payment rejected", payment_rejected: {customer_id: "abcd1234", amount: 100}})
34
34
  expect(built_event).to be_kind_of(Timber::Events::Custom)
35
35
  expect(built_event.type).to eq(:payment_rejected)
36
36
  expect(built_event.message).to eq("Payment rejected")
@@ -58,15 +58,15 @@ describe Timber::LogDevices::HTTP do
58
58
 
59
59
  it "should add a request to the queue" do
60
60
  http = described_class.new("MYKEY", threads: false)
61
- log_entry = Timber::LogEntry.new("INFO", time, nil, "test log message 1", nil, nil)
61
+ log_entry = Timber::LogEntry.new("INFO", time, nil, "test log message 1", nil, nil, [])
62
62
  http.write(log_entry)
63
- log_entry = Timber::LogEntry.new("INFO", time, nil, "test log message 2", nil, nil)
63
+ log_entry = Timber::LogEntry.new("INFO", time, nil, "test log message 2", nil, nil, [])
64
64
  http.write(log_entry)
65
65
  http.send(:flush)
66
66
  request_queue = http.instance_variable_get(:@request_queue)
67
67
  request = request_queue.deq
68
68
  expect(request).to be_kind_of(Net::HTTP::Post)
69
- expect(request.body).to eq("\x92\x83\xA5level\xA4INFO\xA2dt\xBB2016-09-01T12:00:00.000000Z\xA7message\xB2test log message 1\x83\xA5level\xA4INFO\xA2dt\xBB2016-09-01T12:00:00.000000Z\xA7message\xB2test log message 2".force_encoding("ASCII-8BIT"))
69
+ expect(request.body).to start_with("\x92\x85\xA5level\xA4INFO\xA2dt\xBB2016-09-01T12:00:00.000000Z\xA7message\xB2test log message 1\xA7context\x81".force_encoding("ASCII-8BIT"))
70
70
 
71
71
  message_queue = http.instance_variable_get(:@msg_queue)
72
72
  expect(message_queue.size).to eq(0)
@@ -98,20 +98,20 @@ describe Timber::LogDevices::HTTP do
98
98
  it "should start a intervaled flush thread and flush on an interval" do
99
99
  stub = stub_request(:post, "https://logs.timber.io/frames").
100
100
  with(
101
- :body => "\x92\x83\xA5level\xA4INFO\xA2dt\xBB2016-09-01T12:00:00.000000Z\xA7message\xB2test log message 1\x83\xA5level\xA4INFO\xA2dt\xBB2016-09-01T12:00:00.000000Z\xA7message\xB2test log message 2".force_encoding("ASCII-8BIT"),
101
+ :body => start_with("\x92\x85\xA5level\xA4INFO\xA2dt\xBB2016-09-01T12:00:00.000000Z\xA7message\xB2test log message 1\xA7context\x81\xA6system".force_encoding("ASCII-8BIT")),
102
102
  :headers => {
103
103
  'Accept' => 'application/json',
104
104
  'Authorization' => 'Basic TVlLRVk=',
105
105
  'Content-Type' => 'application/msgpack',
106
- 'User-Agent' => "Timber Ruby Gem/#{Timber::VERSION}"
106
+ 'User-Agent' => "Timber Ruby/#{Timber::VERSION} (HTTP)"
107
107
  }
108
108
  ).
109
109
  to_return(:status => 200, :body => "", :headers => {})
110
110
 
111
111
  http = described_class.new("MYKEY", flush_interval: 0.1)
112
- log_entry = Timber::LogEntry.new("INFO", time, nil, "test log message 1", nil, nil)
112
+ log_entry = Timber::LogEntry.new("INFO", time, nil, "test log message 1", nil, nil, [])
113
113
  http.write(log_entry)
114
- log_entry = Timber::LogEntry.new("INFO", time, nil, "test log message 2", nil, nil)
114
+ log_entry = Timber::LogEntry.new("INFO", time, nil, "test log message 2", nil, nil, [])
115
115
  http.write(log_entry)
116
116
  sleep 0.3
117
117
 
@@ -0,0 +1,15 @@
1
+ require "spec_helper"
2
+
3
+ describe Timber::LogEntry, :rails_23 => true do
4
+ describe "#to_msgpack" do
5
+ let(:time) { Time.utc(2016, 9, 1, 12, 0, 0) }
6
+
7
+ it "should encode properly with an event and context" do
8
+ event = Timber::Events::Custom.new(type: :event_type, message: "event_message", data: {a: 1})
9
+ context = {custom: Timber::Contexts::Custom.new(type: :context_type, data: {b: 1})}
10
+ log_entry = described_class.new("INFO", time, nil, "log message", context, event, [])
11
+ msgpack = log_entry.to_msgpack
12
+ expect(msgpack).to start_with("\x86\xA5level\xA4INFO\xA2dt\xBB2016-09-01T12:00:00.000000Z".force_encoding("ASCII-8BIT"))
13
+ end
14
+ end
15
+ end
@@ -15,7 +15,7 @@ describe Timber::Logger, :rails_23 => true do
15
15
 
16
16
  it "should accept strings" do
17
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")
18
+ expect(io.string).to start_with("this is a test @timber.io {\"level\":\"info\",\"dt\":\"2016-09-01T12:00:00.000000Z\"")
19
19
  end
20
20
 
21
21
  context "with a context" do
@@ -37,33 +37,37 @@ describe Timber::Logger, :rails_23 => true do
37
37
  it "should snapshot and include the context" do
38
38
  expect(Timber::CurrentContext.instance).to receive(:snapshot).and_call_original
39
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")
40
+ expect(io.string).to start_with("this is a test @timber.io {\"level\":\"info\",\"dt\":\"2016-09-01T12:00:00.000000Z\"")
41
+ expect(io.string).to include("\"http\":{\"method\":\"POST\",\"path\":\"/checkout\",\"remote_addr\":\"123.456.789.10\",\"request_id\":\"abcd1234\"}")
41
42
  end
42
43
  end
43
44
 
44
45
  it "should call and use Timber::Events.build" do
45
- message = {message: "payment rejected", type: :payment_rejected, data: {customer_id: "abcde1234", amount: 100}}
46
+ message = {message: "payment rejected", payment_rejected: {customer_id: "abcde1234", amount: 100}}
46
47
  expect(Timber::Events).to receive(:build).with(message).and_call_original
47
48
  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
+ expect(io.string).to start_with("payment rejected @timber.io {\"level\":\"info\",\"dt\":\"2016-09-01T12:00:00.000000Z\",")
50
+ expect(io.string).to include("\"event\":{\"server_side_app\":{\"custom\":{\"payment_rejected\":{\"customer_id\":\"abcde1234\",\"amount\":100}}}}")
49
51
  end
50
52
 
51
53
  it "should log properly when an event is passed" do
52
54
  message = Timber::Events::SQLQuery.new(sql: "select * from users", time_ms: 56, message: "select * from users")
53
55
  logger.info(message)
54
- expect(io.string).to eq("select * from users @timber.io {\"level\":\"info\",\"dt\":\"2016-09-01T12:00:00.000000Z\",\"event\":{\"sql_query\":{\"sql\":\"select * from users\",\"time_ms\":56}}}\n")
56
+ expect(io.string).to start_with("select * from users @timber.io {\"level\":\"info\",\"dt\":\"2016-09-01T12:00:00.000000Z\",")
57
+ expect(io.string).to include("\"event\":{\"server_side_app\":{\"sql_query\":{\"sql\":\"select * from users\",\"time_ms\":56.0}}}")
55
58
  end
56
59
 
57
60
  it "should allow functions" do
58
61
  logger.info do
59
- {message: "payment rejected", type: :payment_rejected, data: {customer_id: "abcde1234", amount: 100}}
62
+ {message: "payment rejected", payment_rejected: {customer_id: "abcde1234", amount: 100}}
60
63
  end
61
- 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")
64
+ expect(io.string).to start_with("payment rejected @timber.io {\"level\":\"info\",\"dt\":\"2016-09-01T12:00:00.000000Z\",")
65
+ expect(io.string).to include("\"event\":{\"server_side_app\":{\"custom\":{\"payment_rejected\":{\"customer_id\":\"abcde1234\",\"amount\":100}}}}")
62
66
  end
63
67
 
64
68
  it "should escape new lines" do
65
69
  logger.info "first\nsecond"
66
- expect(io.string).to eq("first\\nsecond @timber.io {\"level\":\"info\",\"dt\":\"2016-09-01T12:00:00.000000Z\"}\n")
70
+ expect(io.string).to start_with("first\\nsecond @timber.io")
67
71
  end
68
72
  end
69
73
 
@@ -72,7 +76,7 @@ describe Timber::Logger, :rails_23 => true do
72
76
 
73
77
  it "should log in the correct format" do
74
78
  logger.info("this is a test")
75
- expect(io.string).to eq("{\"level\":\"info\",\"dt\":\"2016-09-01T12:00:00.000000Z\",\"message\":\"this is a test\"}\n")
79
+ expect(io.string).to start_with("{\"level\":\"info\",\"dt\":\"2016-09-01T12:00:00.000000Z\",\"message\":\"this is a test\"")
76
80
  end
77
81
  end
78
82
 
@@ -85,7 +89,7 @@ describe Timber::Logger, :rails_23 => true do
85
89
  logger.tagged("tag") do
86
90
  logger.info(message)
87
91
  end
88
- expect(io.string).to eq("select * from users @timber.io {\"level\":\"info\",\"dt\":\"2016-09-01T12:00:00.000000Z\",\"event\":{\"sql_query\":{\"sql\":\"select * from users\",\"time_ms\":56}},\"context\":{\"tags\":[\"tag\"]}}\n")
92
+ expect(io.string).to include("\"tags\":[\"tag\"]")
89
93
  end
90
94
  end
91
95
  end
@@ -55,13 +55,12 @@ describe Timber::Probes::ActionControllerLogSubscriber do
55
55
  # Rails uses this to calculate the view runtime below
56
56
  allow(Benchmark).to receive(:ms).and_return(1).and_yield
57
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")
58
+ lines = io.string.split("\n")
59
+ expect(lines.length).to eq(2)
60
+ expect(lines[0]).to start_with('Processing by LogSubscriberController#index as HTML @timber.io {"level":"info","dt":"2016-09-01T12:00:00.000000Z"')
61
+ expect(lines[0]).to include('"event":{"server_side_app":{"controller_call":{"controller":"LogSubscriberController","action":"index"}}}')
62
+ expect(lines[1]).to start_with('Completed 200 OK in 0.0ms (Views: 1.0ms) @timber.io {"level":"info","dt":"2016-09-01T12:00:00.000000Z"')
63
+ expect(lines[1]).to include('"event":{"server_side_app":{"http_server_response":{"status":200,"time_ms":0.0}}}')
65
64
  end
66
65
  end
67
66
  end