timber 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|