edge
Mais em rubyonrails.org: Mais Ruby on Rails

Relatório de Erros em Aplicações Rails

Este guia apresenta maneiras de gerenciar exceções que ocorrem em aplicações Ruby on Rails.

Após ler este guia, você saberá:

1 Relatório de Erros

O relatório de erros do Rails error reporter fornece uma maneira padrão de coletar exceções que ocorrem em sua aplicação e relatá-las para o serviço ou localização de sua preferência.

O relatório de erros tem como objetivo substituir o código de tratamento de erros repetitivo, como este:

begin
  faça_algo
rescue AlgoEstáQuebrado => erro
  MeuServiçoDeRelatórioDeErros.notificar(erro)
end

por uma interface consistente:

Rails.error.handle(AlgoEstáQuebrado) do
  faça_algo
end

O Rails envolve todas as execuções (como requisições HTTP, jobs e invocações rails runner) no relatório de erros, então quaisquer erros não tratados levantados em sua aplicação serão automaticamente relatados para o seu serviço de relatório de erros por meio de seus assinantes.

Isso significa que bibliotecas de relatório de erros de terceiros não precisam mais inserir um middleware Rack ou fazer qualquer monkey-patching para capturar exceções não tratadas. Bibliotecas que usam o ActiveSupport também podem usar isso para relatar avisos de forma não intrusiva que anteriormente seriam perdidos nos logs.

O uso do relatório de erros do Rails não é obrigatório. Todos os outros meios de capturar erros ainda funcionam.

1.1 Assinando o Relatório

Para usar o relatório de erros, você precisa de um assinante. Um assinante é qualquer objeto com um método report. Quando ocorre um erro em sua aplicação ou é relatado manualmente, o relatório de erros do Rails chamará esse método com o objeto de erro e algumas opções.

Algumas bibliotecas de relatório de erros, como Sentry e Honeybadger, registram automaticamente um assinante para você. Consulte a documentação do seu provedor para obter mais detalhes.

Você também pode criar um assinante personalizado. Por exemplo:

# config/initializers/error_subscriber.rb
class ErrorSubscriber
  def report(error, handled:, severity:, context:, source: nil)
    MyErrorReportingService.report_error(error, context: context, handled: handled, level: severity)
  end
end

Após definir a classe do assinante, registre-a chamando o método Rails.error.subscribe:

Rails.error.subscribe(ErrorSubscriber.new)

Você pode registrar quantos assinantes desejar. O Rails os chamará em sequência, na ordem em que foram registrados.

NOTA: O relatório de erros do Rails sempre chamará os assinantes registrados, independentemente do seu ambiente. No entanto, muitos serviços de relatório de erros relatam apenas erros em produção por padrão. Você deve configurar e testar sua configuração em todos os ambientes conforme necessário.

1.2 Usando o Relatório de Erros

Existem três maneiras de usar o relatório de erros:

1.2.1 Relatando e Ignorando Erros

Rails.error.handle irá relatar qualquer erro levantado dentro do bloco. Em seguida, ele irá ignorar o erro, e o restante do seu código fora do bloco continuará normalmente.

resultado = Rails.error.handle do
  1 + '1' # levanta TypeError
end
resultado # => nil
1 + 1 # Isso será executado

Se nenhum erro for levantado no bloco, Rails.error.handle retornará o resultado do bloco, caso contrário, retornará nil. Você pode substituir isso fornecendo um fallback:

usuário = Rails.error.handle(fallback: -> { User.anonymous }) do
  User.find_by(params[:id])
end

1.2.2 Relatando e Levantando Novamente Erros

Rails.error.record irá relatar erros para todos os assinantes registrados e, em seguida, levantará novamente o erro, o que significa que o restante do seu código não será executado.

Rails.error.record do
  1 + '1' # levanta TypeError
end
1 + 1 # Isso não será executado

Se nenhum erro for levantado no bloco, Rails.error.record retornará o resultado do bloco.

1.2.3 Relatando Erros Manualmente

Você também pode relatar erros manualmente chamando Rails.error.report:

begin
  # código
rescue StandardError => e
  Rails.error.report(e)
end

Quaisquer opções que você passar serão repassadas aos assinantes de erros.

1.3 Opções de Relatório de Erros

As 3 APIs de relatório (#handle, #record e #report) suportam as seguintes opções, que são então repassadas a todos os assinantes registrados:

  • handled: um Boolean para indicar se o erro foi tratado. Isso é definido como true por padrão. #record define isso como false.
  • severity: um Symbol que descreve a gravidade do erro. Os valores esperados são: :error, :warning e :info. #handle define isso como :warning, enquanto #record define como :error.
  • context: um Hash para fornecer mais contexto sobre o erro, como detalhes da requisição ou do usuário.
  • source: uma String sobre a origem do erro. A origem padrão é "application". Erros relatados por bibliotecas internas podem definir outras origens; a biblioteca de cache Redis pode usar "redis_cache_store.active_support", por exemplo. Seu assinante pode usar a origem para ignorar erros que você não está interessado. ruby Rails.error.handle(context: { user_id: user.id }, severity: :info) do # ... end

1.4 Filtrando por Classes de Erro

Com Rails.error.handle e Rails.error.record, você também pode escolher reportar apenas erros de certas classes. Por exemplo:

Rails.error.handle(IOError) do
  1 + '1' # gera TypeError
end
1 + 1 # TypeErrors não são IOErrors, então isso *não* será executado

Aqui, o TypeError não será capturado pelo relator de erros do Rails. Apenas instâncias de IOError e suas subclasses serão reportadas. Quaisquer outros erros serão lançados normalmente.

1.5 Definindo Contexto Globalmente

Além de definir o contexto através da opção context, você pode usar a API #set_context. Por exemplo:

Rails.error.set_context(section: "checkout", user_id: @user.id)

Qualquer contexto definido dessa maneira será mesclado com a opção context.

Rails.error.set_context(a: 1)
Rails.error.handle(context: { b: 2 }) { raise }
# O contexto reportado será: {:a=>1, :b=>2}
Rails.error.handle(context: { b: 3 }) { raise }
# O contexto reportado será: {:a=>1, :b=>3}

1.6 Para Bibliotecas

Bibliotecas de relatório de erros podem registrar seus assinantes em um Railtie:

module MySdk
  class Railtie < ::Rails::Railtie
    initializer "my_sdk.error_subscribe" do
      Rails.error.subscribe(MyErrorSubscriber.new)
    end
  end
end

Se você registrar um assinante de erros, mas ainda tiver outros mecanismos de erro como um middleware Rack, você pode acabar com erros reportados várias vezes. Você deve remover seus outros mecanismos ou ajustar sua funcionalidade de relatório para pular a reportagem de uma exceção que já foi vista antes.

Feedback

Você é incentivado a ajudar a melhorar a qualidade deste guia.

Por favor, contribua se encontrar algum erro de digitação ou factual. Para começar, você pode ler nossa contribuição à documentação seção.

Você também pode encontrar conteúdo incompleto ou desatualizado. Por favor, adicione qualquer documentação ausente para o principal. Certifique-se de verificar Guias Edge primeiro para verificar se os problemas já foram corrigidos ou não no branch principal. Verifique as Diretrizes dos Guias do Ruby on Rails para estilo e convenções.

Se por algum motivo você encontrar algo para corrigir, mas não puder corrigi-lo você mesmo, por favor abra uma issue.

E por último, mas não menos importante, qualquer tipo de discussão sobre a documentação do Ruby on Rails é muito bem-vinda no Fórum oficial do Ruby on Rails.