Mostrando postagens com marcador Windows Forms. Mostrar todas as postagens
Mostrando postagens com marcador Windows Forms. Mostrar todas as postagens

domingo, 11 de outubro de 2009

Tratador de Erros Global para ASP.NET e Windows Forms

Você não deve deixar *nenhuma* parte da sua aplicação sem tratamento de erro. Mas isto não significa que você terá que espalhar blocos try/catch em todo o seu código. Tanto aplicações Windows Forms quanto aplicações ASP.NET oferecem a possibilidade de criação de um tratador de erro global, que será ativado sempre que uma exceção não tratada (i.e., uma exceção que ocorreu fora de um bloco try/catch) acontecer.

Windows Forms

Como sempre, as coisas são mais fáceis em aplicações Windows Forms do que em ASP.NET. Windows Forms provê para o desenvolvedor o objeto Application que, dentre outas coisas, tem o evento ThreadException. Basicamente, devemos adicionar um tratador de eventos a este evento, e o tradador de erros global da nossa aplicação estará pronto.

Crie um novo projeto VB.NET Windows Forms e adicione um botão no formulário Form1. Depois, substitua seu código pelo seguinte:


Imports System.Diagnostics

Public Class Form1

Const NOME_FONTE_EVENTOS As String = "Minha Aplicação"

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' Cria a fonte de eventos no Log de Aplicação do Windows
' Isto só precisa ser feito uma vez (p.e. no setup da aplicação)
If Not EventLog.SourceExists(NOME_FONTE_EVENTOS) Then
EventLog.CreateEventSource(NOME_FONTE_EVENTOS, "Application")
End If
' Define o tratador de errors global
AddHandler Application.ThreadException, AddressOf Me.TratadorErros
End Sub

Sub TratadorErros(ByVal sender As Object, ByVal e As Threading.ThreadExceptionEventArgs)
' Acesso à exceção ocorrida
Dim erro As Exception = e.Exception
' Escreve dados da exceção no Log de Aplicação
EventLog.WriteEntry(NOME_FONTE_EVENTOS, _
"Erro não tratado: " + vbNewLine + erro.ToString(), _
EventLogEntryType.Error)
' Mostra mensagem de erro não-técnica ao usuário
MsgBox("Um erro inesperado ocorreu. Por favor entre em contato com o administrador do sistema.", _
MsgBoxStyle.Critical)
End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
' Simula uma exceção não tratada
Throw New Exception("Oh oh Erro acessando o banco de dados")
End Sub
End Class

Clique no botão para simular uma exceção não tradada. Uma mensagem será mostrada e se você olhar o Log de Aplicação do Windows no Visualizador de Eventos (Iniciar > Executar > ebentvwr.msc), lá estará a exceção.

ASP.NET

ASP.NET disponibiliza o evento de aplição Application_Error, que é disparado sempre que uma exceção não tratada ocorre. Este tratador de evento pertence à classe de aplicação global (global application class), como o Visual Studio a chama. O código da classe de aplicação global fica no arquivo Global.asax. As aplicações ASP.NET, por default, não tem um arquivo Global.asax. Você pode adicionar um indo no menu Project e selecionando Add New Item > Global Application Class.

No tratador de evento Application_Error, você terá que usar dois métodos especiais:
  • Server.GetLastError(), que dá acesso à exceção ocorrida.
  • Server.ClearError(), que evita que o ASP.NET mostre a página com a mensagem de erro default.
Crie um novo projeto ASP.NET Web Application, adicione um arquivo Global.asax, e substitua o seu código com o seguinte:


Imports System.Web.SessionState
Imports System.Diagnostics

Public Class Global_asax
Inherits System.Web.HttpApplication

Const NOME_FONTE_EVENTOS As String = "Minha Aplicação"

Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
' Cria a fonte de eventos no Log de Aplicação do Windows
' Isto só precisa ser feito uma vez (p.e. no setup da aplicação)
If Not EventLog.SourceExists(NOME_FONTE_EVENTOS) Then
EventLog.CreateEventSource(NOME_FONTE_EVENTOS, "Application")
End If
End Sub

Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
' Acessa a exceção ocorrida
Dim erro As Exception = Server.GetLastError()
' Instrui o Framework a não mostrar a mensagem de erro default
Server.ClearError()
' Escreve dados da exceção no Log de Aplicação
EventLog.WriteEntry(NOME_FONTE_EVENTOS, _
"Erro não tratado: " + vbNewLine + erro.ToString(), _
EventLogEntryType.Error)
' Redireciona usuário para página com mensagem de erro não-técnica
Response.Redirect("/erro.aspx")
End Sub

End Class

Crie uma página chamada "erro.aspx"; esta é a página para a qual os usuário serão redirecionados quando da ocorrência de uma exceção não tratada.

Por último, adicione um botão à página Default.aspx, e substitua seu código com o seguinte:


Partial Public Class _Default
Inherits System.Web.UI.Page

Protected Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
' Simula uma exceção não tratada
Throw New Exception("Oh oh Aquele erro de acesso ao banco de novo")
End Sub
End Class

Clique no botão e você será redirecionado para a página erro.aspx, e um evento será logado no Log de Aplicação com a informação da exceção.



UPDATE: O código no evento Application_Start vai falhar no Windows Vista e Windows 7, devido à segurança mais restrita destes sistemas operacionais. O código tenta criar uma fonte de eventos no Log de Aplicação para logar os erros nesta fonte. Os métodos EventLog.SourceExists() e EventLog.Create() tentam varrer os logs do Windows para verificar se a fonte especificada já existe. Isto causa o erro, pois por default não é permitido o acesso ao Log de Segurançapor aplicações ASP.NET. Há várias maneiras de se solucionar isto, mas uma solução simples e rápida é criar a fonte "na mão": crie no Registry a seguinte chave: HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\eventlog\Application\NOME_APLICACAO, trocando NOME_APLICACAO pelo nome da fonte de eventos que você quer criar. Um caminho "mais correto" seria p.e. criar uma aplicação console que executasse o código do evento Application_Start, e empacotar esta aplicação em no pacote de setup do seu site, para ser executada ao final do setup.