레일 작업의 좋은 점 중 하나는 개발 중에 문제가 발생하면 정말 멋진 오류 세부 정보 페이지가 표시된다는 것입니다. 앱과 관련된 부분이 강조 표시된 멋진 역추적을 얻을 수 있습니다. 게시된 매개변수를 확인하고 환경 및 세션 변수를 검사할 수 있습니다.
오늘은 이러한 멋진 오류 페이지가 어떻게 작동하는지 살펴보겠습니다.
액션팩 열기
오늘 우리가 주로 다룰 파일은 actionpack/lib/action_dispatch/middleware/debug_exceptions.rb입니다. 개발 모드 오류 페이지를 표시할 때 대부분의 무거운 작업을 수행합니다. 프로덕션 모드 오류 화면의 출처가 궁금하시다면 public_exceptions.rb를 확인하세요.
랙 미들웨어
랙 미들웨어에 익숙하지 않다면 개념은 간단합니다. 이를 통해 HTTP 요청이 앱에 도달하기 전에 가로채고 사용자에게 다시 전달되기 전에 앱의 출력을 가로챌 수 있습니다.
다음은 흥미로운 작업을 수행하지 않는 간단한 미들웨어입니다.
class MyMiddleware
def initialize(app)
@app = app
end
def call(env)
@app.call(env)
end
end
랙 미들웨어를 통한 모든 예외 구조
앱에서 발생하는 모든 예외는 @app.call()을 호출한 결과로 발생합니다. 따라서 랙 앱에서 모든 예외를 복구하는 것은 미들웨어에 복구 조항을 추가하는 것만큼 간단합니다.
def call(env)
@app.call(env)
rescue StandardError => exception
# this is a method we have to provide to generate the exception page
render_exception(env, exception)
end
호출 메서드에서 반환된 모든 것은 일반 웹 페이지인 것처럼 처리됩니다. 따라서 render_exception에서 반환된 콘텐츠가 원래 응답을 대체합니다.
예외 렌더링
ActionDispatch::DebugExceptions에서 render_exception 메서드를 발췌했습니다. 보시다시피 예외에서 관련 데이터를 가져와 ERB 템플릿에 입력하기만 하면 됩니다.
def render_exception(env, exception)
wrapper = ExceptionWrapper.new(env, exception)
log_error(env, wrapper)
if env['action_dispatch.show_detailed_exceptions']
request = Request.new(env)
template = ActionView::Base.new([RESCUES_TEMPLATE_PATH],
request: request,
exception: wrapper.exception,
application_trace: wrapper.application_trace,
framework_trace: wrapper.framework_trace,
full_trace: wrapper.full_trace,
routes_inspector: routes_inspector(exception),
source_extract: wrapper.source_extract,
line_number: wrapper.line_number,
file: wrapper.file
)
file = "rescues/#{wrapper.rescue_template}"
if request.xhr?
body = template.render(template: file, layout: false, formats: [:text])
format = "text/plain"
else
body = template.render(template: file, layout: 'rescues/layout')
format = "text/html"
end
render(wrapper.status_code, body, format)
else
raise exception
end
end
def render(status, body, format)
[status, {'Content-Type' => "#{format}; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
end
기타 용도
이 랙 미들웨어 트릭을 사용하여 예외가 있는 많은 흥미로운 작업을 수행할 수 있습니다. Honeybadge에서는 이를 사용하여 오류를 가로채서 API에 기록합니다. 다음은 이를 수행하는 데 사용하는 코드입니다.
def call(env)
config.with_request(::Rack::Request.new(env)) do
begin
env['honeybadger.config'] = config
response = @app.call(env)
rescue Exception => raised
env['honeybadger.error_id'] = notify_honeybadger(raised, env)
raise
end
framework_exception = framework_exception(env)
if framework_exception
env['honeybadger.error_id'] = notify_honeybadger(framework_exception, env)
end
response
end
ensure
Honeybadger.context.clear!
end