full_request_logger 0.1 → 0.2

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +147 -0
  3. data/README.md +1 -1
  4. data/app/controllers/rails/conductor/full_request_logger/request_logs_controller.rb +15 -3
  5. data/app/views/rails/conductor/full_request_logger/request_logs/index.html.erb +7 -0
  6. data/app/views/rails/conductor/full_request_logger/request_logs/show.html.erb +5 -2
  7. data/config/routes.rb +1 -1
  8. data/full_request_logger.gemspec +3 -2
  9. data/lib/full_request_logger/middleware.rb +1 -1
  10. data/lib/full_request_logger/recorder.rb +24 -7
  11. data/test/dummy/.babelrc +18 -0
  12. data/test/dummy/.gitignore +3 -0
  13. data/test/dummy/.postcssrc.yml +3 -0
  14. data/test/dummy/Rakefile +6 -0
  15. data/test/dummy/app/assets/config/manifest.js +3 -0
  16. data/test/dummy/app/assets/images/.keep +0 -0
  17. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  18. data/test/dummy/app/assets/stylesheets/scaffold.css +80 -0
  19. data/test/dummy/app/channels/application_cable/channel.rb +4 -0
  20. data/test/dummy/app/channels/application_cable/connection.rb +4 -0
  21. data/test/dummy/app/controllers/application_controller.rb +2 -0
  22. data/test/dummy/app/controllers/concerns/.keep +0 -0
  23. data/test/dummy/app/helpers/application_helper.rb +2 -0
  24. data/test/dummy/app/javascript/packs/application.js +0 -0
  25. data/test/dummy/app/jobs/application_job.rb +2 -0
  26. data/test/dummy/app/mailboxes/application_mailbox.rb +2 -0
  27. data/test/dummy/app/mailboxes/messages_mailbox.rb +4 -0
  28. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  29. data/test/dummy/app/models/application_record.rb +3 -0
  30. data/test/dummy/app/models/concerns/.keep +0 -0
  31. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  32. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  33. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  34. data/test/dummy/bin/bundle +3 -0
  35. data/test/dummy/bin/rails +4 -0
  36. data/test/dummy/bin/rake +4 -0
  37. data/test/dummy/bin/setup +36 -0
  38. data/test/dummy/bin/update +31 -0
  39. data/test/dummy/bin/yarn +11 -0
  40. data/test/dummy/config.ru +5 -0
  41. data/test/dummy/config/application.rb +19 -0
  42. data/test/dummy/config/boot.rb +5 -0
  43. data/test/dummy/config/cable.yml +10 -0
  44. data/test/dummy/config/environment.rb +5 -0
  45. data/test/dummy/config/environments/development.rb +34 -0
  46. data/test/dummy/config/environments/production.rb +96 -0
  47. data/test/dummy/config/environments/test.rb +36 -0
  48. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  49. data/test/dummy/config/initializers/assets.rb +14 -0
  50. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  51. data/test/dummy/config/initializers/content_security_policy.rb +22 -0
  52. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  53. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  54. data/test/dummy/config/initializers/inflections.rb +16 -0
  55. data/test/dummy/config/initializers/mime_types.rb +4 -0
  56. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  57. data/test/dummy/config/locales/en.yml +33 -0
  58. data/test/dummy/config/puma.rb +34 -0
  59. data/test/dummy/config/routes.rb +4 -0
  60. data/test/dummy/config/spring.rb +6 -0
  61. data/test/dummy/config/webpack/development.js +3 -0
  62. data/test/dummy/config/webpack/environment.js +3 -0
  63. data/test/dummy/config/webpack/production.js +3 -0
  64. data/test/dummy/config/webpack/test.js +3 -0
  65. data/test/dummy/config/webpacker.yml +65 -0
  66. data/test/dummy/lib/assets/.keep +0 -0
  67. data/test/dummy/log/.keep +0 -0
  68. data/test/dummy/public/404.html +67 -0
  69. data/test/dummy/public/422.html +67 -0
  70. data/test/dummy/public/500.html +66 -0
  71. data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
  72. data/test/dummy/public/apple-touch-icon.png +0 -0
  73. data/test/dummy/public/favicon.ico +0 -0
  74. data/test/recorder_test.rb +28 -3
  75. metadata +145 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bbae2d623039dd9c1b3f1f956adcb5769a4eae0aaae14b2bcaa2f1272fd3d8ac
