Categorias

ASP.NET – Geração e tratamento de log de erros

Este código demonsstra como você pode gerar um arquivo de log de erros em uma aplicação ASP.NET. Vai ser um abordagem bem simples onde os erros serão gravados em um arquivo texto chamado logErro.txt.

Para que serve um arquivo de log ? Serve para você auditar o seu sistema em produção e acompanhar o seu comportamento monitorando as ocorrências que porventura estejam afetando o desempenho ou causando um problema.

Irei criar um novo web site usando o Visual Web Developer Express onde a página Default.aspx irá conter um controle dropdownlist e um controle GridView .

O controle DropDownlist será preenchido com os dados da tabela Categories do banco de dados Northwind.mdf  e o GridView irá exibir os dados da tabela Products  e Categories relacionados com a categoria selecionada no Dropdownlist.

Para criar esta aplicação podemos usar os assistentes de configuração sem ter que digitar nenhuma linha de código mas não vou usar os assistentes e vou fazer tudo via programação. Além disso vou criar um web site AJAX usando o componente UpdatePanel para evitar o postback da página inteira.

Inicie o Visual Web Developer Express e crie um novo web site chamado cmdFiltroNet a partir do menu File -> New Web Site selecionando na janela New Web Site o template ASP.NET AJAX - Enabled Web Site. (Você deve instalar o pacote a última versão do Microsoft ASP.NET AJAX instalada e configurada.( https://ajax.asp.net/)

Selecionando a página Default.aspx , no modo Design, você deverá ver o controle ScriptManager exibido na página.

Inclua agora , a partir da guia AJAX Extensions da ToolBox, o controle UpdatePanel na página;

A seguir inclua , no interior do componente UpdatePanel, um controle DropdownList e um controle GridView , e, fora do UpdatePanel um controle Label;

Como não vou usar nenhum assistente para configurar a conexão com o banco de dados nem preencher os controles ou selecionar os dados para exibição, e assim, teremos que definir primeiro a string de conexão com o banco de dados Northwind.mdf no arquivo web.config.

Você pode usar o assistente de configuração e definir uma fonte de dados para o controle Dropdownlist salvando a string de conexão no web.config e em seguida remover o componente de dados da página ou pode informar a string diretamente no arquivo web.config conforme o código abaixo:






Em seguida devemos declarar os seguintes namespaces no arquivo code-behind :

Imports System.IO
Imports System.Data.sqlclient
Imports System.Data

Preenchendo o DropDownList com os dados da tabela Categories

Vamos preencher o controle Dropdownlist com os dados da tabela Categories. Isto deve ser feito quando a página for carregada por isso no evento Load da página inclua o seguinte código:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
 
If Page.IsPostBack = False Then

     Dim conexaoBD As New SqlConnection()
    Try

       ' configura a string de conexão obtendo-a a partir do arquivo web.config

       Dim strConnectionString As String =        ConfigurationManager.ConnectionStrings("NORTHWNDConnectionString").ConnectionString

        conexaoBD.ConnectionString = strConnectionString

        ' cria o comando com a instrução SQL para selecionar os dados da tabela Categories

        Dim strComandoTexto As String = "SELECT CategoryID, CategoryName FROM Categories ORDER BY CategoryName"

         Dim comando As New SqlCommand(strComandoTexto, conexaoBD)

        ' Abre a conexão com o banco de dados

        conexaoBD.Open()

        ' Preenche controle dropdownlist

        DropDownList1.DataSource = comando.ExecuteReader()
        DropDownList1.DataTextField = "CategoryName"
        DropDownList1.DataValueField = "CategoryD"
        DropDownList1.DataBind()

        ' força a primeira exibição do Gridview    DropDownList1_SelectedIndexChanged(Nothing, Nothing)

    Catch ex As Exception

        ' escreve o erro no arquivo de log
        Dim sw As StreamWriter = File.AppendText(Server.MapPath("~/logErro.txt"))
        sw.WriteLine(DateTime.Now.ToString & " : & ex.Message)
        sw.Close()
        ' exibe o erro na página
        label1.text = ex.Message.ToString

    Finally

        ' fecha a conexão com o banco de dados
         conexaoBD.Close()

    End Try

End If

End Sub

No evento DropDownList1_SelectedIndexChanged vamos colocar o código que irá exibir os dados no GridView de acordo com a seleção feita no dropdownlist:
Protected Sub DropDownList1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList1.SelectedIndexChanged

' configura a string de conexão a partir do arquivo web.config

' abre a conexão

Dim strConnectionString As String = ConfigurationManager.ConnectionStrings("NORTHWNDConnectionString").ConnectionString

Dim conexaoBD As New SqlConnection(strConnectionString)

Try

    'Constroi a consulta SQL para exibir os dados da tabela Produtos e Categorias usando um parâmetro
    Dim strComandoTexto As String = "SELECT Products.ProductName, Categories.CategoryName " _     & "FROM Products INNER JOIN Categories ON Products.CategoryID = Categories.CategoryID " _    
    & "WHERE Products.CategoryID = @CategoryID " _
    & "ORDER BY ProductName"

    ' cria o comando
    Dim comando As New SqlCommand(strComandoTexto, conexaoBD)

    ' define e inclui o parâmetro
    Dim parametro As New SqlParameter()
    parametro.ParameterName = "@CategoryID"
    parametro.SqlDbType = SqlDbType.Int
    parametro.Value = DropDownList1.SelectedValue

    comando.Parameters.Add(parametro)

    ' abre a conexao com o banco de dados
    conexaoBD.Open()

    ' exibe os dados
    GridView1.DataSource = comando.ExecuteReader()

    GridView1.DataBind()

finally

    ' fecha a conexao
    conexaoBD.Close()

End Try
End Sub


Para provocar um erro vou alterar o nome do parâmetro CategoryID removendo a letra I , desta forma ao executar iremos o obter a seguinte mensagem de erro:

Dê uma olhada na janela Solution Explorer , clicando no botão Refresh e perceba que o arquivo logErro.txt foi criado. Se visualizarmos o seu conteúdo iremos ver exatamente a mesma mensagem de erro com indicação de data e hora.

Tudo certo ? Quase...

Na verdade podemos melhorar essa nossa implementação de forma a torná-la menos rígida e mais elegante.
	A primeira coisa a fazer é criar uma pasta na estrutura do nosso projeto para armazenar o arquivo de log. Clique com o botão direito do mouse sobre o nome do projeto e selecione a opção New Folder e crie uma nova pasta chamada logErro

Agora vamos abrir o arquivo web.config e vamos definir o local e o nome do arquivo de log que vamos criar incluindo uma chave para que possamos identificar e obter estas informações através da nossa aplicação. Abaixo temos a definição incluída entre as tags :
.....


      



.......

Outra mudança importante que vamos efetuar será criar uma classe e nela definir a criação do arquivo e a gravação das mensagens no arquivo de log . Na solução atual estamos usando código diretamente na instrução try/catch e desta forma teremos que repetir o código para cada try/catch existente na aplicação.

É Bom saber.

Se você rodar a sua aplicação usando o Visual Web Developer, a ASP.NET irá reportar erros pela exibição de uma mensagem na página do seu Browser. Será usada uma página com uma mensagem de erro padrão mas você pode personalizar a página que é exibida quando um erro ocorrer. Basta definir a tag customErrors no arquivo web.config.: Veja o exemplo abaixo:


  
     
  


Existem 3 valores modos que podem ser usados:

    * Off - usa a página padrão para usuários locais e remotos na exibição dos erros.
    * On - usa a página definida pelo programador para exibir os erros para  usuários locais e remotos.
    * RemoteOnly - a página de erro é mostrada somente para usuário locais.

Após isso você deve definir a página paginaErro.aspx contendo a mensagem que deseja exibir para o usuário.

Clique com o botão direito sobre o nome do projeto e selecione a opção Add New Item. Na janela templates selecione o template Class e informe o nome Log.vb. O arquivo será incluído na pasta App_Data.

Abra o arquivo Log.vb e defina as seguintes declarações no início do arquivo:

Imports Microsoft.VisualBasic
Imports System.configuration
Imports System.io

Em seguida vamos definir dois construtores para a nossa classe:

1- O construtor padrão sem argumentos
2- O construtor que usa dois argumentos: a mensagem de erro e o objeto Exception

Observe que agora podemos gravar a mensagem e também o stackTrace, ou seja, a pilha de erros, fornecendo assim mais informações sobre a exceção ocorrida.
Public Sub New()
     MyBase.New()
End Sub

Public Sub New(ByVal mensagem As String, ByVal erro As Exception)

logErro(mensagem)

If erro IsNot Nothing Then
      logErro(erro.StackTrace)
End If
End Sub

Agora vamos criar o método estático (Shared) logErro(msg) que irá efetivamente criar o arquivo e gravar as mensagens de erro:
Public Shared Sub logErro(ByVal mensagem As String)

Dim caminho As String = ""
Dim data As String = ""
Dim contexto As HttpContext = HttpContext.Current

caminho = ConfigurationManager.AppSettings("arqlogErro")
data = DateTime.Now.ToString

Try
    Dim sw As StreamWriter = File.AppendText(contexto.Server.MapPath(caminho))
    sw.WriteLine(data & " :: " & mensagem)
    sw.Close()
Catch ex As Exception
    MsgBox(ex.Message)
End Try

End Sub

Aqui temos o código usado na solução inicial , onde incluímos a obtenção do caminho e nome do arquivo de log. Usamos a classe HttpContext para obtermos o caminho a partir do contexto , pois no servidor o caminho pode ser diferente do definido inicialmente.

Para usar a classe você deve criar uma instância da mesma e no bloco try/catch, fazer a chamada do construtor passando os argumentos, que podem ser : a mensagem e/ou objeto Exception.
.......
Dim log As LogErro

Try
      'codigo que efetua uma ação
Catch ex As Exception
      log = New LogErro(ex.Message, ex)
End Try
..................

Após efetuarmos alguns testes abrimos o arquivo logErro.txt onde podemos visualizar as mensagens gravadas:
09/08/2007 08:47:53 :: Arithmetic operation resulted in an overflow.
09/08/2007 08:48:13 :: Arithmetic operation resulted in an overflow.
09/08/2007 08:48:19 :: at _Default.Button1_Click(Object sender, EventArgs e) in C:_aspnartigosgeraLogDefault.aspx.vb:line 11
09/08/2007 08:49:44 :: Arithmetic operation resulted in an overflow.
09/08/2007 08:49:44 :: App_Web_dhz8c-i7
09/08/2007 08:49:44 :: at _Default.Button1_Click(Object sender, EventArgs e) in C:_aspnartigosgeraLogDefault.aspx.vb:line 11
09/08/2007 08:50:53 :: Arithmetic operation resulted in an overflow.
09/08/2007 08:51:28 :: Arithmetic operation resulted in an overflow.
09/08/2007 08:51:28 :: at _Default.Button1_Click(Object sender, EventArgs e) in C:_aspnartigosgeraLogDefault.aspx.vb:line 11

Temos ainda uma outra opção para tratar os erros inesperados em páginas ASP.NET :  fazer o tratamento no evento de erro a nível de página ou no evento de erro a nível de aplicação.

Para ilustrar isso clique com o botão direito do mouse sobre o nome do projeto e selecione a opção Add New Item.

Na janela Templates selecione o template  Global Configuration Class e aceite o nome padrão Global.asax

Abra o arquivo Global.asax podemos notar o evento Application_Error() destacada na figura abaixo.

Você pode incluir o seu código para tratar erros neste evento. Abaixo um exemplo de código que você pode usar neste evento para tratar erros:

<%@ Application Language="VB" %>

<%@ Import Namespace="System.Diagnostics"%>
 



 

A classe EventLog permite que você leia e escreva para o log de eventos e permite também que você crie um log, e, é isso que estamos fazendo no código acima.
É Bom saber.

Podemos ainda usar o evento Page_Error() para tratar erros a nível de página conforme exemplo de código abaixo:
Private Sub Page_Error(ByVal sender As Object, ByVal e As EventsArgs)

Dim errorMessage As String = "Erro ocorrido " + Server.GetLastError()

Server.ClearError()

Dim LogName As String = "MyAppLog"
Dim SourceName As String = "MyAppSource"

If Not (EventLog.SourceExists(SourceName) Then
     Not (EventLog.SourceExists(SourceName))
End If

EventLog.CreateEventSource(SourceName, LogName)

Dim MyLog As EventLog = New EventLog()
MyLog.Source = SourceName
MyLog.WriteEnTry(errorMessage, EventLogEnTryType.Error)

End Sub