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,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
4
+ require "timber/cli"
5
+
6
+ begin
7
+ Timber::CLI.run
8
+ rescue => e
9
+ raise e if $DEBUG
10
+ STDERR.puts e.message
11
+ STDERR.puts e.backtrace.join("\n")
12
+ exit 1
13
+ end
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rails', '~> 3.0.0'
4
+
5
+ gemspec :path => '../'
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rails', '~> 3.1.0'
4
+
5
+ gemspec :path => '../'
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rails', '~> 3.2.0'
4
+
5
+ gemspec :path => '../'
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rails', '~> 4.0.0'
4
+
5
+ if RUBY_PLATFORM == "java"
6
+ gem 'mime-types', '2.6.2'
7
+ end
8
+
9
+ gemspec :path => '../'
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rails', '~> 4.1.0'
4
+
5
+ if RUBY_PLATFORM == "java"
6
+ gem 'mime-types', '2.6.2'
7
+ end
8
+
9
+ gemspec :path => '../'
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rails', '~> 4.2.0'
4
+
5
+ if RUBY_PLATFORM == "java"
6
+ gem 'mime-types', '2.6.2'
7
+ end
8
+
9
+ gemspec :path => '../'
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rails', '~> 5.0.0'
4
+
5
+ if RUBY_PLATFORM == "java"
6
+ gem 'mime-types', '2.6.2'
7
+ end
8
+
9
+ gemspec :path => '../'
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rack', github: 'rack/rack', branch: 'master'
4
+ gem 'arel', github: 'rails/arel', branch: 'master'
5
+ gem 'rails', github: 'rails/rails', branch: 'master'
6
+
7
+ gemspec :path => '../'
@@ -1,14 +1,12 @@
1
1
  # core classes
2
2
  require "json" # brings to_json to the core classes
3
3
 
4
- require "timber/overrides/logger_add"
5
- require "timber/overrides/rails_stdout_logging"
6
-
7
4
  # Base (must come first, order matters)
5
+ require "timber/overrides"
8
6
  require "timber/config"
9
7
  require "timber/context"
10
8
  require "timber/event"
11
- require "timber/probe"
9
+ require "timber/integrator"
12
10
  require "timber/util"
13
11
  require "timber/version"
14
12
 
@@ -19,8 +17,10 @@ require "timber/events"
19
17
  require "timber/log_devices"
20
18
  require "timber/log_entry"
21
19
  require "timber/logger"
22
- require "timber/probes"
23
- require "timber/rack_middlewares"
20
+ require "timber/integrations"
24
21
 
25
22
  # Load frameworks
