timber 1.1.14 → 2.0.0

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 (106) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -2
  3. data/.travis.yml +47 -0
  4. data/Gemfile +1 -28
  5. data/README.md +83 -298
  6. data/bin/timber +13 -0
  7. data/gemfiles/rails-3.0.gemfile +5 -0
  8. data/gemfiles/rails-3.1.gemfile +5 -0
  9. data/gemfiles/rails-3.2.gemfile +5 -0
  10. data/gemfiles/rails-4.0.gemfile +9 -0
  11. data/gemfiles/rails-4.1.gemfile +9 -0
  12. data/gemfiles/rails-4.2.gemfile +9 -0
  13. data/gemfiles/rails-5.0.gemfile +9 -0
  14. data/gemfiles/rails-edge.gemfile +7 -0
  15. data/lib/timber.rb +7 -7
  16. data/lib/timber/cli.rb +72 -0
  17. data/lib/timber/cli/api.rb +104 -0
  18. data/lib/timber/cli/application.rb +28 -0
  19. data/lib/timber/cli/install.rb +186 -0
  20. data/lib/timber/cli/io_helper.rb +58 -0
  21. data/lib/timber/cli/messages.rb +170 -0
  22. data/lib/timber/config.rb +47 -6
  23. data/lib/timber/contexts/http.rb +2 -2
  24. data/lib/timber/current_context.rb +1 -1
  25. data/lib/timber/event.rb +8 -0
  26. data/lib/timber/events.rb +2 -0
  27. data/lib/timber/events/controller_call.rb +12 -3
  28. data/lib/timber/events/exception.rb +4 -3
  29. data/lib/timber/events/http_client_request.rb +61 -0
  30. data/lib/timber/events/http_client_response.rb +47 -0
  31. data/lib/timber/events/http_server_request.rb +15 -23
  32. data/lib/timber/events/http_server_response.rb +9 -9
  33. data/lib/timber/events/sql_query.rb +2 -2
  34. data/lib/timber/events/template_render.rb +2 -2
  35. data/lib/timber/frameworks/rails.rb +31 -6
  36. data/lib/timber/integrations.rb +22 -0
  37. data/lib/timber/integrations/action_controller/log_subscriber.rb +25 -0
  38. data/lib/timber/integrations/action_controller/log_subscriber/timber_log_subscriber.rb +40 -0
  39. data/lib/timber/integrations/action_dispatch/debug_exceptions.rb +51 -0
  40. data/lib/timber/integrations/action_view/log_subscriber.rb +25 -0
  41. data/lib/timber/integrations/action_view/log_subscriber/timber_log_subscriber.rb +73 -0
  42. data/lib/timber/integrations/active_record/log_subscriber.rb +25 -0
  43. data/lib/timber/integrations/active_record/log_subscriber/timber_log_subscriber.rb +39 -0
  44. data/lib/timber/integrations/active_support/tagged_logging.rb +71 -0
  45. data/lib/timber/integrations/rack.rb +16 -0
  46. data/lib/timber/integrations/rack/exception_event.rb +28 -0
  47. data/lib/timber/integrations/rack/http_context.rb +25 -0
  48. data/lib/timber/integrations/rack/http_events.rb +46 -0
  49. data/lib/timber/integrations/rack/user_context.rb +59 -0
  50. data/lib/timber/integrations/rails/rack_logger.rb +49 -0
  51. data/lib/timber/integrator.rb +24 -0
  52. data/lib/timber/log_devices/http.rb +14 -21
  53. data/lib/timber/log_entry.rb +1 -1
  54. data/lib/timber/logger.rb +38 -12
  55. data/lib/timber/overrides.rb +9 -0
  56. data/lib/timber/overrides/lograge.rb +14 -0
  57. data/lib/timber/overrides/rails_server.rb +10 -0
  58. data/lib/timber/util.rb +2 -0
  59. data/lib/timber/util/active_support_log_subscriber.rb +13 -9
  60. data/lib/timber/util/http_event.rb +54 -0
  61. data/lib/timber/util/request.rb +44 -0
  62. data/lib/timber/version.rb +1 -1
  63. data/spec/README.md +5 -9
  64. data/spec/spec_helper.rb +1 -4
  65. data/spec/support/action_controller.rb +7 -3
  66. data/spec/support/active_record.rb +23 -19
  67. data/spec/support/rails.rb +56 -32
  68. data/spec/support/timber.rb +2 -3
  69. data/spec/support/webmock.rb +1 -0
  70. data/spec/timber/integrations/action_controller/log_subscriber_spec.rb +55 -0
  71. data/spec/timber/integrations/action_dispatch/debug_exceptions_spec.rb +53 -0
  72. data/spec/timber/integrations/action_view/log_subscriber_spec.rb +115 -0
  73. data/spec/timber/integrations/active_record/log_subscriber_spec.rb +46 -0
  74. data/spec/timber/integrations/rack/http_context_spec.rb +60 -0
  75. data/spec/timber/integrations/rails/rack_logger_spec.rb +58 -0
  76. data/spec/timber/logger_spec.rb +45 -9
  77. data/timber.gemspec +29 -3
  78. metadata +143 -46
  79. data/Appraisals +0 -41
  80. data/circle.yml +0 -33
  81. data/lib/timber/overrides/logger_add.rb +0 -38
  82. data/lib/timber/probe.rb +0 -23
  83. data/lib/timber/probes.rb +0 -23
  84. data/lib/timber/probes/action_controller_log_subscriber.rb +0 -20
  85. data/lib/timber/probes/action_controller_log_subscriber/log_subscriber.rb +0 -64
  86. data/lib/timber/probes/action_controller_user_context.rb +0 -52
  87. data/lib/timber/probes/action_dispatch_debug_exceptions.rb +0 -80
  88. data/lib/timber/probes/action_view_log_subscriber.rb +0 -20
  89. data/lib/timber/probes/action_view_log_subscriber/log_subscriber.rb +0 -69
  90. data/lib/timber/probes/active_record_log_subscriber.rb +0 -20
  91. data/lib/timber/probes/active_record_log_subscriber/log_subscriber.rb +0 -31
  92. data/lib/timber/probes/active_support_tagged_logging.rb +0 -63
  93. data/lib/timber/probes/rails_rack_logger.rb +0 -77
  94. data/lib/timber/rack_middlewares.rb +0 -12
  95. data/lib/timber/rack_middlewares/http_context.rb +0 -30
  96. data/spec/support/action_view.rb +0 -4
  97. data/spec/support/coveralls.rb +0 -2
  98. data/spec/support/simplecov.rb +0 -9
  99. data/spec/timber/overrides/logger_add_spec.rb +0 -26
  100. data/spec/timber/probes/action_controller_log_subscriber_spec.rb +0 -65
  101. data/spec/timber/probes/action_controller_user_context_spec.rb +0 -53
  102. data/spec/timber/probes/action_dispatch_debug_exceptions_spec.rb +0 -48
  103. data/spec/timber/probes/action_view_log_subscriber_spec.rb +0 -107
  104. data/spec/timber/probes/active_record_log_subscriber_spec.rb +0 -47
  105. data/spec/timber/probes/rails_rack_logger_spec.rb +0 -46
  106. data/spec/timber/rack_middlewares/http_context_spec.rb +0 -47
