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.
- checksums.yaml +4 -4
- data/Gemfile.lock +147 -0
- data/README.md +1 -1
- data/app/controllers/rails/conductor/full_request_logger/request_logs_controller.rb +15 -3
- data/app/views/rails/conductor/full_request_logger/request_logs/index.html.erb +7 -0
- data/app/views/rails/conductor/full_request_logger/request_logs/show.html.erb +5 -2
- data/config/routes.rb +1 -1
- data/full_request_logger.gemspec +3 -2
- data/lib/full_request_logger/middleware.rb +1 -1
- data/lib/full_request_logger/recorder.rb +24 -7
- data/test/dummy/.babelrc +18 -0
- data/test/dummy/.gitignore +3 -0
- data/test/dummy/.postcssrc.yml +3 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/config/manifest.js +3 -0
- data/test/dummy/app/assets/images/.keep +0 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/assets/stylesheets/scaffold.css +80 -0
- data/test/dummy/app/channels/application_cable/channel.rb +4 -0
- data/test/dummy/app/channels/application_cable/connection.rb +4 -0
- data/test/dummy/app/controllers/application_controller.rb +2 -0
- data/test/dummy/app/controllers/concerns/.keep +0 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/javascript/packs/application.js +0 -0
- data/test/dummy/app/jobs/application_job.rb +2 -0
- data/test/dummy/app/mailboxes/application_mailbox.rb +2 -0
- data/test/dummy/app/mailboxes/messages_mailbox.rb +4 -0
- data/test/dummy/app/mailers/application_mailer.rb +4 -0
- data/test/dummy/app/models/application_record.rb +3 -0
- data/test/dummy/app/models/concerns/.keep +0 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +36 -0
- data/test/dummy/bin/update +31 -0
- data/test/dummy/bin/yarn +11 -0
- data/test/dummy/config.ru +5 -0
- data/test/dummy/config/application.rb +19 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/cable.yml +10 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +34 -0
- data/test/dummy/config/environments/production.rb +96 -0
- data/test/dummy/config/environments/test.rb +36 -0
- data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
- data/test/dummy/config/initializers/assets.rb +14 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/content_security_policy.rb +22 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +33 -0
- data/test/dummy/config/puma.rb +34 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/config/spring.rb +6 -0
- data/test/dummy/config/webpack/development.js +3 -0
- data/test/dummy/config/webpack/environment.js +3 -0
- data/test/dummy/config/webpack/production.js +3 -0
- data/test/dummy/config/webpack/test.js +3 -0
- data/test/dummy/config/webpacker.yml +65 -0
- data/test/dummy/lib/assets/.keep +0 -0
- data/test/dummy/log/.keep +0 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/test/dummy/public/apple-touch-icon.png +0 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/recorder_test.rb +28 -3
- metadata +145 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69103e9dda0de7ad14689c9479265fa4148620f406ba2cecc10e7350db55c183
|
4
|
+
data.tar.gz: ad9049fdc9cc3c55e462dee1205af4df4239522f1a2f9902c3d0457e4e2b4de3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
-
|
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
|
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:
|
5
|
+
resources :request_logs, only: %i[ index create show ], as: :rails_conductor_request_logs
|
6
6
|
end
|
7
7
|
end
|
data/full_request_logger.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'full_request_logger'
|
3
|
-
s.version = '0.
|
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 '
|
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
|
|
@@ -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
|
-
|
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
|
-
|
31
|
-
|
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(
|
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
|
data/test/dummy/.babelrc
ADDED
@@ -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
|
+
}
|
data/test/dummy/Rakefile
ADDED
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
|
+
}
|