26
- require "timber/frameworks"
23
+ require "timber/frameworks"
24
+
25
+ module Timber
26
+ end
@@ -0,0 +1,72 @@
1
+ require "optparse"
2
+ require "yaml"
3
+ require "timber"
4
+
5
+
6
+ require "timber/cli/api"
7
+ require "timber/cli/application"
8
+ require "timber/cli/io_helper"
9
+ require "timber/cli/messages"
10
+
11
+ require "timber/cli/install"
12
+
13
+ module Timber
14
+ # @private
15
+ class CLI
16
+ AVAILABLE_COMMANDS = %w(install).freeze
17
+
18
+ class << self
19
+ attr_accessor :options
20
+
21
+ def run(argv = ARGV)
22
+ @options = {}
23
+ global = global_option_parser
24
+ commands = command_option_parser
25
+ global.order!(argv)
26
+ command = argv.shift
27
+ if command
28
+ if AVAILABLE_COMMANDS.include?(command)
29
+ commands[command].parse!(argv)
30
+ case command.to_sym
31
+ when :install
32
+ Timber::CLI::Install.run(argv.shift)
33
+ end
34
+ else
35
+ puts "Command '#{command}' does not exist, run timber -h to "\
36
+ "see the help"
37
+ exit(1)
38
+ end
39
+ else
40
+ # Print help
41
+ puts global
42
+ exit(0)
43
+ end
44
+ end
45
+
46
+ def global_option_parser
47
+ OptionParser.new do |o|
48
+ o.banner = "Usage: timber <command> [options]"
49
+
50
+ o.on "-v", "--version", "Print version and exit" do |_arg|
51
+ puts "Timber #{Timber::VERSION}"
52
+ exit(0)
53
+ end
54
+
55
+ o.on "-h", "--help", "Show help and exit" do
56
+ puts o
57
+ exit(0)
58
+ end
59
+
60
+ o.separator ""
61
+ o.separator "Available commands: #{AVAILABLE_COMMANDS.join(", ")}"
62
+ end
63
+ end
64
+
65
+ def command_option_parser
66
+ {
67
+ "install" => OptionParser.new
68
+ }
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,104 @@
1
+ require "base64"
2
+ require "json"
3
+ require "net/https"
4
+ require "securerandom"
5
+ require "uri"
6
+
7
+ module Timber
8
+ class CLI
9
+ class API
10
+ class APIKeyInvalidError < StandardError
11
+ def message
12
+ "Uh oh! The API key supplied is invalid. Please ensure that you copied the" \
13
+ " key properly.\n\n#{Messages.obtain_key_instructions}"
14
+ end
15
+ end
16
+
17
+ class NoAPIKeyError < StandardError
18
+ def message
19
+ "Uh oh! You didn't supply an API key.\n\n#{Messages.obtain_key_instructions}"
20
+ end
21
+ end
22
+
23
+ TIMBER_API_URI = URI.parse('https://api.timber.io')
24
+ APPLICATION_PATH = "/installer/application".freeze
25
+ EVENT_PATH = "/installer/events".freeze
26
+ HAS_LOGS_PATH = "/installer/has_logs".freeze
27
+ USER_AGENT = "Timber Ruby/#{Timber::VERSION} (HTTP)".freeze
28
+
29
+ def initialize(api_key)
30
+ @api_key = api_key
31
+ @session_id = SecureRandom.uuid
32
+ end
33
+
34
+ def application!
35
+ get!(APPLICATION_PATH)
36
+ end
37
+
38
+ def event!(name, data = {})
39
+ post!(EVENT_PATH, event: {name: name, data: data})
40
+ end
41
+
42
+ def wait_for_logs(iteration = 0, &block)
43
+ if block_given?
44
+ yield iteration
45
+ end
46
+
47
+ sleep 0.5
48
+
49
+ res = get!(HAS_LOGS_PATH)
50
+
51
+ case res.code
52
+ when "202"
53
+ wait_for_logs(iteration + 1, &block)
54
+
55
+ when "204"
56
+ true
57
+ end
58
+ end
59
+
60
+ private
61
+ def get!(path)
62
+ req = Net::HTTP::Get.new(path)
63
+ issue!(req)
64
+ end
65
+
66
+ def post!(path, body)
67
+ req = Net::HTTP::Post.new(path)
68
+ req.body = body.to_json
69
+ req['Content-Type'] = "application/json"
70
+ issue!(req)
71
+ end
72
+
73
+ def issue!(req)
74
+ req['Authorization'] = "Basic #{encoded_api_key}"
75
+ req['User-Agent'] = USER_AGENT
76
+ req['X-Installer-Session-Id'] = @session_id
77
+ res = http.start do |http|
78
+ http.request(req)
79
+ end
80
+
81
+ if res.code == "401"
82
+ raise NoAPIKeyError.new
83
+ elsif res.code == "403"
84
+ raise APIKeyInvalidError.new
85
+ else
86
+ res
87
+ end
88
+ end
89
+
90
+ def http
91
+ @http ||= begin
92
+ http = Net::HTTP.new(TIMBER_API_URI.host, TIMBER_API_URI.port)
93
+ http.use_ssl = true
94
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
95
+ http
96
+ end
97
+ end
98
+
99
+ def encoded_api_key
100
+ Base64.urlsafe_encode64(@api_key).chomp
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,28 @@
1
+ require "json"
2
+
3
+ module Timber
4
+ class CLI
5
+ class Application
6
+
7
+ attr_reader :api_key, :environment, :framework_type, :heroku_drain_url, :language_type,
8
+ :name, :platform_type
9
+
10
+ def initialize(api)
11
+ res = api.application!
12
+ parsed_body = JSON.parse(res.body)
13
+ application_data = parsed_body.fetch("data")
14
+ @api_key = application_data.fetch("api_key")
15
+ @environment = application_data.fetch("environment")
16
+ @framework_type = application_data.fetch("framework_type")
17
+ @heroku_drain_url = application_data.fetch("heroku_drain_url")
18
+ @language_type = application_data.fetch("language_type")
19
+ @name = application_data.fetch("name")
20
+ @platform_type = application_data.fetch("platform_type")
21
+ end
22
+
23
+ def heroku?
24
+ platform_type == "heroku"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,186 @@
1
+ require "fileutils"
2
+
3
+ module Timber
4
+ class CLI
5
+ class Install
6
+ EXCLUDED_ENVIRONMENTS = ["test"].freeze
7
+
8
+ class << self
9
+ include IOHelper
10
+
11
+ def run(api_key)
12
+ puts ""
13
+ puts colorize(Messages.header, :green)
14
+ puts colorize(Messages.separator, :green)
15
+ puts colorize(Messages.contact, :green)
16
+ puts colorize(Messages.separator, :green)
17
+ puts ""
18
+
19
+ if !api_key
20
+ puts colorize(Messages.no_api_key_provided, :red)
21
+ return
22
+ end
23
+
24
+ api = API.new(api_key)
25
+
26
+ api.event!(:started)
27
+
28
+ app = Application.new(api)
29
+
30
+ puts Messages.application_details(app)
31
+
32
+ case ask_yes_no("Are the above details correct?")
33
+ when :yes
34
+ if app.heroku?
35
+ create_initializer(:stdout)
36
+
37
+ puts ""
38
+ puts Messages.separator
39
+ puts ""
40
+ puts Messages.heroku_install(app)
41
+ puts ""
42
+
43
+ else
44
+ puts ""
45
+ puts Messages.separator
46
+ puts ""
47
+ puts "How would you like configure Timber?"
48
+ puts ""
49
+ puts "1) Using environment variables"
50
+ puts "2) Configuring in my app"
51
+ puts ""
52
+
53
+ case ask("Enter your choice: (1/2) ")
54
+ when "1"
55
+ create_initializer(:http, :api_key_code => "ENV['TIMBER_API_KEY']")
56
+
57
+ puts ""
58
+ puts Messages.http_environment_variables(app.api_key)
59
+ puts ""
60
+
61
+ when "2"
62
+ create_initializer(:http, :api_key_code => "'#{app.api_key}'")
63
+
64
+ puts ""
65
+ end
66
+
67
+ send_test_messages(api_key)
68
+ end
69
+
70
+ api.wait_for_logs do |iteration|
71
+ write Messages.task_start("Waiting for logs")
72
+ write Messages.spinner(iteration)
73
+ end
74
+
75
+ puts colorize(Messages.task_complete("Waiting for logs"), :green)
76
+
77
+ puts ""
78
+ puts Messages.separator
79
+ puts ""
80
+ puts Messages.free_data
81
+ puts ""
82
+ puts Messages.separator
83
+ puts ""
84
+ puts colorize(Messages.commit_and_deploy_reminder, :green)
85
+
86
+ api.event!(:success)
87
+
88
+ collect_feedback(api)
89
+
90
+ when :no
91
+ puts ""
92
+ puts "Bummer. Head to this URL to update the details:"
93
+ puts ""
94
+ puts " #{Messages.edit_app_url(app)}"
95
+ puts ""
96
+ puts "exiting..."
97
+ return false
98
+ end
99
+ end
100
+
101
+ private
102
+ def create_initializer(log_device_type, options = {})
103
+ puts ""
104
+ write Messages.task_start("Creating config/initializers/timber.rb")
105
+
106
+ logger_code = \
107
+ case log_device_type
108
+ when :http
109
+ api_key_code = options[:api_key_code] || raise(ArgumentError.new("the :api_key_code option is required"))
110
+ "log_device = Timber::LogDevices::HTTP.new(#{api_key_code})\n" +
111
+ "Timber::Logger.new(log_device)"
112
+
113
+ when :stdout
114
+ "Timber::Logger.new(STDOUT)"
115
+ end
116
+
117
+ body = <<-BODY
118
+ # Timber.io Ruby Library
119
+ #
120
+ # ^ ^ ^ ^ ___I_ ^ ^ ^ ^ ^ ^ ^
121
+ # /|\\/|\\/|\\ /|\\ /\\-_--\\ /|\\/|\\ /|\\/|\\/|\\ /|\\/|\\
122
+ # /|\\/|\\/|\\ /|\\ / \\_-__\\ /|\\/|\\ /|\\/|\\/|\\ /|\\/|\\
123
+ # /|\\/|\\/|\\ /|\\ |[]| [] | /|\\/|\\ /|\\/|\\/|\\ /|\\/|\\
124
+ #
125
+ # Library: http://github.com/timberio/timber-ruby
126
+ # Docs: http://www.rubydoc.info/github/timberio/timber-ruby
127
+ # Support: support@timber.io
128
+
129
+ logger = case Rails.env
130
+ when "development", "test"
131
+ logger = Timber::Logger.new(STDOUT)
132
+ logger.formatter = Timber::Logger::SimpleFormatter.new
133
+ logger
134
+ else
135
+ #{logger_code}
136
+ end
137
+
138
+ Timber::Frameworks::Rails.set_logger(logger)
139
+ BODY
140
+
141
+ FileUtils.mkdir_p(File.join(Dir.pwd, "config", "initializers"))
142
+ File.write(File.join(Dir.pwd, "config/initializers/timber.rb"), body)
143
+
144
+ puts colorize(Messages.task_complete("Creating config/initializers/timber.rb"), :green)
145
+ end
146
+
147
+ def send_test_messages(api_key)
148
+ write Messages.task_start("Sending test logs")
149
+
150
+ http_device = LogDevices::HTTP.new(api_key)
151
+ logger = Logger.new(http_device)
152
+ logger.info("test")
153
+
154
+ puts colorize(Messages.task_complete("Sending test logs"), :green)
155
+ end
156
+
157
+ def collect_feedback(api)
158
+ puts ""
159
+ puts Messages.separator
160
+ puts ""
161
+ rating = ask("How would rate this install experience? 1 (bad) - 5 (perfect)")
162
+ case rating
163
+ when "4", "5"
164
+ api.event!(:feedback, rating: rating.to_i)
165
+ puts ""
166
+ puts Messages.we_love_you_too
167
+
168
+ when "1", "2", "3"
169
+ puts ""
170
+ puts Messages.bad_experience_message
171
+ puts ""
172
+
173
+ comments = ask("Type your comments (enter sends)")
174
+
175
+ api.event!(:feedback, rating: rating.to_i, comments: comments)
176
+
177
+ puts ""
178
+ puts "Thank you! We take feedback seriously and will work to improve this."
179
+ end
180
+
181
+ puts ""
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end