@@ -0,0 +1,58 @@
1
+ module Timber
2
+ class CLI
3
+ module IOHelper
4
+ def ask(message)
5
+ write message + " "
6
+ gets
7
+ end
8
+
9
+ def ask_yes_no(message)
10
+ case ask(message + " (y/n)")
11
+ when "y", "Y"
12
+ :yes
13
+ when "n", "N"
14
+ :no
15
+ else
16
+ puts "Woops! That's not a valid input. Please try again."
17
+ ask_yes_no(message)
18
+ end
19
+ end
20
+
21
+ def colorize(text, color)
22
+ return text if Gem.win_platform?
23
+
24
+ code =
25
+ case color
26
+ when :red then 31
27
+ when :green then 32
28
+ when :yellow then 33
29
+ else 0
30
+ end
31
+
32
+ "\e[#{code}m#{text}\e[0m"
33
+ end
34
+
35
+ def gets
36
+ value = stdin.gets
37
+ value ? value.chomp.downcase : ""
38
+ end
39
+
40
+ def puts(message)
41
+ stdout.puts(message)
42
+ end
43
+
44
+ def write(message)
45
+ stdout.write(message)
46
+ end
47
+
48
+ private
49
+ def stdout
50
+ $stdout
51
+ end
52
+
53
+ def stdin
54
+ $stdin
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,170 @@
1
+ # encoding: utf-8
2
+
3
+ module Timber
4
+ class CLI
5
+ module Messages
6
+ extend self
7
+
8
+ APP_URL = "https://app.timber.io"
9
+ DOCS_URL = "https://timber.io/docs"
10
+ REPO_URL = "https://github.com/timberio/timber-ruby"
11
+ SUPPORT_EMAIL = "support@timber.io"
12
+ TWITTER_HANDLE = "@timberdotio"
13
+ WEBSITE_URL = "https://timber.io"
14
+ MAX_LENGTH = 80.freeze
15
+
16
+ def application_details(app)
17
+ message = <<-MESSAGE
18
+ Woot! Your API 🔑 is valid. Here are you application details:
19
+
20
+ Name: #{app.name} (#{app.environment})
21
+ Framework: #{app.framework_type}
22
+ Platform: #{app.platform_type}
23
+ MESSAGE
24
+ message.rstrip
25
+ end
26
+
27
+ def edit_app_url(app)
28
+ "#{APP_URL}"
29
+ end
30
+
31
+ def bad_experience_message
32
+ message = <<-MESSAGE
33
+ Bummer! That is certainly not the experience we were going for.
34
+
35
+ Could you tell us why you a bad experience?
36
+
37
+ (this will be sent directly to the Timber engineering team)
38
+ MESSAGE
39
+ message.rstrip
40
+ end
41
+
42
+ def commit_and_deploy_reminder
43
+ message = <<-MESSAGE
44
+ Last step! Commit these changes and deploy. 🚀
45
+ MESSAGE
46
+ message.rstrip
47
+ end
48
+
49
+ def contact
50
+ message = <<-MESSAGE
51
+ Website: #{WEBSITE_URL}
52
+ Documentation: #{DOCS_URL}
53
+ Support: #{SUPPORT_EMAIL}
54
+ MESSAGE
55
+ message.rstrip
56
+ end
57
+
58
+ def http_environment_variables(api_key)
59
+ message = <<-MESSAGE
60
+ Great! Add this variable to your environment:
61
+
62
+ export TIMBER_API_KEY="#{api_key}"
63
+
64
+ MESSAGE
65
+ message.rstrip
66
+ end
67
+
68
+ def free_data
69
+ message = <<-MESSAGE
70
+ Get free data on Timeber!
71
+
72
+ * Timber URL: https://app.timber.io
73
+ * Get ✨ 250mb✨ for tweeting your experience to #{TWITTER_HANDLE}
74
+ * Get ✨ 100mb✨ for starring our repo: #{REPO_URL}
75
+ * Get ✨ 50mb✨ for following #{TWITTER_HANDLE} on twitter
76
+
77
+ (Your account will be credited within 2-3 business days.
78
+ If you do not notice a credit please contact us: #{@support_email})
79
+ MESSAGE
80
+ message.rstrip
81
+ end
82
+
83
+ def header
84
+ message = <<-MESSAGE
85
+ 🌲 Timber.io Ruby Installer
86
+
87
+ ^ ^ ^ ^ ___I_ ^ ^ ^ ^ ^ ^ ^
88
+ /|\\/|\\/|\\ /|\\ /\\-_--\\ /|\\/|\\ /|\\/|\\/|\\ /|\\/|\\
89
+ /|\\/|\\/|\\ /|\\ / \\_-__\\ /|\\/|\\ /|\\/|\\/|\\ /|\\/|\\
90
+ /|\\/|\\/|\\ /|\\ |[]| [] | /|\\/|\\ /|\\/|\\/|\\ /|\\/|\\
91
+ MESSAGE
92
+ message.rstrip
93
+ end
94
+
95
+ def heroku_install(app)
96
+ message = <<-MESSAGE
97
+ Now we need to send your logs to the Timber service.
98
+
99
+ Please run this command in a separate terminal and return back here when complete:
100
+
101
+ heroku drains:add #{app.heroku_drain_url}
102
+
103
+ Note: Your Heroku app must be generating logs for this to work.
104
+ MESSAGE
105
+ message.rstrip
106
+ end
107
+
108
+ def no_api_key_provided
109
+ message = <<-MESSAGE
110
+ Woops! You didn't provide an API key. You can do so via:
111
+
112
+ bundle exec timber install my-api-key
113
+
114
+ #{obtain_key_instructions}
115
+ MESSAGE
116
+ message.rstrip
117
+ end
118
+
119
+ def obtain_key_instructions
120
+ message = <<-MESSAGE
121
+ Don't have a key? Head over to:
122
+
123
+ https://app.timber.io
124
+
125
+ Once there, create an application. Your API key will be displayed afterwards.
126
+ For more detailed instructions, checkout our docs page:
127
+ https://timber.io/docs/app/obtain-api-key/
128
+ MESSAGE
129
+ message.rstrip
130
+ end
131
+
132
+ def separator
133
+ "--------------------------------------------------------------------------------"
134
+ end
135
+
136
+ def spinner(iteration)
137
+ rem = iteration % 3
138
+ case rem
139
+ when 0
140
+ "/"
141
+ when 1
142
+ "-"
143
+ when 2
144
+ "\\"
145
+ end
146
+ end
147
+
148
+ def success
149
+ "✓ Success!"
150
+ end
151
+
152
+ def task_complete(message)
153
+ remainder = MAX_LENGTH - message.length - success.length
154
+
155
+ dots = "." * remainder
156
+ "\r#{message}#{dots}#{success}"
157
+ end
158
+
159
+ def task_start(message)
160
+ remainder = MAX_LENGTH - message.length - success.length
161
+
162
+ "\r#{message}" + ("." * remainder)
163
+ end
164
+
165
+ def we_love_you_too
166
+ "💖 We love you too! Let's get to loggin' 🌲"
167
+ end
168
+ end
169
+ end
170
+ end
@@ -1,18 +1,59 @@
1
1
  require "singleton"
2
2
 
3
3
  module Timber
4
- # Interface for configuring Timber.
4
+ # Interface for settings and reading Timber configuration.
5
5
  #
6
- # @note If using rails this will be installed in the `config` object via `config.timber`.
6
+ # You can override any configuration supplied here by simply setting it:
7
+ #
8
+ # # Rails
9
+ # config.timber.api_key = "my api key"
10
+ #
11
+ # # Everything else
12
+ # Timber::Config.instance.api_key = "my api key"
13
+ #
14
+ # If a value is not explicity set, the environment is checked for it's associated
15
+ # environment variable. If that is not set, a reasonable default will be chosen. Each
16
+ # method documents this.
7
17
  class Config
18
+ class NoLoggerError < StandardError; end
19
+
20
+ FORM_URL_ENCODED_CONTENT_TYPE = "application/x-www-form-urlencoded".freeze
21
+ JSON_CONTENT_TYPE = "application/json".freeze
22
+
8
23
  include Singleton
9
24
 
10
- attr_writer :logger
25
+ attr_writer :capture_http_bodies, :debug_logger, :log_formatter, :logger
26
+
27
+ def initialize
28
+ @capture_http_bodies = true
29
+ @capture_http_body_content_types = [FORM_URL_ENCODED_CONTENT_TYPE, JSON_CONTENT_TYPE]
30
+ end
31
+
32
+ # Enables and disables the capturing of HTTP bodies in `Events::HTTPServerRequest`,
33
+ # `HTTPClientRequest`, and `HTTPClientRespone`.
34
+ def capture_http_bodies?
35
+ @capture_http_bodies == true
36
+ end
37
+
38
+ # Limits HTTP body capturing to the listed content types. This must be an array.
39
+ def capture_http_body_content_types
40
+ @capture_http_body_content_types ||= []
41
+ end
42
+
43
+ # Set a debug_logger to view internal Timber library log message.
44
+ # Useful for debugging. Defaults to `nil`. If set, debug messages will be
45
+ # written to this logger.
46
+ def debug_logger
47
+ @debug_logger
48
+ end
49
+
11
50
 
12
- # Set a logger to view internal Timber library log message.
13
- # Useful for debugging. Defaults to `::Logger.new(nil)`.
51
+ # This is the logger Timber writes to. It should be set to your global
52
+ # logger to keep the logging destination consitent. Please see `delegate_logger_to`
53
+ # to delegate this call to another method. This is set to `Rails.logger`
54
+ # for rails.
14
55
  def logger
15
- @logger ||= Logger.new(nil)
56
+ @logger || raise(NoLoggerError.new)
16
57
  end
17
58
  end
18
59
  end
@@ -4,8 +4,8 @@ module Timber
4
4
  # as join data across your logs, allowing you to query all logs for any attribute
5
5
  # presented here. For example, viewing all logs for a given request_id.
6
6
  #
7
- # @note This context should be installed automatically through probes,
8
- # such as the {Probes::RackHTTPContext} probe.
7
+ # @note This context should be installed automatically through integrations,
8
+ # such as the {Intregrations::Rack::HTTPContext} rack middleware.
9
9
  class HTTP < Context
10
10
  @keyspace = :http
11
11
 
@@ -56,7 +56,7 @@ module Timber
56
56
  # # ... anything logged here will include the context ...
57
57
  # end
58
58
  # # Be sure to checkout Timber::Contexts! These are officially supported and many of these
59
- # # will be automatically included via Timber::Probes
59
+ # # will be automatically included via Timber::Integrations
60
60
  #
61
61
  # @example Adding multiple contexts
62
62
  # Timber::CurrentContext.with(context1, context2) { ... }
@@ -10,6 +10,14 @@ module Timber
10
10
  raise NotImplementedError.new
11
11
  end
12
12
 
13
+ # This ensures that Timber events get logged as messages if they are passed to
14
+ # the standard ::Logger.
15
+ #
16
+ # See: https://github.com/ruby/ruby/blob/f6e77b9d3555c1fbaa8aab1cdc0bd6bde95f62c6/lib/logger.rb#L615
17
+ def inspect
18
+ message
19
+ end
20
+
13
21
  def to_json(options = {})
14
22
  as_json.to_json(options)
15
23
  end
@@ -1,6 +1,8 @@
1
1
  require "timber/events/controller_call"
2
2
  require "timber/events/custom"
3
3
  require "timber/events/exception"
4
+ require "timber/events/http_client_request"
5
+ require "timber/events/http_client_response"
4
6
  require "timber/events/http_server_request"
5
7
  require "timber/events/http_server_response"
6
8
  require "timber/events/sql_query"
@@ -4,8 +4,8 @@ module Timber
4
4
  #
5
5
  # Processing by PagesController#home as HTML
6
6
  #
7
- # @note This event should be installed automatically through probes,
8
- # such as the {Probes::ActionControllerLogSubscriber} probe.
7
+ # @note This event should be installed automatically through integrations,
8
+ # such as the {Integrations::ActionController::LogSubscriber} integration.
9
9
  class ControllerCall < Timber::Event
10
10
  attr_reader :controller, :action, :params, :format
11
11
 
@@ -17,7 +17,7 @@ module Timber
17
17
  end
18
18
 
19
19
  def to_hash
20
- {controller: controller, action: action}
20
+ {controller: controller, action: action, params_json: params_json}
21
21
  end
22
22
  alias to_h to_hash
23
23
 
@@ -35,6 +35,15 @@ module Timber
35
35
  end
36
36
  message
37
37
  end
38
+
39
+ private
40
+ def params_json
41
+ @params_json ||= if params.nil? || params == {}
42
+ nil
43
+ else
44
+ params.to_json
45
+ end
46
+ end
38
47
  end
39
48
  end
40
49
  end
@@ -2,8 +2,8 @@ module Timber
2
2
  module Events
3
3
  # The exception event is used to track exceptions.
4
4
  #
5
- # @note This event should be installed automatically through probes,
6
- # such as the {Probes::ActionDispatchDebugExceptions} probe.
5
+ # @note This event should be installed automatically through integrations,
6
+ # such as the {Integrations::ActionDispatch::DebugExceptions} integration.
7
7
  class Exception < Timber::Event
8
8
  attr_reader :name, :exception_message, :backtrace
9
9
 
@@ -16,7 +16,8 @@ module Timber
16
16
  raise(ArgumentError.new(":backtrace is required"))
17
17
  end
18
18
 
19
- @backtrace = backtrace.collect { |line| parse_backtrace_line(line) }
19
+ # 10 items max
20
+ @backtrace = backtrace[0..9].collect { |line| parse_backtrace_line(line) }
20
21
  end
21
22
 
22
23
  def to_hash
@@ -0,0 +1,61 @@
1
+ module Timber
2
+ module Events
3
+ # The HTTP client request event tracks *outgoing* HTTP requests giving you structured insight
4
+ # into communication with external services.
5
+ #
6
+ # @note This event should be installed automatically through integrations,
7
+ # such as the {Integrations::NetHTTP} integration.
8
+ class HTTPClientRequest < Timber::Event
9
+ attr_reader :body, :headers, :host, :method, :path, :port, :query_string, :request_id,
10
+ :scheme, :service_name
11
+
12
+ def initialize(attributes)
13
+ @headers = Util::HTTPEvent.normalize_headers(attributes[:headers])
14
+ @host = attributes[:host] || raise(ArgumentError.new(":host is required"))
15
+ @method = Util::HTTPEvent.normalize_method(attributes[:method]) || raise(ArgumentError.new(":method is required"))
16
+ @path = attributes[:path] || raise(ArgumentError.new(":path is required"))
17
+ @port = attributes[:port]
18
+ @query_string = Util::HTTPEvent.normalize_query_string(attributes[:query_string])
19
+ @request_id = attributes[:request_id]
20
+ @scheme = attributes[:scheme] || raise(ArgumentError.new(":scheme is required"))
21
+ @service_name = attributes[:service_name]
22
+
23
+ @body = Util::HTTPEvent.normalize_body(@headers["content-type"], attributes[:body])
24
+ end
25
+
26
+ def to_hash
27
+ {headers: headers, host: host, method: method, parsed_body_json: parsed_body_json,
28
+ path: path, port: port, query_string: query_string, request_id: request_id,
29
+ scheme: scheme, service_name: service_name}
30
+ end
31
+ alias to_h to_hash
32
+
33
+ def as_json(_options = {})
34
+ {:server_side_app => {:http_client_request => to_hash}}
35
+ end
36
+
37
+ def message
38
+ message = 'Outgoing HTTP request to '
39
+
40
+ if service_name
41
+ mesage << " #{service_name} [#{method}] #{full_path}"
42
+ else
43
+ message << " [#{method}] #{full_url}"
44
+ end
45
+ end
46
+
47
+ def status_description
48
+ Rack::Utils::HTTP_STATUS_CODES[status]
49
+ end
50
+
51
+ private
52
+ def full_path
53
+ Util::HTTPEvent.full_path(path, query_string)
54
+ end
55
+
56
+ def full_url
57
+ "#{scheme}#{host}#{full_path}"
58
+ end
59
+ end
60
+ end
61
+ end