timberio 1.0.0.beta1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (157) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -2
  3. data/.rspec +2 -0
  4. data/.yardopts +6 -0
  5. data/Appraisals +4 -0
  6. data/Gemfile +9 -1
  7. data/LICENSE.md +15 -0
  8. data/README.md +170 -8
  9. data/circle.yml +11 -8
  10. data/lib/timber/config.rb +11 -18
  11. data/lib/timber/context.rb +9 -68
  12. data/lib/timber/contexts/custom.rb +27 -0
  13. data/lib/timber/contexts/http.rb +28 -0
  14. data/lib/timber/contexts/organization.rb +24 -22
  15. data/lib/timber/contexts/user.rb +25 -28
  16. data/lib/timber/contexts.rb +7 -20
  17. data/lib/timber/current_context.rb +26 -41
  18. data/lib/timber/event.rb +13 -0
  19. data/lib/timber/events/controller_call.rb +40 -0
  20. data/lib/timber/events/custom.rb +42 -0
  21. data/lib/timber/events/exception.rb +35 -0
  22. data/lib/timber/events/http_request.rb +50 -0
  23. data/lib/timber/events/http_response.rb +36 -0
  24. data/lib/timber/events/sql_query.rb +26 -0
  25. data/lib/timber/events/template_render.rb +26 -0
  26. data/lib/timber/events.rb +37 -0
  27. data/lib/timber/frameworks/rails.rb +3 -14
  28. data/lib/timber/frameworks.rb +8 -10
  29. data/lib/timber/log_devices/http.rb +72 -13
  30. data/lib/timber/log_devices.rb +8 -4
  31. data/lib/timber/log_entry.rb +59 -0
  32. data/lib/timber/logger.rb +136 -13
  33. data/lib/timber/probe.rb +6 -4
  34. data/lib/timber/probes/action_controller_log_subscriber/log_subscriber.rb +64 -0
  35. data/lib/timber/probes/action_controller_log_subscriber.rb +20 -0
  36. data/lib/timber/probes/action_dispatch_debug_exceptions.rb +59 -35
  37. data/lib/timber/probes/action_view_log_subscriber/log_subscriber.rb +62 -0
  38. data/lib/timber/probes/action_view_log_subscriber.rb +20 -0
  39. data/lib/timber/probes/active_record_log_subscriber/log_subscriber.rb +72 -0
  40. data/lib/timber/probes/active_record_log_subscriber.rb +20 -0
  41. data/lib/timber/probes/rack_http_context.rb +51 -0
  42. data/lib/timber/probes/rails_rack_logger.rb +76 -0
  43. data/lib/timber/probes.rb +13 -16
  44. data/lib/timber/util/active_support_log_subscriber.rb +33 -0
  45. data/lib/timber/util/hash.rb +14 -0
  46. data/lib/timber/util.rb +8 -0
  47. data/lib/timber/version.rb +2 -2
  48. data/lib/timber.rb +6 -11
  49. data/spec/spec_helper.rb +7 -4
  50. data/spec/support/rails.rb +9 -5
  51. data/spec/support/timber.rb +1 -20
  52. data/spec/timber/events_spec.rb +55 -0
  53. data/spec/timber/log_devices/http_spec.rb +62 -0
  54. data/spec/timber/logger_spec.rb +68 -0
  55. data/spec/timber/probes/action_controller_log_subscriber_spec.rb +70 -0
  56. data/spec/timber/probes/action_dispatch_debug_exceptions_spec.rb +24 -18
  57. data/spec/timber/probes/action_view_log_subscriber_spec.rb +61 -0
  58. data/spec/timber/probes/active_record_log_subscriber_spec.rb +49 -0
  59. data/spec/timber/probes/rack_http_context_spec.rb +54 -0
  60. data/spec/timber/probes/rails_rack_logger_spec.rb +46 -0
  61. data/timberio.gemspec +2 -0
  62. metadata +62 -123
  63. data/.codeclimate.yml +0 -34
  64. data/LICENSE +0 -38
  65. data/Rakefile +0 -4
  66. data/TODO +0 -4
  67. data/benchmark/README.md +0 -26
  68. data/benchmark/rails_request.rb +0 -68
  69. data/benchmark/support/rails.rb +0 -69
  70. data/docs/installation/rails_on_heroku.md +0 -31
  71. data/docs/installation/rails_over_http.md +0 -22
  72. data/gemfiles/rails_3.0.X.gemfile +0 -25
  73. data/gemfiles/rails_3.1.X.gemfile +0 -25
  74. data/gemfiles/rails_3.2.X.gemfile +0 -25
  75. data/gemfiles/rails_4.0.X.gemfile +0 -26
  76. data/gemfiles/rails_4.1.X.gemfile +0 -26
  77. data/gemfiles/rails_4.2.X.gemfile +0 -26
  78. data/gemfiles/rails_5.0.X.gemfile +0 -26
  79. data/gemfiles/rails_edge.gemfile +0 -27
  80. data/lib/timber/api_settings.rb +0 -17
  81. data/lib/timber/bootstrap.rb +0 -45
  82. data/lib/timber/context_snapshot.rb +0 -64
  83. data/lib/timber/contexts/dynamic_values.rb +0 -59
  84. data/lib/timber/contexts/exception.rb +0 -40
  85. data/lib/timber/contexts/http_request.rb +0 -22
  86. data/lib/timber/contexts/http_requests/action_controller_specific.rb +0 -48
  87. data/lib/timber/contexts/http_requests/rack/params.rb +0 -26
  88. data/lib/timber/contexts/http_requests/rack.rb +0 -105
  89. data/lib/timber/contexts/http_response.rb +0 -19
  90. data/lib/timber/contexts/http_responses/action_controller.rb +0 -76
  91. data/lib/timber/contexts/logger.rb +0 -33
  92. data/lib/timber/contexts/organizations/action_controller.rb +0 -34
  93. data/lib/timber/contexts/server.rb +0 -21
  94. data/lib/timber/contexts/servers/heroku_specific.rb +0 -48
  95. data/lib/timber/contexts/sql_queries/active_record.rb +0 -30
  96. data/lib/timber/contexts/sql_queries/active_record_specific/binds.rb +0 -37
  97. data/lib/timber/contexts/sql_queries/active_record_specific.rb +0 -59
  98. data/lib/timber/contexts/sql_query.rb +0 -18
  99. data/lib/timber/contexts/template_render.rb +0 -17
  100. data/lib/timber/contexts/template_renders/action_view.rb +0 -29
  101. data/lib/timber/contexts/template_renders/action_view_specific.rb +0 -51
  102. data/lib/timber/contexts/users/action_controller.rb +0 -34
  103. data/lib/timber/current_line_indexes.rb +0 -35
  104. data/lib/timber/internal_logger.rb +0 -35
  105. data/lib/timber/log_device.rb +0 -40
  106. data/lib/timber/log_devices/heroku_logplex/hybrid_formatter.rb +0 -14
  107. data/lib/timber/log_devices/heroku_logplex.rb +0 -14
  108. data/lib/timber/log_devices/http/log_pile.rb +0 -86
  109. data/lib/timber/log_devices/http/log_truck/delivery.rb +0 -116
  110. data/lib/timber/log_devices/http/log_truck.rb +0 -87
  111. data/lib/timber/log_devices/io/formatter.rb +0 -46
  112. data/lib/timber/log_devices/io/hybrid_formatter.rb +0 -41
  113. data/lib/timber/log_devices/io/hybrid_hidden_formatter.rb +0 -36
  114. data/lib/timber/log_devices/io/json_formatter.rb +0 -11
  115. data/lib/timber/log_devices/io/logfmt_formatter.rb +0 -11
  116. data/lib/timber/log_devices/io.rb +0 -41
  117. data/lib/timber/log_line.rb +0 -33
  118. data/lib/timber/macros/compactor.rb +0 -16
  119. data/lib/timber/macros/date_formatter.rb +0 -9
  120. data/lib/timber/macros/deep_merger.rb +0 -11
  121. data/lib/timber/macros/logfmt_encoder.rb +0 -77
  122. data/lib/timber/macros.rb +0 -4
  123. data/lib/timber/patterns/delegated_singleton.rb +0 -21
  124. data/lib/timber/patterns/to_json.rb +0 -22
  125. data/lib/timber/patterns/to_logfmt.rb +0 -9
  126. data/lib/timber/patterns.rb +0 -3
  127. data/lib/timber/probes/action_controller_base.rb +0 -31
  128. data/lib/timber/probes/active_support_log_subscriber/action_controller.rb +0 -15
  129. data/lib/timber/probes/active_support_log_subscriber/action_view.rb +0 -26
  130. data/lib/timber/probes/active_support_log_subscriber/active_record.rb +0 -13
  131. data/lib/timber/probes/active_support_log_subscriber.rb +0 -62
  132. data/lib/timber/probes/heroku.rb +0 -30
  133. data/lib/timber/probes/logger.rb +0 -31
  134. data/lib/timber/probes/rack.rb +0 -36
  135. data/lib/timber/probes/server.rb +0 -18
  136. data/spec/timber/bootstrap_spec.rb +0 -31
  137. data/spec/timber/context_snapshot_spec.rb +0 -10
  138. data/spec/timber/context_spec.rb +0 -4
  139. data/spec/timber/contexts/exception_spec.rb +0 -34
  140. data/spec/timber/contexts/organizations/action_controller_spec.rb +0 -49
  141. data/spec/timber/contexts/users/action_controller_spec.rb +0 -65
  142. data/spec/timber/current_line_indexes_spec.rb +0 -40
  143. data/spec/timber/frameworks/rails_spec.rb +0 -9
  144. data/spec/timber/log_devices/heroku_logplex_spec.rb +0 -45
  145. data/spec/timber/log_devices/http/log_truck/delivery_spec.rb +0 -66
  146. data/spec/timber/log_devices/http/log_truck_spec.rb +0 -65
  147. data/spec/timber/log_devices/io/hybrid_hidden_formatter_spec.rb +0 -28
  148. data/spec/timber/log_line_spec.rb +0 -49
  149. data/spec/timber/macros/compactor_spec.rb +0 -19
  150. data/spec/timber/macros/logfmt_encoder_spec.rb +0 -89
  151. data/spec/timber/patterns/to_json_spec.rb +0 -40
  152. data/spec/timber/probes/action_controller_base_spec.rb +0 -43
  153. data/spec/timber/probes/action_controller_log_subscriber/action_controller_spec.rb +0 -35
  154. data/spec/timber/probes/action_controller_log_subscriber/action_view_spec.rb +0 -44
  155. data/spec/timber/probes/action_controller_log_subscriber/active_record_spec.rb +0 -26
  156. data/spec/timber/probes/logger_spec.rb +0 -20
  157. data/spec/timber/probes/rack_spec.rb +0 -26
