Browse files

Dynamic Error Pages by Default

Dynamic error pages have many benefits over static, for instance they can use the default layout if desired. By generating dynamic error pages by default we can encourage developers to take advantage of this already existing feature and build better sites. In addition to moving people towards dynamically generating their error pages we can add prompts to the default pages. One example is telling devs how to get to their logs. I have added erb to `errors/500.html.erb` that I believe will be extremely helpful to new developers.

While walking users through a deployment (such as with they often are not familiar with how to retrieve logs on a given system. I've added a dynamic section that will look for an environment variable `CHECK_LOG_INSTRUCTIONS` and if present show a corresponding message. I can then work with @hone to get a debug log message into the [ruby buildpack]( something like `$ heroku logs --tail`. While we've added a number of great error messages to Rails, helping developers find those error messages is crucial to their sustained success. This isn't just for new devs either. Experienced Rails developers who are new to a system might not know how to access its logs.

By relying on an ENV var we're not coupling the message to any one provider, and if no message is present we fall back on the default error page. It would be the responsibility of providers to set this message on a given system.

Moving in this direction we should also port over `public/index.html` to a dynamic page. This change can be found in a separate PR with it's own set of benefits #7771

Screenshot when running production with `CHECK_LOG_INSTRUCTIONS` set:

  • Loading branch information...
1 parent bfcbaa5 commit 5ff1e64659b01c4ac083e69a8a38508e73d22b8a @schneems committed Sep 24, 2012
@@ -1,5 +1,11 @@
## Rails 4.0.0 (unreleased) ##
+* Replace static generated error pages form `public/` to dynamic error pages in `app/views/pages/errors`. This allows for easier modification and enhancement by developers.
+ The dynamic 500 error page can optionally display instructions on how to check logs by reading an environment variable such as `CHECK_LOG_INSTRUCTIONS='$ tail -f log/production.log'` set by the system.
+ *Richard Schneeman*
* Change `rails new` and `rails plugin new` generators to name the `.gitkeep` files
as `.keep` in a more SCM-agnostic way.
@@ -0,0 +1,15 @@
+class Pages::ErrorsController < ApplicationController
+ layout false
+ def not_found
+ render "pages/errors/404", :status => 404
+ end
+ def rejected_change
+ render "pages/errors/422", :status => 422
+ end
+ def error
+ render "pages/errors/500", :status => 500
+ end
@@ -17,7 +17,7 @@
- <!-- This file lives in public/404.html -->
+ <!-- This file lives in pages/errors/404.html -->
<div class="dialog">
<h1>The page you were looking for doesn't exist.</h1>
<p>You may have mistyped the address or the page may have moved.</p>
@@ -17,7 +17,7 @@
- <!-- This file lives in public/422.html -->
+ <!-- This file lives in pages/errors/422.html -->
<div class="dialog">
<h1>The change you wanted was rejected.</h1>
<p>Maybe you tried to change something you didn't have access to.</p>
@@ -17,10 +17,14 @@
- <!-- This file lives in public/500.html -->
+ <!-- This file lives in pages/errors/500.html -->
<div class="dialog">
<h1>We're sorry, but something went wrong.</h1>
<p>If you are the application owner check the logs for more information.</p>
+ <% if ENV["CHECK_LOG_INSTRUCTIONS"].present? %>
+ <pre><%= ENV["CHECK_LOG_INSTRUCTIONS"] %></pre>
+ <% end %>
@@ -31,6 +31,11 @@ class Application < Rails::Application
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'
+ # Allow rails to dynamically handle errors such as 404 and 500 using routes in config/routes.rb
+ # to use static error pages in `public/` remove line or set to:
+ # config.exceptions_app = ``
+ config.exceptions_app = self.routes
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# config.i18n.default_locale = :de
@@ -52,4 +52,7 @@
# See how all your routes lay out with "rake routes".
+ get '/404' => 'Pages::Errors#not_found'
+ get '/422' => 'Pages::Errors#rejected_change'
+ get '/500' => 'Pages::Errors#error'
@@ -53,7 +53,7 @@ def test_assets
assert_file "app/views/layouts/application.html.erb", /javascript_include_tag\s+"application"/
assert_file "app/assets/stylesheets/application.css"
assert_file "config/application.rb", /config\.assets\.enabled = true/
- assert_file "public/index.html", /url\("assets\/rails.png"\);/
+ assert_file "app/views/pages/errors/500.html.erb", /We're sorry, but something went wrong/
def test_invalid_application_name_raises_an_error
@@ -249,6 +249,11 @@ def test_inclusion_of_javascript_runtime
+ def test_generator_log_env_set
+ run_generator [destination_root]
+ assert_file "app/views/pages/errors/500.html.erb", /ENV\["CHECK_LOG_INSTRUCTIONS"\]/
+ end
def test_generator_if_skip_index_html_is_given
run_generator [destination_root, '--skip-index-html']
assert_no_file 'public/index.html'

0 comments on commit 5ff1e64

Please sign in to comment.