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,17 @@
1
+ module Timber
2
+ # These settings are established by the Timber API. Changing these will result
3
+ # in unsuccessful log delivery.
4
+ module APISettings
5
+ DATE_FORMAT = :iso8601.freeze # must be a method name
6
+ DATE_FORMAT_PRECISION = 6 # millisecond digits
7
+ MESSAGE_BYTE_SIZE_MAX = 1_000_000.freeze # 1mb
8
+
9
+ # List of types that the Timber API accepts
10
+ BOOLEAN_TYPE = "integer".freeze
11
+ DATE_TYPE = "date".freeze
12
+ FLOAT_TYPE = "float".freeze
13
+ INTEGER_TYPE = "integer".freeze
14
+ NIL_TYPE = "nil".freeze
15
+ STRING_TYPE = "string".freeze
16
+ end
17
+ end
@@ -0,0 +1,45 @@
1
+ module Timber
2
+ # Intermediary class between frameworks and Timber. Defines
3
+ # requirements and normalized the setup process.
4
+ class Bootstrap
5
+ def self.bootstrap!(*args)
6
+ new(*args).bootstrap!
7
+ rescue Exception => e
8
+ # Failsafe to ensure Timber never takes down the app
9
+ Config.logger.exception(e)
10
+ end
11
+
12
+ attr_reader :middleware, :insert_before
13
+
14
+ def initialize(middleware, insert_before)
15
+ @middleware = middleware
16
+ @insert_before = insert_before
17
+ end
18
+
19
+ def bootstrap!
20
+ return false unless enabled?
21
+ Probes.insert!(middleware, insert_before)
22
+ log_started
23
+ true
24
+ end
25
+
26
+ private
27
+ def enabled?
28
+ if !Config.enabled?
29
+ Config.logger.warn("Skipping bootstrap, Timber::Config.enabled is not true")
30
+ false
31
+ else
32
+ true
33
+ end
34
+ end
35
+
36
+ def log_started
37
+ Config.logger.info(" _,-,")
38
+ Config.logger.info("T_ | Timber enabled")
39
+ Config.logger.info("||`-'")
40
+ Config.logger.info("||")
41
+ Config.logger.info("||")
42
+ Config.logger.info("~~")
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,25 @@
1
+ module Timber
2
+ class Config
3
+ include Patterns::DelegatedSingleton
4
+
5
+ attr_writer :application_key, :enabled, :logger
6
+
7
+ def application_key
8
+ @application_key ||= ENV['TIMBER_KEY']
9
+ end
10
+
11
+ def enabled
12
+ return @enabled if defined?(@enabled)
13
+ @enabled = true
14
+ end
15
+
16
+ def enabled?
17
+ enabled == true
18
+ end
19
+
20
+ # Internal logger for the Timber library, only for debugging purposes.
21
+ def logger
22
+ @logger ||= InternalLogger.new(nil)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,76 @@
1
+ require "securerandom"
2
+
3
+ module Timber
4
+ class Context
5
+ include Patterns::ToJSON
6
+ include Patterns::ToLogfmt
7
+
8
+ PATH_DELIMITER = ".".freeze
9
+ SECURE_RANDOM_LENGTH = 16.freeze
10
+
11
+ class << self
12
+ def json_shell(&_block)
13
+ {_root_key => yield}
14
+ end
15
+
16
+ def _path
17
+ @path ||= Macros::LogfmtEncoder.encode(json_shell { 1 }).split("=").first
18
+ end
19
+
20
+ def _root_key
21
+ @_root_key ||= const_get(:ROOT_KEY)
22
+ end
23
+
24
+ def _version
25
+ @_version ||= const_get(:VERSION)
26
+ end
27
+ end
28
+
29
+ def _dt
30
+ @_dt ||= Time.now.utc
31
+ end
32
+
33
+ def _path
34
+ self.class._path
35
+ end
36
+
37
+ def _version
38
+ self.class._version
39
+ end
40
+
41
+ def _root_key
42
+ self.class._root_key
43
+ end
44
+
45
+ def as_json(*args)
46
+ @as_json ||= json_shell { super }
47
+ end
48
+
49
+ def json_shell(&block)
50
+ self.class.json_shell(&block)
51
+ end
52
+
53
+ def inspect(*_args)
54
+ "#<#{self.class.name}:#{object_id} ...>"
55
+ end
56
+
57
+ # Some contexts hold mutable object that change as the context block
58
+ # executes. This method checks the state of that object to ensure
59
+ # that the context is valid and ready to be copied for each log line.
60
+ def valid?
61
+ true
62
+ end
63
+
64
+ private
65
+ def json_payload
66
+ @json_payload ||= {
67
+ :_dt => Macros::DateFormatter.format(_dt),
68
+ :_version => _version
69
+ }
70
+ end
71
+
72
+ def generate_secure_random
73
+ SecureRandom.urlsafe_base64(SECURE_RANDOM_LENGTH)
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,64 @@
1
+ module Timber
2
+ class ContextSnapshot
3
+ include Timber::Patterns::ToJSON
4
+
5
+ CONTEXT_VERSION = 1.freeze
6
+
7
+ attr_reader :indexes, :stack
8
+
9
+ def initialize
10
+ # Cloning arrays and hashes is extremely fast. This
11
+ # should not be a concern for hindering performance as we are
12
+ # only cloning the structures, not the content.
13
+ @stack = CurrentContext.valid_stack.clone.freeze
14
+ @indexes = CurrentLineIndexes.indexes.clone.freeze
15
+ end
16
+
17
+ def to_logfmt(options = {})
18
+ @to_logfmt ||= {}
19
+ @to_logfmt[options] ||= begin
20
+ items = stack.collect do |context|
21
+ next if options[:except].is_a?(Array) && options[:except].include?(context.class)
22
+ # Delegate to #to_logfmt on the context object for caching.
23
+ # Add the index on the fly, as a string, since it's more performant.
24
+ Macros::LogfmtEncoder.join(context.to_logfmt, "#{context._path}._index=#{index(context)}")
25
+ end.compact
26
+ items << Macros::LogfmtEncoder.encode(:_version => CONTEXT_VERSION, :_hierarchy => hierarchy)
27
+ Macros::LogfmtEncoder.join(*items)
28
+ end
29
+ end
30
+
31
+ def size
32
+ stack.size
33
+ end
34
+
35
+ private
36
+ def context_hash
37
+ @context_hash ||= begin
38
+ hash = stack.inject({}) do |acc, context|
39
+ acc = Macros::DeepMerger.merge(acc, context.as_json)
40
+ Macros::DeepMerger.merge(acc, index_hash(context))
41
+ end
42
+ hash[:_version] = CONTEXT_VERSION
43
+ hash[:_hierarchy] = hierarchy
44
+ hash
45
+ end
46
+ end
47
+
48
+ def hierarchy
49
+ @hierarchy ||= stack.collect(&:_path).uniq
50
+ end
51
+
52
+ def index(context)
53
+ indexes[context] || raise("couldn't find index for #{context}")
54
+ end
55
+
56
+ def index_hash(context)
57
+ context.json_shell { {:_index => index(context)}}
58
+ end
59
+
60
+ def json_payload
61
+ @json_payload ||= {:context => context_hash}
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,59 @@
1
+ require "bigdecimal"
2
+ require "date"
3
+
4
+ module Timber
5
+ module Contexts
6
+ class DynamicValues
7
+ class UnrecognizedObjectTypeError < StandardError; end
8
+
9
+ include Patterns::ToJSON
10
+
11
+ BOOLEAN_TYPES = [FalseClass, TrueClass].freeze
12
+ DATE_TYPES = [Date, Time].freeze
13
+ FLOAT_TYPES = [BigDecimal, Float].freeze
14
+ INTEGER_TYPES = [Fixnum].freeze
15
+ NIL_TYPES = [NilClass].freeze
16
+ STRING_TYPES = [String].freeze
17
+
18
+ def initialize(values_array = nil)
19
+ @values_array = values_array
20
+ end
21
+
22
+ private
23
+ def json_payload
24
+ @json_payload ||= values_array.collect do |value|
25
+ to_item(value)
26
+ end
27
+ end
28
+
29
+ def to_item(value)
30
+ {
31
+ :name => value[:name],
32
+ :type => type(value[:value]),
33
+ :value => value[:value]
34
+ }
35
+ end
36
+
37
+ def type(value)
38
+ # Using is_a? because it checks the entire hierarchy, unlike a case statement.
39
+ if BOOLEAN_TYPES.any? { |type| value.is_a?(type) }
40
+ APISettings::BOOLEAN_TYPE
41
+ elsif DATE_TYPES.any? { |type| value.is_a?(type) }
42
+ APISettings::DATE_TYPE
43
+ elsif FLOAT_TYPES.any? { |type| value.is_a?(type) }
44
+ APISettings::FLOAT_TYPE
45
+ elsif INTEGER_TYPES.any? { |type| value.is_a?(type) }
46
+ APISettings::INTEGER_TYPE
47
+ elsif NIL_TYPES.any? { |type| value.is_a?(type) }
48
+ APISettings::NIL_TYPE
49
+ else
50
+ APISettings::STRING_TYPE
51
+ end
52
+ end
53
+
54
+ def values_array
55
+ @values_array
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,40 @@
1
+ module Timber
2
+ module Contexts
3
+ class Exception < Context
4
+ ROOT_KEY = :exception.freeze
5
+ VERSION = 1.freeze
6
+
7
+ attr_reader :exception
8
+
9
+ def initialize(exception)
10
+ # Initialize should be as fast as possible since it is executed inline.
11
+ # Hence the lazy methods below.
12
+ @exception = exception
13
+ super()
14
+ end
15
+
16
+ def backtrace
17
+ # only the first 5 lines to save on space
18
+ @backtrace ||= exception.backtrace[0..4]
19
+ end
20
+
21
+ def name
22
+ @name ||= exception.class.name
23
+ end
24
+
25
+ def message
26
+ @message ||= exception.message
27
+ end
28
+
29
+ private
30
+ def json_payload
31
+ @json_payload ||= Macros::DeepMerger.merge({
32
+ # order is relevant for logfmt styling
33
+ :name => name,
34
+ :message => message,
35
+ :backtrace => backtrace
36
+ }, super).freeze
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,22 @@
1
+ module Timber
2
+ module Contexts
3
+ class HTTPRequest < Context
4
+ ROOT_KEY = :http_request.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
+ :method => method,
12
+ :scheme => scheme,
13
+ :host => host,
14
+ :port => port,
15
+ :path => path,
16
+ :query_params => query_params.as_json,
17
+ :headers => headers.as_json
18
+ }, super).freeze
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,48 @@
1
+ module Timber
2
+ module Contexts
3
+ module HTTPRequests
4
+ # Extend Context since we are a sub context and not an actual HTTPRequest
5
+ class ActionControllerSpecific < Context
6
+ ROOT_KEY = :action_controller.freeze
7
+ VERSION = 1.freeze
8
+
9
+ class << self
10
+ def json_shell(&_block)
11
+ Rack.json_shell { super }
12
+ end
13
+ end
14
+
15
+ attr_reader :controller_obj
16
+
17
+ def initialize(controller_obj)
18
+ # Initialize should be as fast as possible since it is executed inline.
19
+ # Hence the lazy methods below.
20
+ @controller_obj = controller_obj
21
+ super()
22
+ end
23
+
24
+ def action
25
+ @action ||= controller_obj.action_name
26
+ end
27
+
28
+ def controller
29
+ @controller ||= controller_obj.class.name
30
+ end
31
+
32
+ def format
33
+ @format ||= controller_obj.request.format.try(:ref)
34
+ end
35
+
36
+ private
37
+ def json_payload
38
+ @json_payload ||= Macros::DeepMerger.merge({
39
+ # order is relevant for logfmt styling
40
+ :controller => controller,
41
+ :action => action,
42
+ :format => format
43
+ }, super).freeze
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,26 @@
1
+ module Timber
2
+ module Contexts
3
+ module HTTPRequests
4
+ class Rack < HTTPRequest
5
+ class Params < DynamicValues
6
+ attr_reader :params
7
+
8
+ def initialize(params)
9
+ # Initialize should be as fast as possible since it is executed inline.
10
+ # Hence the lazy methods below.
11
+ @params = params
12
+ super()
13
+ end
14
+
15
+ private
16
+ # Override values_array so that this is done in the background thread
17
+ def values_array
18
+ @values_array ||= params.collect do |key, value|
19
+ {:name => key, :value => value}
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,105 @@
1
+ require "timber/contexts/http_requests/rack/params"
2
+
3
+ module Timber
4
+ module Contexts
5
+ module HTTPRequests
6
+ class Rack < HTTPRequest
7
+ class Headers
8
+ include Patterns::ToJSON
9
+
10
+ attr_reader :env, :request
11
+
12
+ def initialize(env, request)
13
+ @env = env
14
+ @request = request
15
+ end
16
+
17
+ def content_type
18
+ @content_type ||= request.content_type
19
+ end
20
+
21
+ def referrer
22
+ @referrer ||= request.referrer
23
+ end
24
+
25
+ def remote_addr
26
+ @remote_addr ||= request.ip
27
+ end
28
+
29
+ def request_id
30
+ return @request_id if defined?(@request_id)
31
+ found = env.find do |k,v|
32
+ # Needs to support:
33
+ # action_dispatch.request_id
34
+ # HTTP_X_REQUEST_ID
35
+ # Request-ID
36
+ # Request-Id
37
+ # X-Request-ID
38
+ # X-Request-Id
39
+ # etc
40
+ (k.downcase.include?("request_id") || k.downcase.include?("request-id")) && !v.nil?
41
+ end
42
+ @request_id = found && found.last
43
+ end
44
+
45
+ def user_agent
46
+ @user_agent ||= request.user_agent
47
+ end
48
+
49
+ private
50
+ def json_payload
51
+ @json_payload ||= {
52
+ :content_type => content_type,
53
+ :referrer => referrer,
54
+ :remote_addr => remote_addr,
55
+ :request_id => request_id,
56
+ :user_agent => user_agent
57
+ }
58
+ end
59
+ end
60
+
61
+ attr_reader :env
62
+
63
+ def initialize(env)
64
+ # Initialize should be as fast as possible since it is executed inline.
65
+ # Hence the lazy methods below.
66
+ @env = env
67
+ super()
68
+ end
69
+
70
+ def headers
71
+ @headers ||= Headers.new(env, request)
72
+ end
73
+
74
+ def host
75
+ @host ||= request.host
76
+ end
77
+
78
+ def method
79
+ @method ||= request.request_method.upcase
80
+ end
81
+
82
+ def path
83
+ @path ||= request.path
84
+ end
85
+
86
+ def port
87
+ @port ||= request.port
88
+ end
89
+
90
+ def query_params
91
+ @query_params ||= request.params && Params.new(request.params)
92
+ end
93
+
94
+ def scheme
95
+ @scheme ||= request.scheme
96
+ end
97
+
98
+ private
99
+ def request
100
+ @request ||= ::Rack::Request.new(env)
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,19 @@
1
+ module Timber
2
+ module Contexts
3
+ # Generica HTTP response shared across all platforms.
4
+ class HTTPResponse < Context
5
+ ROOT_KEY = :http_response.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
+ :status => status,
13
+ :headers => headers.as_json,
14
+ :time_ms => time_ms
15
+ }, super).freeze
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,76 @@
1
+ module Timber
2
+ module Contexts
3
+ module HTTPResponses
4
+ class ActionController < HTTPResponse
5
+ class Headers
6
+ include Patterns::ToJSON
7
+
8
+ attr_reader :response
9
+
10
+ def initialize(response)
11
+ @response = response
12
+ end
13
+
14
+ def content_length
15
+ @content_length ||= response.content_length
16
+ end
17
+
18
+ def cache_control
19
+ @cache_control ||= response.headers['Cache-Control']
20
+ end
21
+
22
+ def content_disposition
23
+ @content_disposition ||= response.headers['Cache-Disposition']
24
+ end
25
+
26
+ def content_type
27
+ @content_type ||= response.headers['Content-Type']
28
+ end
29
+
30
+ def location
31
+ @location ||= response.headers['Location']
32
+ end
33
+
34
+ private
35
+ def json_payload
36
+ @json_payload ||= {
37
+ :content_length => content_length,
38
+ :cache_control => cache_control,
39
+ :content_disposition => content_disposition,
40
+ :content_type => content_type,
41
+ :location => location
42
+ }
43
+ end
44
+ end
45
+
46
+ attr_reader :controller
47
+ attr_accessor :event
48
+
49
+ def initialize(controller)
50
+ @controller = controller
51
+ end
52
+
53
+ def headers
54
+ @headers ||= Headers.new(response)
55
+ end
56
+
57
+ def status
58
+ @status ||= response.status
59
+ end
60
+
61
+ def time_ms
62
+ @time_ms ||= event.duration
63
+ end
64
+
65
+ def valid?
66
+ !response.nil? && !event.nil?
67
+ end
68
+
69
+ private
70
+ def response
71
+ controller.response
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,33 @@
1
+ module Timber
2
+ module Contexts
3
+ class Logger < Context
4
+ ROOT_KEY = :logger.freeze
5
+ VERSION = 1.freeze
6
+ LEVEL_MAP = {
7
+ 0 => "debug",
8
+ 1 => "info",
9
+ 2 => "warn",
10
+ 3 => "error",
11
+ 4 => "fatal"
12
+ }
13
+ UNKNOWN_LEVEL = "unknown"
14
+
15
+ attr_reader :level, :progname
16
+
17
+ def initialize(level, progname)
18
+ @level = LEVEL_MAP[level] || UNKNOWN_LEVEL
19
+ @progname = progname
20
+ super()
21
+ end
22
+
23
+ private
24
+ def json_payload
25
+ @json_payload ||= Macros::DeepMerger.merge({
26
+ # order is relevant for logfmt styling
27
+ :level => level,
28
+ :progname => progname
29
+ }, super).freeze
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ module Timber
2
+ module Contexts
3
+ class Organization < Context
4
+ ROOT_KEY = :organization.freeze
5
+ VERSION = 1.freeze
6
+
7
+ attr_reader :organization
8
+
9
+ def id
10
+ return @id if defined?(@id)
11
+ @id = organization.respond_to?(:id) ? organization.id : nil
12
+ end
13
+
14
+ def name
15
+ return @name if defined?(@name)
16
+ @name = organization.respond_to?(:name) ? organization.name : nil
17
+ end
18
+
19
+ def valid?
20
+ !organization.nil?
21
+ end
22
+
23
+ private
24
+ def json_payload
25
+ @json_payload ||= Macros::DeepMerger.merge({
26
+ # order is relevant for logfmt styling
27
+ :id => id,
28
+ :name => name
29
+ }, super).freeze
30
+ end
31
+ end
32
+ end
33
+ end