timberio 1.0.0.beta1
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/.codeclimate.yml +34 -0
- data/.gitignore +14 -0
- data/Appraisals +37 -0
- data/Gemfile +22 -0
- data/LICENSE +38 -0
- data/README.md +22 -0
- data/Rakefile +4 -0
- data/TODO +4 -0
- data/benchmark/README.md +26 -0
- data/benchmark/rails_request.rb +68 -0
- data/benchmark/support/rails.rb +69 -0
- data/circle.yml +27 -0
- data/docs/installation/rails_on_heroku.md +31 -0
- data/docs/installation/rails_over_http.md +22 -0
- data/gemfiles/rails_3.0.X.gemfile +25 -0
- data/gemfiles/rails_3.1.X.gemfile +25 -0
- data/gemfiles/rails_3.2.X.gemfile +25 -0
- data/gemfiles/rails_4.0.X.gemfile +26 -0
- data/gemfiles/rails_4.1.X.gemfile +26 -0
- data/gemfiles/rails_4.2.X.gemfile +26 -0
- data/gemfiles/rails_5.0.X.gemfile +26 -0
- data/gemfiles/rails_edge.gemfile +27 -0
- data/lib/timber/api_settings.rb +17 -0
- data/lib/timber/bootstrap.rb +45 -0
- data/lib/timber/config.rb +25 -0
- data/lib/timber/context.rb +76 -0
- data/lib/timber/context_snapshot.rb +64 -0
- data/lib/timber/contexts/dynamic_values.rb +59 -0
- data/lib/timber/contexts/exception.rb +40 -0
- data/lib/timber/contexts/http_request.rb +22 -0
- data/lib/timber/contexts/http_requests/action_controller_specific.rb +48 -0
- data/lib/timber/contexts/http_requests/rack/params.rb +26 -0
- data/lib/timber/contexts/http_requests/rack.rb +105 -0
- data/lib/timber/contexts/http_response.rb +19 -0
- data/lib/timber/contexts/http_responses/action_controller.rb +76 -0
- data/lib/timber/contexts/logger.rb +33 -0
- data/lib/timber/contexts/organization.rb +33 -0
- data/lib/timber/contexts/organizations/action_controller.rb +34 -0
- data/lib/timber/contexts/server.rb +21 -0
- data/lib/timber/contexts/servers/heroku_specific.rb +48 -0
- data/lib/timber/contexts/sql_queries/active_record.rb +30 -0
- data/lib/timber/contexts/sql_queries/active_record_specific/binds.rb +37 -0
- data/lib/timber/contexts/sql_queries/active_record_specific.rb +59 -0
- data/lib/timber/contexts/sql_query.rb +18 -0
- data/lib/timber/contexts/template_render.rb +17 -0
- data/lib/timber/contexts/template_renders/action_view.rb +29 -0
- data/lib/timber/contexts/template_renders/action_view_specific.rb +51 -0
- data/lib/timber/contexts/user.rb +39 -0
- data/lib/timber/contexts/users/action_controller.rb +34 -0
- data/lib/timber/contexts.rb +23 -0
- data/lib/timber/current_context.rb +58 -0
- data/lib/timber/current_line_indexes.rb +35 -0
- data/lib/timber/frameworks/rails.rb +24 -0
- data/lib/timber/frameworks.rb +21 -0
- data/lib/timber/internal_logger.rb +35 -0
- data/lib/timber/log_device.rb +40 -0
- data/lib/timber/log_devices/heroku_logplex/hybrid_formatter.rb +14 -0
- data/lib/timber/log_devices/heroku_logplex.rb +14 -0
- data/lib/timber/log_devices/http/log_pile.rb +86 -0
- data/lib/timber/log_devices/http/log_truck/delivery.rb +116 -0
- data/lib/timber/log_devices/http/log_truck.rb +87 -0
- data/lib/timber/log_devices/http.rb +28 -0
- data/lib/timber/log_devices/io/formatter.rb +46 -0
- data/lib/timber/log_devices/io/hybrid_formatter.rb +41 -0
- data/lib/timber/log_devices/io/hybrid_hidden_formatter.rb +36 -0
- data/lib/timber/log_devices/io/json_formatter.rb +11 -0
- data/lib/timber/log_devices/io/logfmt_formatter.rb +11 -0
- data/lib/timber/log_devices/io.rb +41 -0
- data/lib/timber/log_devices.rb +4 -0
- data/lib/timber/log_line.rb +33 -0
- data/lib/timber/logger.rb +20 -0
- data/lib/timber/macros/compactor.rb +16 -0
- data/lib/timber/macros/date_formatter.rb +9 -0
- data/lib/timber/macros/deep_merger.rb +11 -0
- data/lib/timber/macros/logfmt_encoder.rb +77 -0
- data/lib/timber/macros.rb +4 -0
- data/lib/timber/patterns/delegated_singleton.rb +21 -0
- data/lib/timber/patterns/to_json.rb +22 -0
- data/lib/timber/patterns/to_logfmt.rb +9 -0
- data/lib/timber/patterns.rb +3 -0
- data/lib/timber/probe.rb +21 -0
- data/lib/timber/probes/action_controller_base.rb +31 -0
- data/lib/timber/probes/action_dispatch_debug_exceptions.rb +57 -0
- data/lib/timber/probes/active_support_log_subscriber/action_controller.rb +15 -0
- data/lib/timber/probes/active_support_log_subscriber/action_view.rb +26 -0
- data/lib/timber/probes/active_support_log_subscriber/active_record.rb +13 -0
- data/lib/timber/probes/active_support_log_subscriber.rb +62 -0
- data/lib/timber/probes/heroku.rb +30 -0
- data/lib/timber/probes/logger.rb +31 -0
- data/lib/timber/probes/rack.rb +36 -0
- data/lib/timber/probes/server.rb +18 -0
- data/lib/timber/probes.rb +24 -0
- data/lib/timber/version.rb +3 -0
- data/lib/timber.rb +27 -0
- data/spec/spec_helper.rb +27 -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 +33 -0
- data/spec/support/simplecov.rb +9 -0
- data/spec/support/socket_hostname.rb +12 -0
- data/spec/support/timber.rb +23 -0
- data/spec/support/timecop.rb +3 -0
- data/spec/support/webmock.rb +2 -0
- data/spec/timber/bootstrap_spec.rb +31 -0
- data/spec/timber/context_snapshot_spec.rb +10 -0
- data/spec/timber/context_spec.rb +4 -0
- data/spec/timber/contexts/exception_spec.rb +34 -0
- data/spec/timber/contexts/organizations/action_controller_spec.rb +49 -0
- data/spec/timber/contexts/users/action_controller_spec.rb +65 -0
- data/spec/timber/current_line_indexes_spec.rb +40 -0
- data/spec/timber/frameworks/rails_spec.rb +9 -0
- data/spec/timber/log_devices/heroku_logplex_spec.rb +45 -0
- data/spec/timber/log_devices/http/log_truck/delivery_spec.rb +66 -0
- data/spec/timber/log_devices/http/log_truck_spec.rb +65 -0
- data/spec/timber/log_devices/io/hybrid_hidden_formatter_spec.rb +28 -0
- data/spec/timber/log_line_spec.rb +49 -0
- data/spec/timber/macros/compactor_spec.rb +19 -0
- data/spec/timber/macros/logfmt_encoder_spec.rb +89 -0
- data/spec/timber/patterns/to_json_spec.rb +40 -0
- data/spec/timber/probes/action_controller_base_spec.rb +43 -0
- data/spec/timber/probes/action_controller_log_subscriber/action_controller_spec.rb +35 -0
- data/spec/timber/probes/action_controller_log_subscriber/action_view_spec.rb +44 -0
- data/spec/timber/probes/action_controller_log_subscriber/active_record_spec.rb +26 -0
- data/spec/timber/probes/action_dispatch_debug_exceptions_spec.rb +45 -0
- data/spec/timber/probes/logger_spec.rb +20 -0
- data/spec/timber/probes/rack_spec.rb +26 -0
- data/timberio.gemspec +20 -0
- metadata +210 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Timber
|
|
2
|
+
module Contexts
|
|
3
|
+
module Organizations
|
|
4
|
+
class ActionController < Organization
|
|
5
|
+
DEFAULT_METHOD_NAME = :current_organization.freeze
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
attr_writer :method_name
|
|
9
|
+
|
|
10
|
+
def method_name
|
|
11
|
+
@method_name ||= DEFAULT_METHOD_NAME
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
attr_reader :controller
|
|
16
|
+
|
|
17
|
+
def initialize(controller)
|
|
18
|
+
@controller = controller
|
|
19
|
+
super()
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
def method_name
|
|
24
|
+
self.class.method_name
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def organization
|
|
28
|
+
return @organization if defined?(@organization)
|
|
29
|
+
@organization = controller.respond_to?(method_name, true) ? controller.send(method_name) : nil
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require "socket"
|
|
2
|
+
|
|
3
|
+
module Timber
|
|
4
|
+
module Contexts
|
|
5
|
+
class Server < Context
|
|
6
|
+
ROOT_KEY = :server.freeze
|
|
7
|
+
VERSION = 1.freeze
|
|
8
|
+
|
|
9
|
+
def hostname
|
|
10
|
+
@hostname ||= ::Socket.gethostname
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
def json_payload
|
|
15
|
+
@json_payload ||= Macros::DeepMerger.merge({
|
|
16
|
+
:hostname => hostname
|
|
17
|
+
}, super).freeze
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module Timber
|
|
2
|
+
module Contexts
|
|
3
|
+
module Servers
|
|
4
|
+
# Because this is a sub context we do not extend Server.
|
|
5
|
+
class HerokuSpecific < Context
|
|
6
|
+
ROOT_KEY = :heroku.freeze
|
|
7
|
+
VERSION = 1.freeze
|
|
8
|
+
DELIMITER = ".".freeze
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
def json_shell(&_block)
|
|
12
|
+
Server.json_shell { super }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
attr_reader :dyno
|
|
17
|
+
|
|
18
|
+
def initialize(dyno)
|
|
19
|
+
# Initialize should be as fast as possible since it is executed inline.
|
|
20
|
+
# Hence the lazy methods below.
|
|
21
|
+
@dyno = dyno
|
|
22
|
+
super()
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def process_type
|
|
26
|
+
@process_type ||= parts.first
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def dyno_id
|
|
30
|
+
@dyno_id ||= parts.last
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
def parts
|
|
35
|
+
@parts ||= dyno.split(DELIMITER)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def json_payload
|
|
39
|
+
@json_payload ||= Macros::DeepMerger.merge({
|
|
40
|
+
# order is relevant for logfmt styling
|
|
41
|
+
:process_type => process_type,
|
|
42
|
+
:dyno_id => dyno_id
|
|
43
|
+
}, super).freeze.freeze
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Timber
|
|
2
|
+
module Contexts
|
|
3
|
+
module SQLQueries
|
|
4
|
+
class ActiveRecord < SQLQuery
|
|
5
|
+
attr_reader :log_subscriber, :event
|
|
6
|
+
|
|
7
|
+
def initialize(log_subscriber, event)
|
|
8
|
+
# Initialize should be as fast as possible since it is executed inline.
|
|
9
|
+
# Hence the lazy methods below.
|
|
10
|
+
@log_subscriber = log_subscriber
|
|
11
|
+
@event = event
|
|
12
|
+
super()
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def sql
|
|
16
|
+
@sql ||= payload[:sql].try(:strip)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def time_ms
|
|
20
|
+
@time_ms ||= event.duration
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
def payload
|
|
25
|
+
event.payload
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Timber
|
|
2
|
+
module Contexts
|
|
3
|
+
module SQLQueries
|
|
4
|
+
class ActiveRecordSpecific < Context
|
|
5
|
+
class Binds < DynamicValues
|
|
6
|
+
attr_reader :log_subscriber, :binds
|
|
7
|
+
|
|
8
|
+
def initialize(log_subscriber, binds)
|
|
9
|
+
# Initialize should be as fast as possible since it is executed inline.
|
|
10
|
+
# Hence the lazy methods below.
|
|
11
|
+
@log_subscriber = log_subscriber
|
|
12
|
+
@binds = binds
|
|
13
|
+
super()
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
# Override values_array so that this is done in the background thread
|
|
18
|
+
def values_array
|
|
19
|
+
@values_array ||= binds.collect do |bind|
|
|
20
|
+
name, value = render_bind(bind)
|
|
21
|
+
{:name => name, :value => value}
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def render_bind(bind)
|
|
26
|
+
if bind.is_a?(Array)
|
|
27
|
+
# AR 4.2.X
|
|
28
|
+
log_subscriber.render_bind(*bind)
|
|
29
|
+
else
|
|
30
|
+
log_subscriber.render_bind(bind)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require "timber/contexts/sql_queries/active_record_specific/binds"
|
|
2
|
+
|
|
3
|
+
module Timber
|
|
4
|
+
module Contexts
|
|
5
|
+
module SQLQueries
|
|
6
|
+
# Because this is a sub context we extend Context
|
|
7
|
+
class ActiveRecordSpecific < Context
|
|
8
|
+
ROOT_KEY = :active_record.freeze
|
|
9
|
+
VERSION = 1.freeze
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
def json_shell(&_block)
|
|
13
|
+
SQLQuery.json_shell { super }
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
attr_reader :log_subscriber, :event
|
|
18
|
+
|
|
19
|
+
def initialize(log_subscriber, event)
|
|
20
|
+
# Initialize should be as fast as possible since it is executed inline.
|
|
21
|
+
# Hence the lazy methods below.
|
|
22
|
+
@log_subscriber = log_subscriber
|
|
23
|
+
@event = event
|
|
24
|
+
super()
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def binds
|
|
28
|
+
@binds ||= payload[:binds] && Binds.new(log_subscriber, payload[:binds])
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def connection_id
|
|
32
|
+
@connection_id ||= payload[:connection_id].try(:to_s)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def statement_name
|
|
36
|
+
@statement_name ||= payload[:statement_name]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def transaction_id
|
|
40
|
+
@transaction_id ||= event.transaction_id.try(:to_s)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
def json_payload
|
|
45
|
+
@json_payload ||= Macros::DeepMerger.merge({
|
|
46
|
+
:binds => binds.as_json,
|
|
47
|
+
:connection_id => connection_id,
|
|
48
|
+
:statement_name => statement_name,
|
|
49
|
+
:transaction_id => transaction_id
|
|
50
|
+
}, super).freeze
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def payload
|
|
54
|
+
event.payload
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Timber
|
|
2
|
+
module Contexts
|
|
3
|
+
# Base class for SQLQuery sub types.
|
|
4
|
+
class SQLQuery < Context
|
|
5
|
+
ROOT_KEY = :sql_query.freeze
|
|
6
|
+
VERSION = 1.freeze
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
def json_payload
|
|
10
|
+
@json_payload ||= Macros::DeepMerger.merge({
|
|
11
|
+
# order is relevant for logfmt styling
|
|
12
|
+
:sql => sql,
|
|
13
|
+
:time_ms => time_ms
|
|
14
|
+
}, super).freeze
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Timber
|
|
2
|
+
module Contexts
|
|
3
|
+
class TemplateRender < Context
|
|
4
|
+
ROOT_KEY = :template_render.freeze
|
|
5
|
+
VERSION = 1.freeze
|
|
6
|
+
|
|
7
|
+
private
|
|
8
|
+
def json_payload
|
|
9
|
+
@json_payload ||= Macros::DeepMerger.merge({
|
|
10
|
+
# order is relevant for logfmt styling
|
|
11
|
+
:name => name,
|
|
12
|
+
:time_ms => time_ms
|
|
13
|
+
}, super).freeze
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Timber
|
|
2
|
+
module Contexts
|
|
3
|
+
module TemplateRenders
|
|
4
|
+
class ActionView < TemplateRender
|
|
5
|
+
attr_reader :event
|
|
6
|
+
|
|
7
|
+
def initialize(event)
|
|
8
|
+
# Initialize should be as fast as possible since it is executed inline.
|
|
9
|
+
# Hence the lazy methods below.
|
|
10
|
+
@event = event
|
|
11
|
+
super()
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def name
|
|
15
|
+
@name ||= payload[:identifier]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def time_ms
|
|
19
|
+
@time_ms ||= event.duration
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
def payload
|
|
24
|
+
event.payload
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module Timber
|
|
2
|
+
module Contexts
|
|
3
|
+
module TemplateRenders
|
|
4
|
+
# Because this is a sub type we extend Context
|
|
5
|
+
class ActionViewSpecific < Context
|
|
6
|
+
ROOT_KEY = :action_view.freeze
|
|
7
|
+
VERSION = 1.freeze
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
def json_shell(&_block)
|
|
11
|
+
TemplateRender.json_shell { super }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
attr_reader :event
|
|
16
|
+
|
|
17
|
+
def initialize(event)
|
|
18
|
+
# Initialize should be as fast as possible since it is executed inline.
|
|
19
|
+
# Hence the lazy methods below.
|
|
20
|
+
@event = event
|
|
21
|
+
super()
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def cache_hits
|
|
25
|
+
@cache_hits ||= payload[:cache_hits]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def count
|
|
29
|
+
@count ||= payload[:count]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def layout
|
|
33
|
+
@layout ||= payload[:layout]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
def json_payload
|
|
38
|
+
@json_payload ||= Macros::DeepMerger.merge({
|
|
39
|
+
:cache_hits => cache_hits,
|
|
40
|
+
:count => count,
|
|
41
|
+
:layout => layout
|
|
42
|
+
}, super).freeze
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def payload
|
|
46
|
+
event.payload
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module Timber
|
|
2
|
+
module Contexts
|
|
3
|
+
class User < Context
|
|
4
|
+
ROOT_KEY = :user.freeze
|
|
5
|
+
VERSION = 1.freeze
|
|
6
|
+
|
|
7
|
+
attr_reader :user
|
|
8
|
+
|
|
9
|
+
def email
|
|
10
|
+
return @email if defined?(@email)
|
|
11
|
+
@email = user.respond_to?(:email) ? user.email : nil
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def id
|
|
15
|
+
return @id if defined?(@id)
|
|
16
|
+
@id = user.respond_to?(:id) ? user.id : nil
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def name
|
|
20
|
+
return @name if defined?(@name)
|
|
21
|
+
@name = user.respond_to?(:name) ? user.name : nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def valid?
|
|
25
|
+
!user.nil?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
def json_payload
|
|
30
|
+
@json_payload ||= Macros::DeepMerger.merge({
|
|
31
|
+
# order is relevant for logfmt styling
|
|
32
|
+
:id => id,
|
|
33
|
+
:name => name,
|
|
34
|
+
:email => email
|
|
35
|
+
}, super).freeze
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Timber
|
|
2
|
+
module Contexts
|
|
3
|
+
module Users
|
|
4
|
+
class ActionController < User
|
|
5
|
+
DEFAULT_METHOD_NAME = :current_user.freeze
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
attr_writer :method_name
|
|
9
|
+
|
|
10
|
+
def method_name
|
|
11
|
+
@method_name ||= DEFAULT_METHOD_NAME
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
attr_reader :controller
|
|
16
|
+
|
|
17
|
+
def initialize(controller)
|
|
18
|
+
@controller = controller
|
|
19
|
+
super()
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
def method_name
|
|
24
|
+
self.class.method_name
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def user
|
|
28
|
+
return @user if defined?(@user)
|
|
29
|
+
@user = controller.respond_to?(method_name, true) ? controller.send(method_name) : nil
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Base, order is relevant
|
|
2
|
+
require "timber/contexts/dynamic_values"
|
|
3
|
+
require "timber/contexts/exception"
|
|
4
|
+
require "timber/contexts/http_request"
|
|
5
|
+
require "timber/contexts/http_response"
|
|
6
|
+
require "timber/contexts/logger"
|
|
7
|
+
require "timber/contexts/organization"
|
|
8
|
+
require "timber/contexts/server"
|
|
9
|
+
require "timber/contexts/sql_query"
|
|
10
|
+
require "timber/contexts/template_render"
|
|
11
|
+
require "timber/contexts/user"
|
|
12
|
+
|
|
13
|
+
# Sub contexts, order is relevant
|
|
14
|
+
require "timber/contexts/http_requests/rack"
|
|
15
|
+
require "timber/contexts/http_requests/action_controller_specific"
|
|
16
|
+
require "timber/contexts/http_responses/action_controller"
|
|
17
|
+
require "timber/contexts/organizations/action_controller"
|
|
18
|
+
require "timber/contexts/servers/heroku_specific"
|
|
19
|
+
require "timber/contexts/sql_queries/active_record"
|
|
20
|
+
require "timber/contexts/sql_queries/active_record_specific"
|
|
21
|
+
require "timber/contexts/template_renders/action_view"
|
|
22
|
+
require "timber/contexts/template_renders/action_view_specific"
|
|
23
|
+
require "timber/contexts/users/action_controller"
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require "set"
|
|
2
|
+
|
|
3
|
+
module Timber
|
|
4
|
+
# Holds the current context in the current thread's memory.
|
|
5
|
+
# This context gets copied as each log line is written.
|
|
6
|
+
class CurrentContext
|
|
7
|
+
THREAD_NAMESPACE = :_timber_current_context.freeze
|
|
8
|
+
STACK_KEYNAME = :stack.freeze
|
|
9
|
+
PRECISION = 8.freeze
|
|
10
|
+
|
|
11
|
+
include Patterns::DelegatedSingleton
|
|
12
|
+
|
|
13
|
+
# Adds a context to the current stack.
|
|
14
|
+
def add(*contexts, &_block)
|
|
15
|
+
contexts = contexts.compact
|
|
16
|
+
contexts.each do |context|
|
|
17
|
+
stack << context
|
|
18
|
+
end
|
|
19
|
+
block_given? ? yield : self
|
|
20
|
+
ensure
|
|
21
|
+
remove(*contexts) if block_given?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Get a specific context type off the stack
|
|
25
|
+
def get(type)
|
|
26
|
+
stack.find { |context| context.is_a?(type) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Removes the contexts from the current stack.
|
|
30
|
+
def remove(*contexts)
|
|
31
|
+
# Ensure we clear the cacke when the stack changes
|
|
32
|
+
contexts.each do |context|
|
|
33
|
+
CurrentLineIndexes.context_removed(context)
|
|
34
|
+
stack.delete(context)
|
|
35
|
+
end
|
|
36
|
+
self
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Used to efficiently clone the context
|
|
40
|
+
def snapshot
|
|
41
|
+
# Cloning the array is efficient and will point to the same objects.
|
|
42
|
+
Timber::ContextSnapshot.new
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def valid_stack
|
|
46
|
+
stack.select(&:valid?)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
def stack
|
|
51
|
+
storage[STACK_KEYNAME] ||= []
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def storage
|
|
55
|
+
Thread.current[THREAD_NAMESPACE] ||= {}
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Timber
|
|
2
|
+
# Holds the log line counts on a per context basis in memory.
|
|
3
|
+
class CurrentLineIndexes
|
|
4
|
+
THREAD_NAMESPACE = :_timber_current_line_counts.freeze
|
|
5
|
+
|
|
6
|
+
include Patterns::DelegatedSingleton
|
|
7
|
+
|
|
8
|
+
def context_removed(context)
|
|
9
|
+
indexes.delete(context)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def indexes
|
|
13
|
+
Thread.current[THREAD_NAMESPACE] ||= {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def log_line_added(_log_line)
|
|
17
|
+
CurrentContext.valid_stack.each do |context|
|
|
18
|
+
if indexes.key?(context)
|
|
19
|
+
indexes[context] += 1
|
|
20
|
+
else
|
|
21
|
+
indexes[context] = 0
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def reset!
|
|
27
|
+
indexes.clear
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def snapshot
|
|
31
|
+
# No need to cache, this is blown out for each log line
|
|
32
|
+
LineIndexesSnapshot.new(self)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Timber
|
|
2
|
+
module Frameworks
|
|
3
|
+
module Rails
|
|
4
|
+
class Railtie < ::Rails::Railtie
|
|
5
|
+
config.timber = Config.instance
|
|
6
|
+
config.before_initialize do
|
|
7
|
+
Bootstrap.bootstrap!(config.app_middleware, ::Rails::Rack::Logger)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.base_logger(logdev)
|
|
12
|
+
defined?(::ActiveSupport::Logger) ?
|
|
13
|
+
::ActiveSupport::Logger.new(logdev) :
|
|
14
|
+
::Logger.new(logdev)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.logger(logdev)
|
|
18
|
+
defined?(::ActiveSupport::TaggedLogging) ?
|
|
19
|
+
::ActiveSupport::TaggedLogging.new(base_logger(logdev)) :
|
|
20
|
+
base_logger(logdev)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require "logger"
|
|
2
|
+
|
|
3
|
+
# Attempt to require Rails. We can not list it as a gem
|
|
4
|
+
# dependency because we want to support multiple frameworks.
|
|
5
|
+
require("rails") rescue LoadError
|
|
6
|
+
|
|
7
|
+
if defined?(Rails)
|
|
8
|
+
require 'timber/frameworks/rails'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module Timber
|
|
12
|
+
module Frameworks
|
|
13
|
+
def self.logger(logdev)
|
|
14
|
+
if defined?(Timber::Frameworks::Rails)
|
|
15
|
+
Rails.logger(logdev)
|
|
16
|
+
else
|
|
17
|
+
::Logger.new(logdev)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require "logger"
|
|
2
|
+
|
|
3
|
+
module Timber
|
|
4
|
+
# Allows us to prefix all logs with [Timber] without having to
|
|
5
|
+
# rely on external dependencies. This is slightly different
|
|
6
|
+
# in that we don't want to create an entirely new logger or modify
|
|
7
|
+
# the logger they pass us. We only want to prefix logs in the context
|
|
8
|
+
# of this library.
|
|
9
|
+
class InternalLogger < ::Logger
|
|
10
|
+
class Formatter < ::Logger::Formatter
|
|
11
|
+
TAG = "[Timber]"
|
|
12
|
+
|
|
13
|
+
# This method is invoked when a log event occurs
|
|
14
|
+
def call(_severity, _timestamp, _progname, msg)
|
|
15
|
+
"#{TAG} #{String === msg ? msg : msg.inspect}\n"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize(*args)
|
|
20
|
+
super
|
|
21
|
+
@formatter = Formatter.new
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# This is a convenience method for logging exceptions. Also
|
|
25
|
+
# allows us to build a notify hook for any exception that happen in
|
|
26
|
+
# the Timber library. This is extremely important for quality control.
|
|
27
|
+
def exception(exception)
|
|
28
|
+
if !exception.is_a?(Exception)
|
|
29
|
+
raise ArgumentError.new("#exception must take an Exception type")
|
|
30
|
+
end
|
|
31
|
+
# TODO: notify us that this exception happened
|
|
32
|
+
error("#{exception.inspect}: #{exception.backtrace.inspect}")
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Timber
|
|
2
|
+
class LogDevice
|
|
3
|
+
NEWLINE = "\n".freeze
|
|
4
|
+
SPLIT_LINES = true
|
|
5
|
+
|
|
6
|
+
def write(message)
|
|
7
|
+
return false if ignoring?
|
|
8
|
+
ignore do
|
|
9
|
+
messages(message).each do |message_part|
|
|
10
|
+
log_line = LogLine.new(message_part)
|
|
11
|
+
write_log_line(log_line)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
true
|
|
15
|
+
rescue Exception => e
|
|
16
|
+
Config.logger.exception(e)
|
|
17
|
+
raise e
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
def ignore(&block)
|
|
22
|
+
@ignoring = true
|
|
23
|
+
yield
|
|
24
|
+
ensure
|
|
25
|
+
@ignoring = false
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def ignoring?
|
|
29
|
+
@ignoring == true
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def messages(message)
|
|
33
|
+
message.chomp.split(NEWLINE)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def write_formatted(formatted_message)
|
|
37
|
+
raise NotImplementedError.new
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Timber
|
|
2
|
+
module LogDevices
|
|
3
|
+
class HerokuLogplex < IO
|
|
4
|
+
module HybridFormatter
|
|
5
|
+
private
|
|
6
|
+
def encoded_context(log_line)
|
|
7
|
+
log_line.context_snapshot.to_logfmt(
|
|
8
|
+
:except => [Contexts::Servers::HerokuSpecific]
|
|
9
|
+
)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|