timber 1.1.14 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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