timber 1.0.3
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 +7 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.yardopts +6 -0
- data/Appraisals +41 -0
- data/Gemfile +30 -0
- data/LICENSE.md +15 -0
- data/README.md +194 -0
- data/circle.yml +33 -0
- data/lib/timber/config.rb +18 -0
- data/lib/timber/context.rb +17 -0
- data/lib/timber/contexts/custom.rb +27 -0
- data/lib/timber/contexts/http.rb +28 -0
- data/lib/timber/contexts/organization.rb +35 -0
- data/lib/timber/contexts/user.rb +36 -0
- data/lib/timber/contexts.rb +10 -0
- data/lib/timber/current_context.rb +43 -0
- data/lib/timber/event.rb +17 -0
- data/lib/timber/events/controller_call.rb +40 -0
- data/lib/timber/events/custom.rb +42 -0
- data/lib/timber/events/exception.rb +35 -0
- data/lib/timber/events/http_request.rb +50 -0
- data/lib/timber/events/http_response.rb +36 -0
- data/lib/timber/events/sql_query.rb +26 -0
- data/lib/timber/events/template_render.rb +26 -0
- data/lib/timber/events.rb +37 -0
- data/lib/timber/frameworks/rails.rb +13 -0
- data/lib/timber/frameworks.rb +19 -0
- data/lib/timber/log_devices/http.rb +87 -0
- data/lib/timber/log_devices.rb +8 -0
- data/lib/timber/log_entry.rb +59 -0
- data/lib/timber/logger.rb +142 -0
- data/lib/timber/probe.rb +23 -0
- data/lib/timber/probes/action_controller_log_subscriber/log_subscriber.rb +64 -0
- data/lib/timber/probes/action_controller_log_subscriber.rb +20 -0
- data/lib/timber/probes/action_dispatch_debug_exceptions.rb +80 -0
- data/lib/timber/probes/action_view_log_subscriber/log_subscriber.rb +62 -0
- data/lib/timber/probes/action_view_log_subscriber.rb +20 -0
- data/lib/timber/probes/active_record_log_subscriber/log_subscriber.rb +29 -0
- data/lib/timber/probes/active_record_log_subscriber.rb +20 -0
- data/lib/timber/probes/rack_http_context.rb +51 -0
- data/lib/timber/probes/rails_rack_logger.rb +76 -0
- data/lib/timber/probes.rb +21 -0
- data/lib/timber/util/active_support_log_subscriber.rb +33 -0
- data/lib/timber/util/hash.rb +14 -0
- data/lib/timber/util.rb +8 -0
- data/lib/timber/version.rb +3 -0
- data/lib/timber.rb +22 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/support/action_controller.rb +4 -0
- data/spec/support/action_view.rb +4 -0
- data/spec/support/active_record.rb +28 -0
- data/spec/support/coveralls.rb +2 -0
- data/spec/support/rails/templates/_partial.html +1 -0
- data/spec/support/rails/templates/template.html +1 -0
- data/spec/support/rails.rb +37 -0
- data/spec/support/simplecov.rb +9 -0
- data/spec/support/socket_hostname.rb +12 -0
- data/spec/support/timber.rb +4 -0
- data/spec/support/timecop.rb +3 -0
- data/spec/support/webmock.rb +2 -0
- data/spec/timber/events_spec.rb +55 -0
- data/spec/timber/log_devices/http_spec.rb +62 -0
- data/spec/timber/logger_spec.rb +89 -0
- data/spec/timber/probes/action_controller_log_subscriber_spec.rb +70 -0
- data/spec/timber/probes/action_dispatch_debug_exceptions_spec.rb +51 -0
- data/spec/timber/probes/action_view_log_subscriber_spec.rb +61 -0
- data/spec/timber/probes/active_record_log_subscriber_spec.rb +52 -0
- data/spec/timber/probes/rack_http_context_spec.rb +54 -0
- data/spec/timber/probes/rails_rack_logger_spec.rb +46 -0
- data/timber.gemspec +22 -0
- metadata +149 -0
@@ -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,89 @@
|
|
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 log properly when an event is passed" do
|
52
|
+
message = Timber::Events::SQLQuery.new(sql: "select * from users", time_ms: 56, message: "select * from users")
|
53
|
+
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")
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should allow functions" do
|
58
|
+
logger.info do
|
59
|
+
{message: "payment rejected", type: :payment_rejected, data: {customer_id: "abcde1234", amount: 100}}
|
60
|
+
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")
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should escape new lines" do
|
65
|
+
logger.info "first\nsecond"
|
66
|
+
expect(io.string).to eq("first\\nsecond @timber.io {\"level\":\"info\",\"dt\":\"2016-09-01T12:00:00.000000Z\"}\n")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "with the :json format" do
|
71
|
+
before(:each) { logger.formatter = Timber::Logger::JSONFormatter.new }
|
72
|
+
|
73
|
+
it "should log in the correct format" do
|
74
|
+
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")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "with TaggedLogging" do
|
80
|
+
let(:logger) { ActiveSupport::TaggedLogging.new(Timber::Logger.new(io)) }
|
81
|
+
|
82
|
+
it "should format properly with events" do
|
83
|
+
message = Timber::Events::SQLQuery.new(sql: "select * from users", time_ms: 56, message: "select * from users")
|
84
|
+
logger.info(message)
|
85
|
+
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")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
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
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Timber::Probes::ActionDispatchDebugExceptions 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|
|
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
|
+
Timecop.freeze(time) { example.run }
|
32
|
+
|
33
|
+
Object.send(:remove_const, :ExceptionController)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should set the context" do
|
37
|
+
mock_class
|
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)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Timber::Probes::ActionViewLogSubscriber 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 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
|
+
Timecop.freeze(time) { example.run }
|
32
|
+
|
33
|
+
Object.send(:remove_const, :ActionViewLogSubscriberController)
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#sql" do
|
37
|
+
it "should not log if the level is not sufficient" do
|
38
|
+
allow_any_instance_of(Timber::Probes::ActionViewLogSubscriber::LogSubscriber).to receive(:logger).and_return(logger)
|
39
|
+
dispatch_rails_request("/action_view_log_subscriber")
|
40
|
+
expect(io.string).to eq("")
|
41
|
+
end
|
42
|
+
|
43
|
+
context "with an info level" do
|
44
|
+
around(:each) do |example|
|
45
|
+
old_level = logger.level
|
46
|
+
logger.level = ::Logger::INFO
|
47
|
+
example.run
|
48
|
+
logger.level = old_level
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should log the controller call event" do
|
52
|
+
allow_any_instance_of(Timber::Probes::ActionViewLogSubscriber::LogSubscriber).to receive(:logger).and_return(logger)
|
53
|
+
dispatch_rails_request("/action_view_log_subscriber")
|
54
|
+
message = " Rendered spec/support/rails/templates/template.html (0.0ms) @timber.io {\"level\":\"info\",\"dt\":\"2016-09-01T12:00:00.000000Z\",\"event\":{\"template_render\":{\"name\":\"spec/support/rails/templates/template.html\",\"time_ms\":0.0}},\"context\":{\"http\":{\"method\":\"GET\",\"path\":\"/action_view_log_subscriber\",\"remote_addr\":\"123.456.789.10\",\"request_id\":\"unique-request-id-1234\"}}}\n"
|
55
|
+
expect(io.string).to eq(message)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Timber::Probes::ActiveRecordLogSubscriber 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::INFO
|
11
|
+
logger
|
12
|
+
end
|
13
|
+
|
14
|
+
around(:each) do |example|
|
15
|
+
old_logger = ::ActiveRecord::Base.logger
|
16
|
+
::ActiveRecord::Base.logger = logger
|
17
|
+
|
18
|
+
Timecop.freeze(time) { example.run }
|
19
|
+
|
20
|
+
::ActiveRecord::Base.logger = old_logger
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#sql" do
|
24
|
+
it "should not log if the level is not sufficient" do
|
25
|
+
User.order("users.id DESC").all.collect # collect kicks the sql because it is lazily executed
|
26
|
+
expect(io.string).to eq("")
|
27
|
+
end
|
28
|
+
|
29
|
+
context "with an info level" do
|
30
|
+
around(:each) do |example|
|
31
|
+
old_level = logger.level
|
32
|
+
logger.level = ::Logger::DEBUG
|
33
|
+
example.run
|
34
|
+
logger.level = old_level
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should log the sql query" do
|
38
|
+
User.order("users.id DESC").all.collect # collect kicks the sql because it is lazily executed
|
39
|
+
message = " \e[1m\e[36mUser Load (0.0ms)\e[0m \e[1m\e[34mSELECT \"users\".* FROM \"users\" ORDER BY users.id DESC\e[0m @timber.io {\"level\":\"debug\",\"dt\":\"2016-09-01T12:00:00.000000Z\",\"event\":{\"sql_query\":{\"sql\":\"SELECT \\\"users\\\".* FROM \\\"users\\\" ORDER BY users.id DESC\",\"time_ms\":0.0}}}\n"
|
40
|
+
# Rails 4.X adds random spaces :/
|
41
|
+
string = io.string.gsub(" ORDER BY", " ORDER BY")
|
42
|
+
string = string.gsub(" ORDER BY", " ORDER BY")
|
43
|
+
expect(string).to include("users.id DESC")
|
44
|
+
expect(string).to include("@timber.io")
|
45
|
+
expect(string).to include("\"level\":\"debug\"")
|
46
|
+
expect(string).to include("\"sql\":")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Timber::Probes::RackHTTPContext do
|
4
|
+
describe described_class::Middleware 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] = 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
|
+
Timecop.freeze(time) { example.run }
|
32
|
+
|
33
|
+
Object.send(:remove_const, :RackHttpController)
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#process" do
|
37
|
+
it "should set the context" do
|
38
|
+
allow(Benchmark).to receive(:ms).and_return(1).and_yield
|
39
|
+
allow_any_instance_of(Timber::Probes::ActionControllerLogSubscriber::LogSubscriber).to receive(:logger).and_return(logger)
|
40
|
+
|
41
|
+
dispatch_rails_request("/rack_http")
|
42
|
+
http_context = Thread.current[:_timber_context][:http]
|
43
|
+
|
44
|
+
expect(http_context).to be_kind_of(Timber::Contexts::HTTP)
|
45
|
+
expect(http_context.method).to eq("GET")
|
46
|
+
expect(http_context.path).to eq("/rack_http")
|
47
|
+
expect(http_context.remote_addr).to eq("123.456.789.10")
|
48
|
+
expect(http_context.request_id).to_not be_nil
|
49
|
+
message = "Processing by RackHttpController#index as HTML @timber.io {\"level\":\"info\",\"dt\":\"2016-09-01T12:00:00.000000Z\",\"event\":{\"controller_call\":{\"controller\":\"RackHttpController\",\"action\":\"index\"}},\"context\":{\"http\":{\"method\":\"GET\",\"path\":\"/rack_http\",\"remote_addr\":\"123.456.789.10\",\"request_id\":\"unique-request-id-1234\"}}}\nCompleted 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\":\"/rack_http\",\"remote_addr\":\"123.456.789.10\",\"request_id\":\"unique-request-id-1234\"}}}\n"
|
50
|
+
expect(io.string).to eq(message)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Timber::Probes::RailsRackLogger do
|
4
|
+
describe described_class::InstanceMethods 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 RailsRackLoggerController < ActionController::Base
|
15
|
+
layout nil
|
16
|
+
|
17
|
+
def index
|
18
|
+
render json: {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def method_for_action(action_name)
|
22
|
+
action_name
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
::RailsApp.routes.draw do
|
27
|
+
get '/rails_rack_logger' => 'rails_rack_logger#index'
|
28
|
+
end
|
29
|
+
|
30
|
+
Timecop.freeze(time) { example.run }
|
31
|
+
|
32
|
+
Object.send(:remove_const, :RailsRackLoggerController)
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#started_request_message" do
|
36
|
+
it "should set the context" do
|
37
|
+
allow(::Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new("production")) # Rails 3.2.X
|
38
|
+
allow(::Rails).to receive(:logger).and_return(logger) # Rails 3.2.X
|
39
|
+
allow_any_instance_of(::Rails::Rack::Logger).to receive(:logger).and_return(logger)
|
40
|
+
dispatch_rails_request("/rails_rack_logger")
|
41
|
+
message = "Started GET \"/rails_rack_logger\" for 123.456.789.10 @timber.io {\"level\":\"info\",\"dt\":\"2016-09-01T12:00:00.000000Z\",\"event\":{\"http_request\":{\"host\":\"example.org\",\"method\":\"GET\",\"path\":\"/rails_rack_logger\",\"port\":80,\"headers\":{\"remote_addr\":\"123.456.789.10\",\"request_id\":\"unique-request-id-1234\"}}},\"context\":{\"http\":{\"method\":\"GET\",\"path\":\"/rails_rack_logger\",\"remote_addr\":\"123.456.789.10\",\"request_id\":\"unique-request-id-1234\"}}}\n"
|
42
|
+
expect(io.string).to eq(message)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/timber.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$LOAD_PATH.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "timber/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "timber"
|
7
|
+
s.version = Timber::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Timber Technologies, Inc."]
|
10
|
+
s.email = ["hi@timber.io"]
|
11
|
+
s.homepage = "http://timber.io"
|
12
|
+
s.summary = "Logs you'll actually use."
|
13
|
+
|
14
|
+
s.required_ruby_version = '>= 1.9.0'
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_dependency("msgpack", "~> 1.0")
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: timber
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Timber Technologies, Inc.
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-12-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: msgpack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
description:
|
28
|
+
email:
|
29
|
+
- hi@timber.io
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- ".gitignore"
|
35
|
+
- ".rspec"
|
36
|
+
- ".yardopts"
|
37
|
+
- Appraisals
|
38
|
+
- Gemfile
|
39
|
+
- LICENSE.md
|
40
|
+
- README.md
|
41
|
+
- circle.yml
|
42
|
+
- lib/timber.rb
|
43
|
+
- lib/timber/config.rb
|
44
|
+
- lib/timber/context.rb
|
45
|
+
- lib/timber/contexts.rb
|
46
|
+
- lib/timber/contexts/custom.rb
|
47
|
+
- lib/timber/contexts/http.rb
|
48
|
+
- lib/timber/contexts/organization.rb
|
49
|
+
- lib/timber/contexts/user.rb
|
50
|
+
- lib/timber/current_context.rb
|
51
|
+
- lib/timber/event.rb
|
52
|
+
- lib/timber/events.rb
|
53
|
+
- lib/timber/events/controller_call.rb
|
54
|
+
- lib/timber/events/custom.rb
|
55
|
+
- lib/timber/events/exception.rb
|
56
|
+
- lib/timber/events/http_request.rb
|
57
|
+
- lib/timber/events/http_response.rb
|
58
|
+
- lib/timber/events/sql_query.rb
|
59
|
+
- lib/timber/events/template_render.rb
|
60
|
+
- lib/timber/frameworks.rb
|
61
|
+
- lib/timber/frameworks/rails.rb
|
62
|
+
- lib/timber/log_devices.rb
|
63
|
+
- lib/timber/log_devices/http.rb
|
64
|
+
- lib/timber/log_entry.rb
|
65
|
+
- lib/timber/logger.rb
|
66
|
+
- lib/timber/probe.rb
|
67
|
+
- lib/timber/probes.rb
|
68
|
+
- lib/timber/probes/action_controller_log_subscriber.rb
|
69
|
+
- lib/timber/probes/action_controller_log_subscriber/log_subscriber.rb
|
70
|
+
- lib/timber/probes/action_dispatch_debug_exceptions.rb
|
71
|
+
- lib/timber/probes/action_view_log_subscriber.rb
|
72
|
+
- lib/timber/probes/action_view_log_subscriber/log_subscriber.rb
|
73
|
+
- lib/timber/probes/active_record_log_subscriber.rb
|
74
|
+
- lib/timber/probes/active_record_log_subscriber/log_subscriber.rb
|
75
|
+
- lib/timber/probes/rack_http_context.rb
|
76
|
+
- lib/timber/probes/rails_rack_logger.rb
|
77
|
+
- lib/timber/util.rb
|
78
|
+
- lib/timber/util/active_support_log_subscriber.rb
|
79
|
+
- lib/timber/util/hash.rb
|
80
|
+
- lib/timber/version.rb
|
81
|
+
- spec/spec_helper.rb
|
82
|
+
- spec/support/action_controller.rb
|
83
|
+
- spec/support/action_view.rb
|
84
|
+
- spec/support/active_record.rb
|
85
|
+
- spec/support/coveralls.rb
|
86
|
+
- spec/support/rails.rb
|
87
|
+
- spec/support/rails/templates/_partial.html
|
88
|
+
- spec/support/rails/templates/template.html
|
89
|
+
- spec/support/simplecov.rb
|
90
|
+
- spec/support/socket_hostname.rb
|
91
|
+
- spec/support/timber.rb
|
92
|
+
- spec/support/timecop.rb
|
93
|
+
- spec/support/webmock.rb
|
94
|
+
- spec/timber/events_spec.rb
|
95
|
+
- spec/timber/log_devices/http_spec.rb
|
96
|
+
- spec/timber/logger_spec.rb
|
97
|
+
- spec/timber/probes/action_controller_log_subscriber_spec.rb
|
98
|
+
- spec/timber/probes/action_dispatch_debug_exceptions_spec.rb
|
99
|
+
- spec/timber/probes/action_view_log_subscriber_spec.rb
|
100
|
+
- spec/timber/probes/active_record_log_subscriber_spec.rb
|
101
|
+
- spec/timber/probes/rack_http_context_spec.rb
|
102
|
+
- spec/timber/probes/rails_rack_logger_spec.rb
|
103
|
+
- timber.gemspec
|
104
|
+
homepage: http://timber.io
|
105
|
+
licenses: []
|
106
|
+
metadata: {}
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options: []
|
109
|
+
require_paths:
|
110
|
+
- lib
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: 1.9.0
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
requirements: []
|
122
|
+
rubyforge_project:
|
123
|
+
rubygems_version: 2.6.8
|
124
|
+
signing_key:
|
125
|
+
specification_version: 4
|
126
|
+
summary: Logs you'll actually use.
|
127
|
+
test_files:
|
128
|
+
- spec/spec_helper.rb
|
129
|
+
- spec/support/action_controller.rb
|
130
|
+
- spec/support/action_view.rb
|
131
|
+
- spec/support/active_record.rb
|
132
|
+
- spec/support/coveralls.rb
|
133
|
+
- spec/support/rails.rb
|
134
|
+
- spec/support/rails/templates/_partial.html
|
135
|
+
- spec/support/rails/templates/template.html
|
136
|
+
- spec/support/simplecov.rb
|
137
|
+
- spec/support/socket_hostname.rb
|
138
|
+
- spec/support/timber.rb
|
139
|
+
- spec/support/timecop.rb
|
140
|
+
- spec/support/webmock.rb
|
141
|
+
- spec/timber/events_spec.rb
|
142
|
+
- spec/timber/log_devices/http_spec.rb
|
143
|
+
- spec/timber/logger_spec.rb
|
144
|
+
- spec/timber/probes/action_controller_log_subscriber_spec.rb
|
145
|
+
- spec/timber/probes/action_dispatch_debug_exceptions_spec.rb
|
146
|
+
- spec/timber/probes/action_view_log_subscriber_spec.rb
|
147
|
+
- spec/timber/probes/active_record_log_subscriber_spec.rb
|
148
|
+
- spec/timber/probes/rack_http_context_spec.rb
|
149
|
+
- spec/timber/probes/rails_rack_logger_spec.rb
|