@@ -1,40 +0,0 @@
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
@@ -1,14 +0,0 @@
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
@@ -1,14 +0,0 @@
1
- require File.join(File.dirname(__FILE__), "heroku_logplex", "hybrid_formatter")
2
-
3
- module Timber
4
- module LogDevices
5
- class HerokuLogplex < IO
6
- def initialize(_options = {})
7
- super(STDOUT)
8
- if formatter.is_a?(IO::HybridFormatter)
9
- formatter.extend HybridFormatter
10
- end
11
- end
12
- end
13
- end
14
- end
@@ -1,86 +0,0 @@
1
- require "thread"
2
-
3
- module Timber
4
- module LogDevices
5
- class HTTP < LogDevice
6
- # This is a thread safe queue for transporting logs to the Timber API.
7
- # TODO: Have these log lines persist to a file where
8
- # a daemon can pick them up.
9
- class LogPile
10
- class << self
11
- def each(&block)
12
- instances.values.each(&block)
13
- end
14
-
15
- def get(application_key)
16
- instances[application_key] ||= new(application_key)
17
- end
18
-
19
- private
20
- def instances
21
- @instances ||= {}
22
- end
23
- end
24
-
25
- attr_reader :application_key
26
-
27
- def initialize(application_key)
28
- @application_key = application_key
29
- @mutex = Mutex.new
30
- end
31
-
32
- def drop(log_line)
33
- mutex.synchronize do
34
- log_lines << log_line
35
- end
36
- rescue LogLine::InvalidMessageError => e
37
- # Ignore the error and log it.
38
- Config.logger.error(e)
39
- rescue Exception => e
40
- # Fail safe to ensure the Timber gem never fails the app.
41
- Config.logger.exception(e)
42
- end
43
-
44
- def empty(&_block)
45
- if log_lines.any?
46
- copy = log_lines_copy
47
- yield(copy) if block_given?
48
- remove(copy)
49
- self
50
- end
51
- end
52
-
53
- def size
54
- log_lines.size
55
- end
56
-
57
- private
58
- def mutex
59
- @mutex
60
- end
61
-
62
- def remove(log_lines_copy)
63
- mutex.synchronize do
64
- # Delete items by object_id since we are working
65
- # with the same object. Do not use equality here.
66
- log_lines_copy.each do |l1|
67
- log_lines.delete_if { |l2| l2.object_id == l1.object_id }
68
- end
69
- end
70
- end
71
-
72
- def log_lines_copy
73
- mutex.synchronize do
74
- # Copy the array structure so we aren't dealing with
75
- # a changing array, but do not copy the items.
76
- log_lines.clone
77
- end
78
- end
79
-
80
- def log_lines
81
- @log_lines ||= []
82
- end
83
- end
84
- end
85
- end
86
- end
@@ -1,116 +0,0 @@
1
- require "base64"
2
- require "net/http"
3
- require "net/https"
4
- require "uri"
5
-
6
- module Timber
7
- module LogDevices
8
- class HTTP < LogDevice
9
- class LogTruck
10
- class Delivery
11
- class DeliveryError < StandardError; end
12
-
13
- API_URI = URI.parse("https://timber-odin.herokuapp.com/agent_log_frames")
14
- CONTENT_TYPE = 'application/json'.freeze
15
- READ_TIMEOUT_SECONDS = 35.freeze
16
- RETRY_BACKOFF_SECONDS = 1.freeze
17
- RETRY_COUNT = 4.freeze
18
- USER_AGENT = "Timber Ruby Gem/#{Timber::VERSION}".freeze
19
-
20
- HTTPS = Net::HTTP.new(API_URI.host, API_URI.port).tap do |https|
21
- https.use_ssl = true
22
- https.read_timeout = READ_TIMEOUT_SECONDS
23
- end
24
-
25
- attr_reader :application_key, :log_lines
26
-
27
- def initialize(application_key, log_lines)
28
- @application_key = application_key
29
- @log_lines = log_lines
30
- end
31
-
32
- def deliver!(retry_count = 0)
33
- Config.logger.debug("Attempting delivery of: #{body_json}")
34
- request!
35
- # Catch them all because of all the unknown exceptions that can happen during
36
- # a http request.
37
- rescue Exception => e
38
- # Ensure that we are always returning a consistent error.
39
- # This ensures we handle it appropriately and don't kill the
40
- # thread above.
41
- Config.logger.warn("Failed delivery: #{e.message}")
42
-
43
- retry_count += 1
44
- if retry_count <= RETRY_COUNT
45
- backoff_seconds = RETRY_BACKOFF_SECONDS ** retry_count
46
- Config.logger.warn("Backing off #{backoff_seconds} seconds")
47
- sleep backoff_seconds
48
- Config.logger.warn("Retrying, attempt #{retry_count}")
49
- deliver!(retry_count)
50
- else
51
- Config.logger.warn("Retry attempts exceeded, dropping logs")
52
- raise DeliveryError.new(e.message)
53
- end
54
- end
55
-
56
- private
57
- def https
58
- @https ||= HTTPS
59
- end
60
-
61
- def request!
62
- https.request(new_request).tap do |res|
63
- code = res.code.to_i
64
- if code < 200 || code >= 300
65
- raise DeliveryError.new("Bad response from Timber API - #{res.code}: #{res.body}")
66
- end
67
- Config.logger.debug("Success! #{code}: #{res.body}")
68
- end
69
- end
70
-
71
- def new_request
72
- Net::HTTP::Post.new(API_URI.request_uri).tap do |req|
73
- req['Authorization'] = authorization_payload
74
- req['Body-Checksum'] = body_checksum # the API checks for duplicate requests
75
- req['Content-Type'] = CONTENT_TYPE
76
- req['Log-Line-Count'] = log_lines.size # additional check to ensure the correct # of log lines were sent
77
- req['User-Agent'] = USER_AGENT
78
- req.body = body_json
79
- end
80
- end
81
-
82
- # Used by the API to check for duplicate requests.
83
- def body_checksum
84
- @body_checksum ||= Digest::MD5.hexdigest(body_json)
85
- end
86
-
87
- def body_json
88
- return @body_json if defined?(@body_json)
89
- # Build the json as a string since it is more efficient.
90
- # We are also working with string upstream for the same reason.
91
- @body_json ||= <<-JSON
92
- {"agent_log_frame": {"log_lines": #{log_lines_json}}}
93
- JSON
94
- @body_json.strip!
95
- @body_json
96
- end
97
-
98
- def log_lines_json
99
- return @log_lines_json if defined?(@log_lines_json)
100
- @log_lines_json = "["
101
- last_index = log_lines.size - 1
102
- log_lines.each_with_index do |log_line, index|
103
- @log_lines_json += log_line.to_json
104
- @log_lines_json += ", " if index != last_index
105
- end
106
- @log_lines_json += "]"
107
- end
108
-
109
- def authorization_payload
110
- @authorization_payload ||= "Basic #{Base64.strict_encode64(application_key).chomp}"
111
- end
112
- end
113
- end
114
- end
115
- end
116
- end
@@ -1,87 +0,0 @@
1
- require "uri"
2
- require "net/http"
3
- require "net/https"
4
- require File.join(File.dirname(__FILE__), "log_truck", "delivery")
5
-
6
- module Timber
7
- module LogDevices
8
- class HTTP < LogDevice
9
- # Temporary class for alpha / beta purposes.
10
- # Log lines will be written to a file where a daemon
11
- # will pick them up. Most of this code will be moved
12
- # to that daemon.
13
- class LogTruck
14
- THROTTLE_SECONDS = 3.freeze
15
-
16
- class NoPayloadError < ArgumentError; end
17
-
18
- class << self
19
- def start!(options = {}, &_block)
20
- return if @thread && @thread.alive?
21
-
22
- # Old school options to support ruby 1.9 :(
23
- options[:throttle_seconds] = THROTTLE_SECONDS if !options.key?(:throttle_seconds)
24
- Config.logger.debug("Starting log truck with a #{options[:throttle_seconds]} second throttle")
25
-
26
- # A new thread for looping and monitoring. We need to
27
- # use a thread so that we can share memory.
28
- @thread = Thread.new do
29
- # ensure we always deliver upon exiting
30
- at_exit { deliver }
31
-
32
- # Keep looking for logs
33
- loop do
34
- deliver
35
-
36
- # Yield a block, primarily for testing purposes
37
- yield(Thread.current) if block_given?
38
-
39
- # Throttle to reduce checking the pile
40
- sleep options[:throttle_seconds]
41
- end
42
- end
43
-
44
- rescue Exception => e
45
- # failsafe to ensure we don't kill the app
46
- Config.logger.exception(e)
47
- end
48
-
49
- # Deliver, return LogTruck object, otherwise
50
- # raise an error.
51
- def deliver
52
- log_truck = nil
53
- LogPile.each do |log_pile|
54
- log_pile.empty do |log_lines|
55
- # LogPile only empties if no exception is raised
56
- begin
57
- # This will retry a number of times. If we can't get it during the retries
58
- # we drop the logs. Note, this strategy will improve when we write to a file
59
- # and use an actual agent.
60
- log_truck = new(log_pile.application_key, log_lines).tap(&:deliver!)
61
- rescue Delivery::DeliveryError => e
62
- Config.logger.exception(e)
63
- # TODO: How do we handle server timeouts? The request could have still been processed.
64
- end
65
- end
66
- end
67
- log_truck
68
- end
69
- end
70
-
71
- attr_reader :application_key, :log_lines
72
-
73
- def initialize(application_key, log_lines)
74
- if log_lines.empty?
75
- raise NoPayloadError.new("a truck must contain a payload (at least one log line)")
76
- end
77
- @application_key = application_key
78
- @log_lines = log_lines
79
- end
80
-
81
- def deliver!
82
- Delivery.new(application_key, log_lines).deliver!
83
- end
84
- end
85
- end
86
- end
87
- end
@@ -1,46 +0,0 @@
1
- module Timber
2
- module LogDevices
3
- class IO < LogDevice
4
- class Formatter
5
- # Do not change this, the API matches on it. Otherwise nothing
6
- # get parsed.
7
- CALLOUT = "@timber.io "
8
- CALLOUT_END = "@original "
9
-
10
- # Embed in a String to clear all previous ANSI sequences.
11
- CLEAR = "\e[0m"
12
- BOLD = "\e[1m"
13
-
14
- # Colors
15
- BLACK = "\e[30m"
16
- DARK_GRAY = "\e[1;30m"
17
- RED = "\e[31m"
18
- GREEN = "\e[32m"
19
- YELLOW = "\e[33m"
20
- BLUE = "\e[34m"
21
- MAGENTA = "\e[35m"
22
- CYAN = "\e[36m"
23
- WHITE = "\e[37m"
24
-
25
- def initialize(options = {})
26
- @ansi_format = options.key?(:ansi_format) ? options[:ansi_format] == true : true
27
- end
28
-
29
- def ansi_format?
30
- @ansi_format == true
31
- end
32
-
33
- def format(_log_line)
34
- raise NotImplementedError.new("#format is not implemented")
35
- end
36
-
37
- private
38
- def ansi_format(*args)
39
- text = args.pop
40
- return text unless ansi_format?
41
- "#{args.join}#{text}#{CLEAR}"
42
- end
43
- end
44
- end
45
- end
46
- end
@@ -1,41 +0,0 @@
1
- module Timber
2
- module LogDevices
3
- class IO < LogDevice
4
- class HybridFormatter < Formatter
5
- def initialize(options = {})
6
- super
7
- @date_prefix = options.key?(:date_prefix) ? options[:date_prefix] : false
8
- end
9
-
10
- def date_prefix?
11
- @date_prefix == true
12
- end
13
-
14
- def format(log_line)
15
- "#{log_line.message}#{context_message(log_line)}"
16
- end
17
-
18
- private
19
- def base_message(log_line)
20
- text = ""
21
- if date_prefix?
22
- text << "#{log_line.formatted_dt} "
23
- end
24
- text << log_line.message
25
- text
26
- end
27
-
28
- def context_message(log_line)
29
- # The callout must be before the formatting, otherwise we leave
30
- # the message ending with a color formatting and not a reset.
31
- # Anything before the callout modifies the original message.
32
- CALLOUT + ansi_format(DARK_GRAY, encoded_context(log_line))
33
- end
34
-
35
- def encoded_context(log_line)
36
- log_line.context_snapshot.to_logfmt
37
- end
38
- end
39
- end
40
- end
41
- end
@@ -1,36 +0,0 @@
1
- module Timber
2
- module LogDevices
3
- class IO < LogDevice
4
- class HybridHiddenFormatter < HybridFormatter
5
- CLEAR_SEQUENCE = "\e8\e[K".freeze
6
- CLEAR_STEP_SIZE = 20.freeze
7
- SAVE_CURSOR_POSITION = "\e7".freeze
8
-
9
- def format(log_line)
10
- "#{SAVE_CURSOR_POSITION}#{context_message(log_line)}#{base_message(log_line)}"
11
- end
12
-
13
- private
14
- def context_message(log_line)
15
- text = encoded_context(log_line)
16
- position = CLEAR_STEP_SIZE
17
- sequence_size = CLEAR_SEQUENCE.size
18
- step_size = sequence_size + CLEAR_STEP_SIZE
19
- while position < text.length
20
- # ensure we don't insert before a \
21
- while text[position - 1] == "\\"
22
- position += 1
23
- end
24
- text.insert(position, CLEAR_SEQUENCE)
25
- position += step_size
26
- end
27
- ansi_format(DARK_GRAY, "#{CALLOUT}#{CLEAR_SEQUENCE}#{text} #{CALLOUT_END}#{CLEAR_SEQUENCE}")
28
- end
29
-
30
- def encoded_context(log_line)
31
- log_line.context_snapshot.to_logfmt
32
- end
33
- end
34
- end
35
- end
36
- end
@@ -1,11 +0,0 @@
1
- module Timber
2
- module LogDevices
3
- class IO < LogDevice
4
- class JSONFormatter < Formatter
5
- def format(log_line)
6
- CALLOUT + log_line.to_json
7
- end
8
- end
9
- end
10
- end
11
- end
@@ -1,11 +0,0 @@
1
- module Timber
2
- module LogDevices
3
- class IO < LogDevice
4
- class LogfmtFormatter < Formatter
5
- def format(log_line)
6
- @CALLOUT + log_line.to_logfmt
7
- end
8
- end
9
- end
10
- end
11
- end
@@ -1,41 +0,0 @@
1
- require File.join(File.dirname(__FILE__), "io", "formatter")
2
- require File.join(File.dirname(__FILE__), "io", "hybrid_formatter")
3
- require File.join(File.dirname(__FILE__), "io", "hybrid_hidden_formatter")
4
- require File.join(File.dirname(__FILE__), "io", "json_formatter")
5
- require File.join(File.dirname(__FILE__), "io", "logfmt_formatter")
6
-
7
- module Timber
8
- module LogDevices
9
- # The purpose of a Timber log device is to take the raw log message and enrich it
10
- # with the current context.
11
- #
12
- # The IO log device works with any IO object. That is, any object that
13
- # response to #write(message).
14
- class IO < LogDevice
15
- attr_reader :formatter
16
-
17
- # Instantiates a new Timber IO log device.
18
- #
19
- # @param io [IO] any object the responds to #write(message)
20
- def initialize(io = STDOUT, options = {})
21
- io.sync = true if io.respond_to?(:sync=) # ensures logs are written immediately instead of being buffered by ruby
22
- @formatter = options[:formatter] || HybridHiddenFormatter.new
23
- @io = io
24
- end
25
-
26
- def close(*_args)
27
- io.close
28
- end
29
-
30
- private
31
- def write_log_line(log_line)
32
- formatted_message = formatter.format(log_line)
33
- io.write(formatted_message + "\n")
34
- end
35
-
36
- def io
37
- @io
38
- end
39
- end
40
- end
41
- end
@@ -1,33 +0,0 @@
1
- module Timber
2
- class LogLine
3
- include Patterns::ToJSON
4
- include Patterns::ToLogfmt
5
-
6
- # Raised when there is an issue with the message being passed.
7
- # Note: this is handled in Logger
8
- class InvalidMessageError < ArgumentError; end
9
-
10
- attr_reader :context_snapshot, :dt, :line_indexes, :message
11
-
12
- def initialize(message)
13
- @dt = Time.now.utc # Capture the time as soon as possible
14
- message = message.to_s
15
- if message.bytesize > APISettings::MESSAGE_BYTE_SIZE_MAX
16
- Config.logger.warn("Log line message is too long, truncating")
17
- message = message.byteslice(0, APISettings::MESSAGE_BYTE_SIZE_MAX)
18
- end
19
- @message = message
20
- CurrentLineIndexes.log_line_added(self) # Bump the indexes
21
- @context_snapshot = CurrentContext.snapshot
22
- end
23
-
24
- def formatted_dt
25
- @formatted_dt ||= Macros::DateFormatter.format(dt)
26
- end
27
-
28
- private
29
- def json_payload
30
- @json_payload ||= {:dt => formatted_dt, :message => message}.merge(context_snapshot.as_json)
31
- end
32
- end
33
- end
@@ -1,16 +0,0 @@
1
- module Timber
2
- module Macros
3
- module Compactor
4
- def self.compact(hash)
5
- new_hash = {}
6
- hash.each do |k, v|
7
- deep_v = v.is_a?(Hash) ? compact(v) : v
8
- if !deep_v.nil? && deep_v != [] && deep_v != {}
9
- new_hash[k] = deep_v
10
- end
11
- end
12
- new_hash
13
- end
14
- end
15
- end
16
- end
@@ -1,9 +0,0 @@
1
- module Timber
2
- module Macros
3
- module DateFormatter
4
- def self.format(dt)
5
- dt.send(APISettings::DATE_FORMAT, APISettings::DATE_FORMAT_PRECISION)
6
- end
7
- end
8
- end
9
- end
@@ -1,11 +0,0 @@
1
- module Timber
2
- module Macros
3
- # Deep merges hash keys
4
- module DeepMerger
5
- def self.merge(first, second)
6
- merger = proc { |_key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
7
- first.merge(second, &merger)
8
- end
9
- end
10
- end
11
- end