Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

Rails의 멋진 예외 페이지 작동 방식

레일 작업의 좋은 점 중 하나는 개발 중에 문제가 발생하면 정말 멋진 오류 세부 정보 페이지가 표시된다는 것입니다. 앱과 관련된 부분이 강조 표시된 멋진 역추적을 얻을 수 있습니다. 게시된 매개변수를 확인하고 환경 및 세션 변수를 검사할 수 있습니다.

오늘은 이러한 멋진 오류 페이지가 어떻게 작동하는지 살펴보겠습니다.

Rails의 멋진 예외 페이지 작동 방식

액션팩 열기

오늘 우리가 주로 다룰 파일은 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