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.
Files changed (133) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +34 -0
  3. data/.gitignore +14 -0
  4. data/Appraisals +37 -0
  5. data/Gemfile +22 -0
  6. data/LICENSE +38 -0
  7. data/README.md +22 -0
  8. data/Rakefile +4 -0
  9. data/TODO +4 -0
  10. data/benchmark/README.md +26 -0
  11. data/benchmark/rails_request.rb +68 -0
  12. data/benchmark/support/rails.rb +69 -0
  13. data/circle.yml +27 -0
  14. data/docs/installation/rails_on_heroku.md +31 -0
  15. data/docs/installation/rails_over_http.md +22 -0
  16. data/gemfiles/rails_3.0.X.gemfile +25 -0
  17. data/gemfiles/rails_3.1.X.gemfile +25 -0
  18. data/gemfiles/rails_3.2.X.gemfile +25 -0
  19. data/gemfiles/rails_4.0.X.gemfile +26 -0
  20. data/gemfiles/rails_4.1.X.gemfile +26 -0
  21. data/gemfiles/rails_4.2.X.gemfile +26 -0
  22. data/gemfiles/rails_5.0.X.gemfile +26 -0
  23. data/gemfiles/rails_edge.gemfile +27 -0
  24. data/lib/timber/api_settings.rb +17 -0
  25. data/lib/timber/bootstrap.rb +45 -0
  26. data/lib/timber/config.rb +25 -0
  27. data/lib/timber/context.rb +76 -0
  28. data/lib/timber/context_snapshot.rb +64 -0
  29. data/lib/timber/contexts/dynamic_values.rb +59 -0
  30. data/lib/timber/contexts/exception.rb +40 -0
  31. data/lib/timber/contexts/http_request.rb +22 -0
  32. data/lib/timber/contexts/http_requests/action_controller_specific.rb +48 -0
  33. data/lib/timber/contexts/http_requests/rack/params.rb +26 -0
  34. data/lib/timber/contexts/http_requests/rack.rb +105 -0
  35. data/lib/timber/contexts/http_response.rb +19 -0
  36. data/lib/timber/contexts/http_responses/action_controller.rb +76 -0
  37. data/lib/timber/contexts/logger.rb +33 -0
  38. data/lib/timber/contexts/organization.rb +33 -0
  39. data/lib/timber/contexts/organizations/action_controller.rb +34 -0
  40. data/lib/timber/contexts/server.rb +21 -0
  41. data/lib/timber/contexts/servers/heroku_specific.rb +48 -0
  42. data/lib/timber/contexts/sql_queries/active_record.rb +30 -0
  43. data/lib/timber/contexts/sql_queries/active_record_specific/binds.rb +37 -0
  44. data/lib/timber/contexts/sql_queries/active_record_specific.rb +59 -0
  45. data/lib/timber/contexts/sql_query.rb +18 -0
  46. data/lib/timber/contexts/template_render.rb +17 -0
  47. data/lib/timber/contexts/template_renders/action_view.rb +29 -0
  48. data/lib/timber/contexts/template_renders/action_view_specific.rb +51 -0
  49. data/lib/timber/contexts/user.rb +39 -0
  50. data/lib/timber/contexts/users/action_controller.rb +34 -0
  51. data/lib/timber/contexts.rb +23 -0
  52. data/lib/timber/current_context.rb +58 -0
  53. data/lib/timber/current_line_indexes.rb +35 -0
  54. data/lib/timber/frameworks/rails.rb +24 -0
  55. data/lib/timber/frameworks.rb +21 -0
  56. data/lib/timber/internal_logger.rb +35 -0
  57. data/lib/timber/log_device.rb +40 -0
  58. data/lib/timber/log_devices/heroku_logplex/hybrid_formatter.rb +14 -0
  59. data/lib/timber/log_devices/heroku_logplex.rb +14 -0
  60. data/lib/timber/log_devices/http/log_pile.rb +86 -0
  61. data/lib/timber/log_devices/http/log_truck/delivery.rb +116 -0
  62. data/lib/timber/log_devices/http/log_truck.rb +87 -0
  63. data/lib/timber/log_devices/http.rb +28 -0
  64. data/lib/timber/log_devices/io/formatter.rb +46 -0
  65. data/lib/timber/log_devices/io/hybrid_formatter.rb +41 -0
  66. data/lib/timber/log_devices/io/hybrid_hidden_formatter.rb +36 -0
  67. data/lib/timber/log_devices/io/json_formatter.rb +11 -0
  68. data/lib/timber/log_devices/io/logfmt_formatter.rb +11 -0
  69. data/lib/timber/log_devices/io.rb +41 -0
  70. data/lib/timber/log_devices.rb +4 -0
  71. data/lib/timber/log_line.rb +33 -0
  72. data/lib/timber/logger.rb +20 -0
  73. data/lib/timber/macros/compactor.rb +16 -0
  74. data/lib/timber/macros/date_formatter.rb +9 -0
  75. data/lib/timber/macros/deep_merger.rb +11 -0
  76. data/lib/timber/macros/logfmt_encoder.rb +77 -0
  77. data/lib/timber/macros.rb +4 -0
  78. data/lib/timber/patterns/delegated_singleton.rb +21 -0
  79. data/lib/timber/patterns/to_json.rb +22 -0
  80. data/lib/timber/patterns/to_logfmt.rb +9 -0
  81. data/lib/timber/patterns.rb +3 -0
  82. data/lib/timber/probe.rb +21 -0
  83. data/lib/timber/probes/action_controller_base.rb +31 -0
  84. data/lib/timber/probes/action_dispatch_debug_exceptions.rb +57 -0
  85. data/lib/timber/probes/active_support_log_subscriber/action_controller.rb +15 -0
  86. data/lib/timber/probes/active_support_log_subscriber/action_view.rb +26 -0
  87. data/lib/timber/probes/active_support_log_subscriber/active_record.rb +13 -0
  88. data/lib/timber/probes/active_support_log_subscriber.rb +62 -0
  89. data/lib/timber/probes/heroku.rb +30 -0
  90. data/lib/timber/probes/logger.rb +31 -0
  91. data/lib/timber/probes/rack.rb +36 -0
  92. data/lib/timber/probes/server.rb +18 -0
  93. data/lib/timber/probes.rb +24 -0
  94. data/lib/timber/version.rb +3 -0
  95. data/lib/timber.rb +27 -0
  96. data/spec/spec_helper.rb +27 -0
  97. data/spec/support/action_controller.rb +4 -0
  98. data/spec/support/action_view.rb +4 -0
  99. data/spec/support/active_record.rb +28 -0
  100. data/spec/support/coveralls.rb +2 -0
  101. data/spec/support/rails/templates/_partial.html +1 -0
  102. data/spec/support/rails/templates/template.html +1 -0
  103. data/spec/support/rails.rb +33 -0
  104. data/spec/support/simplecov.rb +9 -0
  105. data/spec/support/socket_hostname.rb +12 -0
  106. data/spec/support/timber.rb +23 -0
  107. data/spec/support/timecop.rb +3 -0
  108. data/spec/support/webmock.rb +2 -0
  109. data/spec/timber/bootstrap_spec.rb +31 -0
  110. data/spec/timber/context_snapshot_spec.rb +10 -0
  111. data/spec/timber/context_spec.rb +4 -0
  112. data/spec/timber/contexts/exception_spec.rb +34 -0
  113. data/spec/timber/contexts/organizations/action_controller_spec.rb +49 -0
  114. data/spec/timber/contexts/users/action_controller_spec.rb +65 -0
  115. data/spec/timber/current_line_indexes_spec.rb +40 -0
  116. data/spec/timber/frameworks/rails_spec.rb +9 -0
  117. data/spec/timber/log_devices/heroku_logplex_spec.rb +45 -0
  118. data/spec/timber/log_devices/http/log_truck/delivery_spec.rb +66 -0
  119. data/spec/timber/log_devices/http/log_truck_spec.rb +65 -0
  120. data/spec/timber/log_devices/io/hybrid_hidden_formatter_spec.rb +28 -0
  121. data/spec/timber/log_line_spec.rb +49 -0
  122. data/spec/timber/macros/compactor_spec.rb +19 -0
  123. data/spec/timber/macros/logfmt_encoder_spec.rb +89 -0
  124. data/spec/timber/patterns/to_json_spec.rb +40 -0
  125. data/spec/timber/probes/action_controller_base_spec.rb +43 -0
  126. data/spec/timber/probes/action_controller_log_subscriber/action_controller_spec.rb +35 -0
  127. data/spec/timber/probes/action_controller_log_subscriber/action_view_spec.rb +44 -0
  128. data/spec/timber/probes/action_controller_log_subscriber/active_record_spec.rb +26 -0
  129. data/spec/timber/probes/action_dispatch_debug_exceptions_spec.rb +45 -0
  130. data/spec/timber/probes/logger_spec.rb +20 -0
  131. data/spec/timber/probes/rack_spec.rb +26 -0
  132. data/timberio.gemspec +20 -0
  133. 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