Computer >> 컴퓨터 >  >> 프로그래밍 >> Ruby

Honeybadger 3.2 신기능: 예외 클래스에 컨텍스트 추가하기

최근 honeybadger 루비 젬(Ruby Gem) 버전 3.2를 출시했습니다. 이번 릴리스에는 에러 리포트에 컨텍스트를 더 쉽게 추가할 수 있는 새로운 기능이 포함되어 있습니다.

핵심 요약

honeybadger 젬은 이제 모든 예외 클래스에서 #to_honeybadger_context 메서드를 정의할 수 있습니다. 해당 예외의 인스턴스가 발생해 Honeybadger에 보고되면, 이 메서드가 반환하는 컨텍스트가 에러 리포트에 자동으로 포함됩니다.

class MyError < StandardError
  attr_reader :custom_attribute

  def initialize(err, custom_attribute)
    @custom_attribute = custom_attribute
    super(err)
  end

  def to_honeybadger_context
    {
      custom_attribute: custom_attribute
    }
  end
end

raise MyError.new("Something went wrong", { foo: 'bar' })
# Honeybadger 컨텍스트에는 다음이 포함됩니다:
# {
#   custom_attribute: {
#     foo: 'bar'
#   }
# }

컨텍스트란 무엇인가?

컨텍스트(Context)는 애플리케이션에서 오류가 발생했을 때 Honeybadger에 추가 데이터를 함께 전송할 수 있는 기능입니다. Rails에서는 Ruby 젬이 제공하는 Honeybadger.context 메서드를 사용해 현재 요청에 대한 컨텍스트를 설정할 수 있습니다.

Honeybadger.context({
  user_email: 'user@example.com'
})

이렇게 설정하면 현재 요청(백그라운드 워커를 실행 중이라면 해당 잡)에서 발생하는 모든 오류가 고유한 컨텍스트 데이터를 갖게 됩니다.

컨텍스트 데이터의 형식은 자유롭지만, 일반적으로 로그인한 사용자의 ID나 이메일 주소, 디버깅에 도움이 되는 원시 POST 데이터나 관련 페이로드, 백그라운드 잡의 ID 등이 포함됩니다.

오류를 수동으로 보고할 때도 로컬 컨텍스트를 추가할 수 있습니다.

Honeybadger.notify(exception, context: {
  user_email: 'user@example.com'
})

재미있는 사실 하나: 컨텍스트에는 어떤 값이든 담을 수 있지만, Honeybadger에는 몇 가지 "특별한" 컨텍스트 키가 존재합니다. 예를 들어 에러 리포트에 user_email 키를 포함하면, Honeybadger가 오류별로 영향을 받은 사용자 목록을 자동으로 생성해 줍니다.

예외에서 컨텍스트 추가하기

어떤 컨텍스트는 요청 단위가 아니라 예외 자체에 종속적입니다. 예를 들어 faraday 젬으로 HTTP 요청을 보내는 상황을 생각해 봅시다.

require 'faraday'

conn = Faraday.new(:url => 'https://example.com') do |faraday|
  faraday.response :raise_error # 요청이 성공하지 않으면 오류 발생
  faraday.adapter  Faraday.default_adapter
end

response = conn.get('/does-not-exist') # => Faraday::ResourceNotFound

위 코드는 다음과 같은 예외를 발생시킵니다.

Faraday::ResourceNotFound: the server responded with status 404

Honeybadger는 이 오류를 자동으로 보고하지만(설정되어 있다면), response 객체에 대한 정보는 리포트에 담기지 않습니다. 특히 500 응답처럼 원인이 명확하지 않은 서버 오류의 경우 이 정보가 있다면 디버깅에 큰 도움이 됩니다.

GitHub에서 Faraday::ResourceNotFound의 정의를 살펴보면, 이 클래스가 사실 ClientError의 한 종류라는 것을 알 수 있습니다. 그리고 ClientError는 각 인스턴스에 response 객체를 저장하는 속성을 정의하고 있습니다.

이 정보를 활용하면 Faraday::ClientError 인스턴스를 직접 rescue한 뒤, Honeybadger.notify를 호출해 응답 데이터를 컨텍스트에 추가할 수 있습니다.

begin
  response = conn.get('/does-not-exist')
rescue Faraday::ClientError => err
  Honeybadger.notify(err, context: {
    response_status:  err.response.status,
    response_headers: err.response.headers
  })
  # 추가적인 오류 처리...
end

이렇게 하면 실패한 요청을 응답 관련 추가 정보와 함께 Honeybadger에 보고할 수 있습니다.

저희도 오류 발생 시 예외별 컨텍스트를 붙이기 위해 이 패턴을 사용해 왔습니다. 물론 잘 동작하지만, rescue 문과 커스텀 알림 로직이 코드 곳곳에 들어나면서 코드가 지저분해지고 유지보수 부담도 커집니다. 다행히 더 나은 방법이 있습니다!

새 기능: 예외 수준의 컨텍스트

이제는 오류를 수동으로 보고할 필요 없이, 컨텍스트를 예외 클래스 자체에 정의할 수 있습니다. 오류가 최종적으로 어디에서 보고되든 Honeybadger가 자동으로 이를 감지해 리포트에 반영합니다.

앞선 예제로 돌아가 보겠습니다. 번거로운 rescue 문을 추가하는 대신, Honeybadger의 내장 보고 기능이 예외를 처리하도록 그대로 두겠습니다.

response = conn.get('/does-not-exist') # => Faraday::ResourceNotFound

그리고 Faraday::ClientError#to_honeybadger_context 메서드를 추가합니다. Honeybadger는 예외가 보고될 때마다 이 특수 메서드의 존재 여부를 확인합니다.

Faraday::ClientError.class_eval do
  def to_honeybadger_context
    {
      response_status:  response.status,
      response_headers: response.headers
    }
  end
end

Faraday::ClientError#to_honeybadger_context 메서드만 추가해 두면, 해당 오류가 발생할 때마다 응답 컨텍스트가 자동으로 수집됩니다. 애플리케이션 코드를 어지럽힐 필요가 전혀 없습니다!