4
- data.tar.gz: 8799755a93425af26cf02bb6fa38f53cc0087ff1cea0318845f26326dc0a5b0c
3
+ metadata.gz: 69103e9dda0de7ad14689c9479265fa4148620f406ba2cecc10e7350db55c183
4
+ data.tar.gz: ad9049fdc9cc3c55e462dee1205af4df4239522f1a2f9902c3d0457e4e2b4de3
5
5
  SHA512:
6
- metadata.gz: 3d92ba086bb4f520f6b8e97143c7c82dbc69d7c8c1dd03fd27de96faa5d773c6cbc7a85d4f8e3db002422a8682ee6bf5d5202c4a078e2d3a7d79eb835882e5b5
7
- data.tar.gz: 0e4bd4e58a52d3eac3d1c09ecda279c0aaa547cfeaf4802135bc82958ecd188bfe9728a8b82ea890b29d4b2c61eb90f0a4f05ec9e44647786c39846f98f5b1c1
6
+ metadata.gz: dec6f6c382133e02c94d8c875bab02cd925c046f91b6f0a85b742e707863fc4dbcee5256528d5d2332bd2c69ce251660b82227bb035ffecdcb843ac0ea2c47e9
7
+ data.tar.gz: a9f6274384f9a81f4178cca95b99b28dbd72a9a960309055f0524b7c6c4ac2cea4a3508f36eb535d64c61d2191f30fbfc250aab7aa4600e982a869eb3a47d502
data/Gemfile.lock ADDED
@@ -0,0 +1,147 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ full_request_logger (0.1)
5
+ rails (>= 6.0.0)
6
+ redis (>= 4.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ actioncable (6.0.0)
12
+ actionpack (= 6.0.0)
13
+ nio4r (~> 2.0)
14
+ websocket-driver (>= 0.6.1)
15
+ actionmailbox (6.0.0)
16
+ actionpack (= 6.0.0)
17
+ activejob (= 6.0.0)
18
+ activerecord (= 6.0.0)
19
+ activestorage (= 6.0.0)
20
+ activesupport (= 6.0.0)
21
+ mail (>= 2.7.1)
22
+ actionmailer (6.0.0)
23
+ actionpack (= 6.0.0)
24
+ actionview (= 6.0.0)
25
+ activejob (= 6.0.0)
26
+ mail (~> 2.5, >= 2.5.4)
27
+ rails-dom-testing (~> 2.0)
28
+ actionpack (6.0.0)
29
+ actionview (= 6.0.0)
30
+ activesupport (= 6.0.0)
31
+ rack (~> 2.0)
32
+ rack-test (>= 0.6.3)
33
+ rails-dom-testing (~> 2.0)
34
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
35
+ actiontext (6.0.0)
36
+ actionpack (= 6.0.0)
37
+ activerecord (= 6.0.0)
38
+ activestorage (= 6.0.0)
39
+ activesupport (= 6.0.0)
40
+ nokogiri (>= 1.8.5)
41
+ actionview (6.0.0)
42
+ activesupport (= 6.0.0)
43
+ builder (~> 3.1)
44
+ erubi (~> 1.4)
45
+ rails-dom-testing (~> 2.0)
46
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
47
+ activejob (6.0.0)
48
+ activesupport (= 6.0.0)
49
+ globalid (>= 0.3.6)
50
+ activemodel (6.0.0)
51
+ activesupport (= 6.0.0)
52
+ activerecord (6.0.0)
53
+ activemodel (= 6.0.0)
54
+ activesupport (= 6.0.0)
55
+ activestorage (6.0.0)
56
+ actionpack (= 6.0.0)
57
+ activejob (= 6.0.0)
58
+ activerecord (= 6.0.0)
59
+ marcel (~> 0.3.1)
60
+ activesupport (6.0.0)
61
+ concurrent-ruby (~> 1.0, >= 1.0.2)
62
+ i18n (>= 0.7, < 2)
63
+ minitest (~> 5.1)
64
+ tzinfo (~> 1.1)
65
+ zeitwerk (~> 2.1, >= 2.1.8)
66
+ builder (3.2.3)
67
+ byebug (11.0.1)
68
+ concurrent-ruby (1.1.5)
69
+ crass (1.0.5)
70
+ erubi (1.9.0)
71
+ globalid (0.4.2)
72
+ activesupport (>= 4.2.0)
73
+ i18n (1.7.0)
74
+ concurrent-ruby (~> 1.0)
75
+ loofah (2.3.0)
76
+ crass (~> 1.0.2)
77
+ nokogiri (>= 1.5.9)
78
+ mail (2.7.1)
79
+ mini_mime (>= 0.1.1)
80
+ marcel (0.3.3)
81
+ mimemagic (~> 0.3.2)
82
+ method_source (0.9.2)
83
+ mimemagic (0.3.3)
84
+ mini_mime (1.0.2)
85
+ mini_portile2 (2.4.0)
86
+ minitest (5.12.2)
87
+ nio4r (2.5.2)
88
+ nokogiri (1.10.4)
89
+ mini_portile2 (~> 2.4.0)
90
+ rack (2.0.7)
91
+ rack-test (1.1.0)
92
+ rack (>= 1.0, < 3)
93
+ rails (6.0.0)
94
+ actioncable (= 6.0.0)
95
+ actionmailbox (= 6.0.0)
96
+ actionmailer (= 6.0.0)
97
+ actionpack (= 6.0.0)
98
+ actiontext (= 6.0.0)
99
+ actionview (= 6.0.0)
100
+ activejob (= 6.0.0)
101
+ activemodel (= 6.0.0)
102
+ activerecord (= 6.0.0)
103
+ activestorage (= 6.0.0)
104
+ activesupport (= 6.0.0)
105
+ bundler (>= 1.3.0)
106
+ railties (= 6.0.0)
107
+ sprockets-rails (>= 2.0.0)
108
+ rails-dom-testing (2.0.3)
109
+ activesupport (>= 4.2.0)
110
+ nokogiri (>= 1.6)
111
+ rails-html-sanitizer (1.3.0)
112
+ loofah (~> 2.3)
113
+ railties (6.0.0)
114
+ actionpack (= 6.0.0)
115
+ activesupport (= 6.0.0)
116
+ method_source
117
+ rake (>= 0.8.7)
118
+ thor (>= 0.20.3, < 2.0)
119
+ rake (13.0.0)
120
+ redis (4.1.3)
121
+ sprockets (4.0.0)
122
+ concurrent-ruby (~> 1.0)
123
+ rack (> 1, < 3)
124
+ sprockets-rails (3.2.1)
125
+ actionpack (>= 4.0)
126
+ activesupport (>= 4.0)
127
+ sprockets (>= 3.0.0)
128
+ thor (0.20.3)
129
+ thread_safe (0.3.6)
130
+ tzinfo (1.2.5)
131
+ thread_safe (~> 0.1)
132
+ websocket-driver (0.7.1)
133
+ websocket-extensions (>= 0.1.0)
134
+ websocket-extensions (0.1.4)
135
+ zeitwerk (2.2.0)
136
+
137
+ PLATFORMS
138
+ ruby
139
+
140
+ DEPENDENCIES
141
+ bundler (~> 1.17)
142
+ byebug
143
+ full_request_logger!
144
+ rake
145
+
146
+ BUNDLED WITH
147
+ 1.17.2
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Easy access to full request logs via a web UI. The recorder attaches to the existing Rails.logger instance,
4
4
  and captures a copy of each log line into a per-thread buffer. When the request is over, the middleware makes
5
- the recorder flush all the log lines that were recorded for that request as a compressed batch to an expiring Redis key.
5
+ the recorder store all the log lines that were recorded for that request as a compressed batch to an auto-expiring Redis key.
6
6
 
7
7
  Thus you no longer have to grep through log files or wrestle with logging pipelines to instantly see all the
8
8
  log lines relevant to a request you just made. This is ideal for when you're testing a feature in the wild with
@@ -1,11 +1,16 @@
1
1
  module Rails
2
2
  class Conductor::FullRequestLogger::RequestLogsController < ActionController::Base
3
- if credentials = FullRequestLogger.credentials
4
- http_basic_authenticate_with credentials
5
- end
3
+ before_action :authenticate
6
4
 
7
5
  layout "rails/conductor"
8
6
 
7
+ def index
8
+ end
9
+
10
+ def create
11
+ redirect_to rails_conductor_request_log_url(params[:id])
12
+ end
13
+
9
14
  def show
10
15
  if @logs = FullRequestLogger::Recorder.instance.retrieve(params[:id])
11
16
  respond_to do |format|
@@ -16,5 +21,12 @@ module Rails
16
21
  head :not_found
17
22
  end
18
23
  end
24
+
25
+ private
26
+ def authenticate
27
+ if credentials = FullRequestLogger.credentials
28
+ http_basic_authenticate_or_request_with credentials
29
+ end
30
+ end
19
31
  end
20
32
  end
@@ -0,0 +1,7 @@
1
+ <h1>Lookup a request log</h1>
2
+
3
+ <%= form_with url: rails_conductor_request_logs_path do |form| %>
4
+ <p>Copy the X-Request-Id header and paste it in:</p>
5
+ <%= form.text_field :id, size: 40, placeholder: "0540689b-1ec6-4b92-ae8e-1c55bf7c3b79" %>
6
+ <%= form.submit "Retrieve" %>
7
+ <% end %>
@@ -1,5 +1,8 @@
1
1
  <h1>Request: <%= params[:id] %></h1>
2
2
 
3
- <p><%= link_to "Download", rails_conductor_request_log_path(params[:id], format: :text) %></p>
3
+ <p>
4
+ <%= link_to "Download this request log", rails_conductor_request_log_path(params[:id], format: :text) %> |
5
+ <%= link_to "Lookup another request", rails_conductor_request_logs_path %>
6
+ </p>
4
7
 
5
- <pre><%= @logs %></pre>
8
+ <pre style="background: black; color: white; padding: 10px"><%= @logs %></pre>
data/config/routes.rb CHANGED
@@ -2,6 +2,6 @@
2
2
 
3
3
  Rails.application.routes.draw do
4
4
  scope "rails/conductor/full_request_logger/", module: "rails/conductor/full_request_logger" do
5
- resources :request_logs, only: :show, as: :rails_conductor_request_logs
5
+ resources :request_logs, only: %i[ index create show ], as: :rails_conductor_request_logs
6
6
  end
7
7
  end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'full_request_logger'
3
- s.version = '0.1'
3
+ s.version = '0.2'
4
4
  s.authors = 'David Heinemeier Hansson'
5
5
  s.email = 'david@basecamp.com'
6
6
  s.summary = 'Make full request logs accessible via web UI'
@@ -9,7 +9,8 @@ Gem::Specification.new do |s|
9
9
 
10
10
  s.required_ruby_version = '>= 2.6.0'
11
11
 
12
- s.add_dependency 'activesupport', '>= 6.0.0'
12
+ s.add_dependency 'rails', '>= 6.0.0'
13
+ s.add_dependency 'redis', '>= 4.0'
13
14
 
14
15
  s.add_development_dependency 'bundler', '~> 1.17'
15
16
 
@@ -7,7 +7,7 @@ module FullRequestLogger
7
7
  def call(env)
8
8
  @app.call(env).tap do
9
9
  if FullRequestLogger.enabled
10
- Recorder.instance.flush ActionDispatch::Request.new(env).request_id
10
+ Recorder.instance.store ActionDispatch::Request.new(env).request_id
11
11
  end
12
12
  end
13
13
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "redis"
3
4
  require "zlib"
4
5
 
5
6
  class FullRequestLogger::Recorder
@@ -13,37 +14,47 @@ class FullRequestLogger::Recorder
13
14
  @redis = Redis.new FullRequestLogger.redis
14
15
  end
15
16
 
17
+ # Extends an existing logger instance with a broadcast aspect that'll send a copy of all logging lines to this recorder.
16
18
  def attach_to(logger)
17
- logger.extend ActiveSupport::Logger.broadcast(
18
- ActiveSupport::Logger.new(self)
19
- )
19
+ logger.extend ActiveSupport::Logger.broadcast(ActiveSupport::Logger.new(self))
20
20
  end
21
21
 
22
+ # Writes a log message to a buffer that'll be stored when the request is over.
22
23
  def write(message)
23
24
  messages << remove_ansi_colors(message)
24
25
  end
25
26
 
26
- def log
27
+ # Return a single string with all the log messages that have been buffered so far.
28
+ def combined_log
27
29
  messages.join.strip
28
30
  end
29
31
 
30
- def flush(request_id)
31
- if (log_to_be_flushed = log).present?
32
+ # Store all log messages as a single string to the full request logging storage accessible under the +request_id+.
33
+ def store(request_id)
34
+ if (log_to_be_stored = combined_log).present?
32
35
  redis.setex \
33
36
  request_key(request_id),
34
37
  FullRequestLogger.ttl,
35
- compress(log_to_be_flushed)
38
+ compress(log_to_be_stored)
36
39
  end
37
40
  ensure
38
41
  messages.clear
39
42
  end
40
43
 
44
+ # Returns a single string with all the log messages that were captured for the given +request_id+ (or nil if nothing was
45
+ # recorded or it has since expired).
41
46
  def retrieve(request_id)
42
47
  if log = redis.get(request_key(request_id))
43
48
  uncompress(log).force_encoding("utf-8")
44
49
  end
45
50
  end
46
51
 
52
+ # Clear out any messages pending in the buffer as well as all existing stored request logs.
53
+ def reset
54
+ messages.clear
55
+ clear_stored_requests
56
+ end
57
+
47
58
  # no-op needed for Logger to treat this as a valid log device
48
59
  def close
49
60
  redis.disconnect!
@@ -62,6 +73,12 @@ class FullRequestLogger::Recorder
62
73
  "full_request_logger/requests/#{id}"
63
74
  end
64
75
 
76
+ def clear_stored_requests
77
+ if (request_keys = redis.keys(request_key("*"))).any?
78
+ redis.del request_keys
79
+ end
80
+ end
81
+
65
82
  def compress(text)
66
83
  Zlib::Deflate.deflate(text)
67
84
  end
@@ -0,0 +1,18 @@
1
+ {
2
+ "presets": [
3
+ ["env", {
4
+ "modules": false,
5
+ "targets": {
6
+ "browsers": "> 1%",
7
+ "uglify": true
8
+ },
9
+ "useBuiltIns": true
10
+ }]
11
+ ],
12
+
13
+ "plugins": [
14
+ "syntax-dynamic-import",
15
+ "transform-object-rest-spread",
16
+ ["transform-class-properties", { "spec": true }]
17
+ ]
18
+ }
@@ -0,0 +1,3 @@
1
+ *.log
2
+ *.sqlite3
3
+ tmp/*
@@ -0,0 +1,3 @@
1
+ plugins:
2
+ postcss-import: {}
3
+ postcss-cssnext: {}
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require_relative 'config/application'
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,3 @@
1
+ //= link_tree ../images
2
+ //= link_directory ../javascripts .js
3
+ //= link_directory ../stylesheets .css
File without changes
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,80 @@
1
+ body {
2
+ background-color: #fff;
3
+ color: #333;
4
+ margin: 33px;
5
+ }
6
+
7
+ body, p, ol, ul, td {
8
+ font-family: verdana, arial, helvetica, sans-serif;
9
+ font-size: 13px;
10
+ line-height: 18px;
11
+ }
12
+
13
+ pre {
14
+ background-color: #eee;
15
+ padding: 10px;
16
+ font-size: 11px;
17
+ }
18
+
19
+ a {
20
+ color: #000;
21
+ }
22
+
23
+ a:visited {
24
+ color: #666;
25
+ }
26
+
27
+ a:hover {
28
+ color: #fff;
29
+ background-color: #000;
30
+ }
31
+
32
+ th {
33
+ padding-bottom: 5px;
34
+ }
35
+
36
+ td {
37
+ padding: 0 5px 7px;
38
+ }
39
+
40
+ div.field,
41
+ div.actions {
42
+ margin-bottom: 10px;
43
+ }
44
+
45
+ #notice {
46
+ color: green;
47
+ }
48
+
49
+ .field_with_errors {
50
+ padding: 2px;
51
+ background-color: red;
52
+ display: table;
53
+ }
54
+
55
+ #error_explanation {
56
+ width: 450px;
57
+ border: 2px solid red;
58
+ padding: 7px 7px 0;
59
+ margin-bottom: 20px;
60
+ background-color: #f0f0f0;
61
+ }
62
+
63
+ #error_explanation h2 {
64
+ text-align: left;
65
+ font-weight: bold;
66
+ padding: 5px 5px 5px 15px;
67
+ font-size: 12px;
68
+ margin: -7px -7px 0;
69
+ background-color: #c00;
70
+ color: #fff;
71
+ }
72
+
73
+ #error_explanation ul li {
74
+ font-size: 12px;
75
+ list-style: square;
76
+ }
77
+
78
+ label {
79
+ display: block;
80
+ }