Usando ELMAH en una aplicación de consola
-
20-08-2019 - |
Pregunta
Recién comencé a usar ELMAH y soy fanático.Mi equipo admite una gran cantidad de aplicaciones web y estoy particularmente entusiasmado de que ELMAH nos permita guardar excepciones de cada aplicación en la misma tabla de base de datos MS SQL.
También admitimos algunas aplicaciones de consola, DLL y de escritorio.¿Es posible utilizar la DLL ELMAH para registrar excepciones en estas aplicaciones en esa misma ubicación?
Solución
Tenemos exactamente la misma situación aquí.Ejecutando ELMAH para todas nuestras aplicaciones web.Algunos de ellos tienen programadores basados en consola.
Después de investigar un poco el código fuente, el siguiente código parece funcionar:
ErrorLog errorLog = ErrorLog.GetDefault(null);
errorLog.ApplicationName = "/LM/W3SVC/1/ROOT/AppName";
errorLog.Log(new Error(ex));
El único problema real con lo anterior es que necesita mantener el nombre de la aplicación en algún lugar de su configuración para poder ver las entradas en el visor ELMAH.axd.
Entonces, en nuestro código genérico de manejo de errores hacemos:
if (HttpContext.Current != null)
ErrorSignal.FromCurrentContext().Raise(ex);
else
{
ErrorLog errorLog = ErrorLog.GetDefault(null);
errorLog.ApplicationName = ErrorHandling.Application;
errorLog.Log(new Error(ex));
}
Otros consejos
Necesitábamos la capacidad de iniciar sesión desde una aplicación de consola y un servicio de Windows además de nuestro sitio ASP.NET. Utilicé la respuesta (ErrorLog.GetDefault(null);
) que funcionó bien hasta que también tuve que enviar un correo electrónico.
Entonces, aquí está mi solución. Maneja el registro, el correo electrónico, el tweet y el filtrado (tanto en el archivo de configuración como en el código). También incluí la llamada principal como una extensión de Exception para que se pueda llamar así: catch(Exception ex) { ex.LogToElmah(); }
Para filtrar el código, enganche el evento .Filtering correspondiente: ElmahExtension.ErrorLog.Filtering += new ExceptionFilterEventHandler(ErrorLog_Filtering);
Código:
using System;
using System.Web;
using Elmah;
namespace System
{
public static class ElmahExtension
{
public static void LogToElmah(this Exception ex)
{
if (HttpContext.Current != null)
{
ErrorSignal.FromCurrentContext().Raise(ex);
}
else
{
if (httpApplication == null) InitNoContext();
ErrorSignal.Get(httpApplication).Raise(ex);
}
}
private static HttpApplication httpApplication = null;
private static ErrorFilterConsole errorFilter = new ErrorFilterConsole();
public static ErrorMailModule ErrorEmail = new ErrorMailModule();
public static ErrorLogModule ErrorLog = new ErrorLogModule();
public static ErrorTweetModule ErrorTweet = new ErrorTweetModule();
private static void InitNoContext()
{
httpApplication = new HttpApplication();
errorFilter.Init(httpApplication);
(ErrorEmail as IHttpModule).Init(httpApplication);
errorFilter.HookFiltering(ErrorEmail);
(ErrorLog as IHttpModule).Init(httpApplication);
errorFilter.HookFiltering(ErrorLog);
(ErrorTweet as IHttpModule).Init(httpApplication);
errorFilter.HookFiltering(ErrorTweet);
}
private class ErrorFilterConsole : ErrorFilterModule
{
public void HookFiltering(IExceptionFiltering module)
{
module.Filtering += new ExceptionFilterEventHandler(base.OnErrorModuleFiltering);
}
}
}
}
Además, necesitará agregar una referencia a System.Web.dll
en su proyecto para que esto funcione.
EDITAR : según los comentarios, este código enviará correos electrónicos solo si su archivo de configuración tiene <errorMail async="false"/>
. Consulte este fragmento de código si desea mantener <errorMail async="true"/>
en su archivo de configuración (para usar cuando HttpContext.Current está disponible).
Si solo desea enviar el registro por correo electrónico sin http, puede hacer esto:
public class MyElmahMail: ErrorMailModule
{
public MyElmahMail()
{
//this basically just gets config from errorMail (app.config)
base.OnInit(new HttpApplication());
}
public void Log(Error error)
{
//just send the email pls
base.ReportError(error);
}
}
//to call it
var mail = new MyElmahMail();
mail.Log(new Error(new NullReferenceException()));//whatever exception u want to log
Y en términos de app.config
//Under configSections
<sectionGroup name="elmah">
<section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah" />
<section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" />
<section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" />
<section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah" />
</sectionGroup>
Y
<elmah>
<errorLog type="Elmah.XmlFileErrorLog, Elmah" logPath="C:\Elmah.Error" applicationName="MyAwesomeApp" />
<errorMail from="from@email.com" to="to@email.com" />
</elmah>
Y la configuración smtp de su elección.
Todo listo. :-)
Editar : Esto PUEDE hacerse - Consulte esto respuesta.
Estoy bastante seguro de que no puedes hacer esto. Trataré de desenterrar el material relevante.
Para aquellos que necesitan la respuesta de Brian Chance portada a VB.NET:
Imports System
Imports System.Web
Imports Elmah
Namespace System
Public NotInheritable Class ElmahExtension
Private Sub New()
End Sub
<System.Runtime.CompilerServices.Extension> _
Public Shared Sub LogToElmah(ex As Exception)
If HttpContext.Current IsNot Nothing Then
ErrorSignal.FromCurrentContext().Raise(ex)
Else
If httpApplication Is Nothing Then
InitNoContext()
End If
ErrorSignal.[Get](httpApplication).Raise(ex)
End If
End Sub
Private Shared httpApplication As HttpApplication = Nothing
Private Shared errorFilter As New ErrorFilterConsole()
Public Shared ErrorEmail As New ErrorMailModule()
Public Shared ErrorLog As New ErrorLogModule()
Public Shared ErrorTweet As New ErrorTweetModule()
Private Shared Sub InitNoContext()
httpApplication = New HttpApplication()
errorFilter.Init(httpApplication)
TryCast(ErrorEmail, IHttpModule).Init(httpApplication)
errorFilter.HookFiltering(ErrorEmail)
TryCast(ErrorLog, IHttpModule).Init(httpApplication)
errorFilter.HookFiltering(ErrorLog)
TryCast(ErrorTweet, IHttpModule).Init(httpApplication)
errorFilter.HookFiltering(ErrorTweet)
End Sub
Private Class ErrorFilterConsole
Inherits Elmah.ErrorFilterModule
Public Sub HookFiltering([module] As Elmah.IExceptionFiltering)
AddHandler [module].Filtering, New Elmah.ExceptionFilterEventHandler(AddressOf MyBase.OnErrorModuleFiltering)
End Sub
End Class
End Class
End Namespace
Sin embargo, solo para registrar errores en la base de datos, esto será suficiente:
If System.Web.HttpContext.Current Is Nothing Then
Dim req As System.Web.HttpRequest = New System.Web.HttpRequest(String.Empty, "https://www.domain.tld", Nothing)
Dim res As System.Web.HttpResponse = New System.Web.HttpResponse(Nothing)
System.Web.HttpContext.Current = New System.Web.HttpContext(req, res)
'Dim request As System.Web.Hosting.SimpleWorkerRequest = New System.Web.Hosting.SimpleWorkerRequest("/blah", "c:\inetpub\wwwroot\blah", "blah.html", Nothing, New System.IO.StringWriter())
'System.Web.HttpContext.Current = New System.Web.HttpContext(request)
System.Web.HttpContext.Current.ApplicationInstance = New System.Web.HttpApplication()
Dim ErrorLog As New Elmah.ErrorLogModule()
TryCast(ErrorLog, System.Web.IHttpModule).Init(System.Web.HttpContext.Current.ApplicationInstance)
End If
Como solución completa:
Public parent As Elmah.ServiceProviderQueryHandler = Nothing
' http://stackoverflow.com/questions/5981750/configuring-elmah-with-sql-server-logging-with-encrypted-connection-string
Public Function Elmah_MS_SQL_Callback(objContext As Object) As System.IServiceProvider
Dim container As New System.ComponentModel.Design.ServiceContainer(parent(objContext))
Dim strConnectionString As String = COR.SQL.MS_SQL.GetConnectionString()
Dim log As Elmah.SqlErrorLog = New Elmah.SqlErrorLog(strConnectionString)
'Dim strApplicationName = System.Web.Compilation.BuildManager.GetGlobalAsaxType().BaseType.Assembly().FullName
Dim strApplicationName As String = System.Reflection.Assembly.GetExecutingAssembly().FullName
If Not String.IsNullOrEmpty(strApplicationName) Then
log.ApplicationName = strApplicationName.Substring(0, strApplicationName.IndexOf(","))
End If
container.AddService(GetType(Elmah.ErrorLog), log)
Return container
End Function ' Elmah_MS_SQL_Callback
Public Function Elmah_PG_SQL_Callback(objContext As Object) As System.IServiceProvider
Dim container As New System.ComponentModel.Design.ServiceContainer(parent(objContext))
Dim strConnectionString As String = COR.SQL.MS_SQL.GetConnectionString()
Dim log As Elmah.PgsqlErrorLog = New Elmah.PgsqlErrorLog(strConnectionString)
'Dim strApplicationName = System.Web.Compilation.BuildManager.GetGlobalAsaxType().BaseType.Assembly().FullName
Dim strApplicationName As String = System.Reflection.Assembly.GetExecutingAssembly().FullName
If Not String.IsNullOrEmpty(strApplicationName) Then
log.ApplicationName = strApplicationName.Substring(0, strApplicationName.IndexOf(","))
End If
container.AddService(GetType(Elmah.ErrorLog), log)
Return container
End Function ' Elmah_PG_SQL_Callback
' http://weblogs.asp.net/stevewellens/archive/2009/02/01/debugging-a-deployed-site.aspx
Public Sub Initialize()
If System.Web.HttpContext.Current Is Nothing Then
Dim req As System.Web.HttpRequest = New System.Web.HttpRequest(String.Empty, "https://www.domain.tld", Nothing)
Dim res As System.Web.HttpResponse = New System.Web.HttpResponse(Nothing)
System.Web.HttpContext.Current = New System.Web.HttpContext(req, res)
'Dim request As System.Web.Hosting.SimpleWorkerRequest = New System.Web.Hosting.SimpleWorkerRequest("/blah", "c:\inetpub\wwwroot\blah", "blah.html", Nothing, New System.IO.StringWriter())
'System.Web.HttpContext.Current = New System.Web.HttpContext(request)
System.Web.HttpContext.Current.ApplicationInstance = New System.Web.HttpApplication()
Dim ErrorLog As New Elmah.ErrorLogModule()
TryCast(ErrorLog, System.Web.IHttpModule).Init(System.Web.HttpContext.Current.ApplicationInstance)
End If
parent = Elmah.ServiceCenter.Current
If SQL.IsMsSql Then
Elmah.ServiceCenter.Current = AddressOf Elmah_MS_SQL_Callback
End If
If SQL.IsPostGreSql Then
Elmah.ServiceCenter.Current = AddressOf Elmah_PG_SQL_Callback
End If
End Sub ' InitializeElmah
Y
Elmah.ErrorSignal.FromCurrentContext().Raise(New NotImplementedException("Test"))
funcionará si se llama después de Initialize ()
Bueno, como no puedo comentar, publicaré esto aquí y tal vez alguien lo vea.
Después de seguir el método de Brian y los comentaristas, pude hacer que el correo electrónico funcionara pero aún no veía los mensajes SQL que se registraban, a pesar de que había configurado el nombre de la aplicación. Lo que no me di cuenta es que en realidad se estaban registrando. Simplemente no los estaba viendo porque el applicationName debe ser el mismo que su web.config para poder verlo.
Mi web.config no tenía applicationName especificado, por lo que estaba predeterminado a " / LM / W3SVC / 2 / ROOT " ;, que es básicamente lo que " asgeo1 quot; comentó, aunque no me di cuenta de que tenía que ser lo mismo.
Como realmente no tenía ningún error que me preocupara, configuré applicationName en mi web.config y mi app.config para que fueran iguales y ahora todo aparece como un campeón.
<errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="ELMAH" applicationName="MyAppName" />
ELMAH significa Módulos y controladores de registro de errores, en referencia, por supuesto, a IHttpModule
y IHttpHandler
.
Las aplicaciones de consola no utilizan HTTP, por lo que normalmente no podrían beneficiarse mucho de los módulos y controladores creados para HTTP.