Este artigo tem como finalidade mostrar como centralizar o tratamento e manipulação de todos os erros de um site em um único local. Alem disso, vamos salvar todos os erros em um arquivo txt e responder uma pagina amigável de erro para o usuário usando um template.
Alem de tratar os erros, algo muito importante é exibir os erros ou pelo menos saber o que esta acontecendo. Em uma página normal fica fácil fazer o tratamento e exibir um erro amigável, mas quando usamos Ajax nem sempre isso é possível, bem como ações acionadas por terceiros como o consumo de um webservice. Ou então quando o cliente manda um print de erro 500 do IE. Por estes e outros motivos vamos criar um arquivo de log dos erros. E quando a aplicação não funcionar e não aparecer nenhum erro na tela, podemos consultar o arquivo de log.
- Vamos usar o Application_Error do Global.asax.
Assim como o Web.Config o Global.asax fica no diretorio base do site e tudo passa por ele.
O Application_Error do Global.asax.cs lida com todos os erros não tratados de uma aplicação ASP.NET. Mas é importante lembrar que se o erro for tratado usando try e catch ele não vai passar pelo Application_Error.
- Log.cs, vamos criar essa classe para gerenciar o log e as mensagens de erro.
Nesse exemplo vou criar um txt com o nome log-yyyyMMdd.txt, ou seja, vai criar um arquivo novo para cada dia. Para facilitar a exlusao de logs velhos e para não criar um arquivo gigantesco. Sem contar que ficar mais fácil de achar o erro já que os arquivos ficam divididos por data.
- template.htm, este arquivo será o template para as mensagens de erro e para as paginas não encontradas. Pois não faremos nenhum redirect para manter o “Internal Server Error” e o “Not Found” na URL devida. Em resumo, esse template vai substituir aquela tela amarela da morte.
Vamos inverter a ordem e iniciar pelo template, neste exemplo usei um template muito básico.
/template.htm
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Ops, {0}</title>
</head>
<body>
<h1>Ops, {0}</h1>
<p>URL:{1}</p>
<p>{2}</p>
</body>
</html>
Agora vamos criar a classe Log
/App_Code/Log.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using System.Net;
/// <summary>
/// Summary description for Log
/// </summary>
public class Log
{
/// <summary>
/// Salva o LOG de erros, cria um novo arquivo para cada dia.
/// </summary>
/// <param name="msg"></param>
public static void Save(string msg)
{
//Path do arquivo txt
//C:\inetpub\wwwroot\nomedosite\App_Data\log-20111024.txt
string strFile = System.Web.HttpContext.Current.Server.MapPath("~/App_Data/log-" + DateTime.Now.ToString("yyyyMMdd") + ".txt");
//Se arquivo não existir
if (!File.Exists(strFile))
{
//Criar o arquivo,
//Estou usando o using para fazer o Dispose automático do arquivo após criá-lo.
using (FileStream fs = File.Create(strFile)) { }
}
//Escreve o Erro no txt
//Os erros são concatenados, ou seja, não são sobreescritos.
using (StreamWriter w = File.AppendText(strFile))
{
string _msg = string.Empty;
string URL = HttpContext.Current.Request.Url.AbsoluteUri.ToString();
//Adicionar um separador
_msg = "#############################################################\r\n";
//Data do erro
_msg += "Data:" + DateTime.Now.ToString("yyyyMMdd-HH:mm:ss") + "\r\n";
//URL do erro
_msg += "URL:" + URL + "\r\n";
//Adicionando a mensagem
_msg += msg;
//quebra de lina e nova linha
_msg += "\r\n\r\n";
//Escreve no arquivo
w.Write(_msg);
//Fecha
w.Close();
}
}
/// <summary>
/// Monta e exibe o template para o erro
/// </summary>
/// <param name="strTitle"></param>
/// <param name="strMessage"></param>
public static void ShowTemplate(string strTitle, string strMessage)
{
HttpContext context = HttpContext.Current;
//A linha abaixo limpa as mensagens de erro e não vai exibi-las na página.
context.Server.ClearError();
//Limpa todo o conteudo.
context.Response.Clear();
//Criando o WebClient, para ler o template
WebClient wc = new WebClient();
wc.Encoding = System.Text.Encoding.UTF8;
//Obtendo o conteúdo do template
string strTemplate = wc.DownloadString(context.Server.MapPath("~/template.htm"));
strTemplate = string.Format(strTemplate, strTitle, strMessage, DateTime.Now.ToString());
context.Response.Write(strTemplate);
context.Response.End();
}
}
E por fim vamos criar o Global.asax
/Global.asax
<%@ Application Language="C#" %>
<%@ Import Namespace="System.Diagnostics" %>
<%@ Import Namespace="System.Text" %>
<script runat="server">
void Application_Start(object sender, EventArgs e)
{
}
void Application_End(object sender, EventArgs e)
{
}
void Application_Error(object sender, EventArgs e)
{
//URL da pagina
string URL = HttpContext.Current.Request.Url.AbsoluteUri.ToString();
//Criando um objeto do tipo System.Exception e pegar o último erro
Exception error = Server.GetLastError();
//Se error não for null
if (error != null)
{
string strErrorMessage = string.Empty;
//Pegando o código do erro.
//Caso o erro seja do tipo HttpException vai pegar o código do erro,
//ou então definirá o código como 500
int code = (error is HttpException) ? (error as HttpException).GetHttpCode() : 500;
//Pegando os erros e montar a mensagem
while (error.InnerException != null)
{
//código do erro
strErrorMessage += "Código:" + code + "\r\n";
//Mensagem de erro (InnerException)
strErrorMessage += "Mensagem:" + error.InnerException;
//Salva no arquivo txt
Log.Save(strErrorMessage);
//Próximo erro
error = error.InnerException;
}
//Caso o erro seja 404, vamos salvar como URL não encontrada.
if (code == 404)
{
strErrorMessage += "URL não encontrada";
//Definir o StatusCode da página para 404
Response.StatusCode = 404;
//Salva no arquivo txt
Log.Save(strErrorMessage);
//Exibir o template de erro na página atual
Log.ShowTemplate("página não encontrada.", URL);
}
else
{
//Definir o StatusCode da página para 500
Response.StatusCode = 500;
//Exibir o template de erro na página atual
Log.ShowTemplate("ocorreu um erro inesperado na página.", URL);
}
}
}
void Session_Start(object sender, EventArgs e)
{
}
void Session_End(object sender, EventArgs e)
{
}
</script>
Para testar crie uma página com o código abaixo.
/Default.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
Response.Write(Convert.ToInt32("c"));
}
Ou entao:
try
{
//lança nova Exceção (apenas para teste)
throw new Exception("Exemplo de Exception");
}
catch (Exception ex)
{
//Gravar o erro usando a classe Log
Log.Save(ex.Message);
Log.ShowTemplate("Exemplo de Exception", Request.Url.AbsoluteUri);
}
Se preferir pode fazer o download do projeto aqui.