Использование ELMAH в консольном приложении
Я только начал использовать ELMAH и я фанат. Моя команда поддерживает большое количество веб-приложений, и я особенно рад, что ELMAH позволяет нам сохранять исключения из каждого приложения в одной таблице базы данных MS SQL.
Мы также поддерживаем несколько консольных, DLL и настольных приложений. Можно ли использовать библиотеку ELMAH для регистрации исключений в этих приложениях в том же месте?
7 ответов
У нас здесь точно такая же ситуация. Запуск ELMAH для всех наших веб-приложений. У некоторых из них есть консольные планировщики.
После того, как вы покопались в исходном коде, кажется, что работает следующий код:
ErrorLog errorLog = ErrorLog.GetDefault(null);
errorLog.ApplicationName = "/LM/W3SVC/1/ROOT/AppName";
errorLog.Log(new Error(ex));
Единственная реальная проблема, связанная с вышесказанным, заключается в том, что вам нужно сохранить имя приложения где-то в вашей конфигурации, чтобы иметь возможность видеть записи в средстве просмотра ELMAH.axd.
Итак, в нашем общем коде обработки ошибок мы делаем:
if (HttpContext.Current != null)
ErrorSignal.FromCurrentContext().Raise(ex);
else
{
ErrorLog errorLog = ErrorLog.GetDefault(null);
errorLog.ApplicationName = ErrorHandling.Application;
errorLog.Log(new Error(ex));
}
Нам нужна была возможность входа из консольного приложения и службы Windows в дополнение к нашему сайту ASP.NET. Я использовал ответ (ErrorLog.GetDefault(null);
), который работал хорошо, пока мне не нужно было по электронной почте тоже.
Итак, вот мое решение. Он обрабатывает журнал, электронную почту, твиты и фильтрацию (как в файле конфигурации, так и в коде). Я также обернул основной вызов как расширение для исключения, поэтому он может быть вызван как: catch(Exception ex) { ex.LogToElmah(); }
Чтобы отфильтровать код, подключите соответствующее событие.Filtering: ElmahExtension.ErrorLog.Filtering += new ExceptionFilterEventHandler(ErrorLog_Filtering);
Код:
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);
}
}
}
}
Кроме того, вам нужно будет добавить ссылку на System.Web.dll
в вашем проекте, чтобы это работало.
РЕДАКТИРОВАТЬ: Согласно комментариям, этот код будет отправлять электронные письма, только если ваш файл конфигурации имеет <errorMail async="false"/>
, Обратитесь к этому фрагменту кода, если вы хотите сохранить <errorMail async="true"/>
в вашем конфигурационном файле (будет использоваться, когда HttpContext.Current доступен).
Если вы просто хотите отправить журнал по электронной почте без http, вы можете сделать это так:
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
И с точки зрения 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>
А также
<elmah>
<errorLog type="Elmah.XmlFileErrorLog, Elmah" logPath="C:\Elmah.Error" applicationName="MyAwesomeApp" />
<errorMail from="from@email.com" to="to@email.com" />
</elmah>
И настройки SMTP на ваш выбор.
Все сделано.:-)
Изменить: это может быть сделано - см. Этот ответ.
Я почти уверен, что ты не сможешь этого сделать. Я постараюсь выкопать соответствующий материал.
Итак, что я могу найти в поиске группы Google, так это то, что это невозможно... Поскольку ELMAH работает от HttpHandlers (конструкция asp.net), это только ASP.NET.
С учетом сказанного, есть способы, которыми вы можете использовать его в консольном приложении. ELMAH предоставляет метод для выявления ошибок, так что вы можете обернуть ELMAH в вашу обработку исключений и затем сообщить об ошибке через:
ErrorSignal.FromCurrentContext().Raise(new NotSupportedException());
Это будет означать оборачивание всего приложения в обработчик исключений и сигнализацию. Может потребоваться некоторая настройка, чтобы это исправить, но я думаю, что это вполне возможно.
Если вам это нужно, это ссылка на хранилище кодов ELMAH.
Для тех, кому нужен ответ Брайана Чанса, портированный на 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
Однако для регистрации ошибок в базе данных этого будет достаточно:
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
Как полное решение:
Public parent As Elmah.ServiceProviderQueryHandler = Nothing
' http://stackru.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
А также
Elmah.ErrorSignal.FromCurrentContext().Raise(New NotImplementedException("Test"))
будет работать, если он вызывается после Initialize()
Ну, так как я не могу комментировать, я опубликую это здесь, и, возможно, кто-то увидит это.
Следуя методу Брайана и его комментаторам, я смог заставить работать электронную почту, но я все еще не видел, чтобы сообщения SQL регистрировались, хотя я установил applicationName. Чего я не понял, так это того, что они на самом деле регистрировались, я просто их не видел, потому что applicationName должно совпадать с вашим web.config, чтобы иметь возможность его просматривать.
В моем файле web.config не было указано applicationName, поэтому по умолчанию он был "/LM/W3SVC/2/ROOT", что и было, в основном, комментарием asgeo1, хотя я не понимал, что должно быть так же.
Поскольку у меня действительно не было ошибок, с которыми я сталкивался, я настроил applicationName в своем web.config и мой app.config, чтобы они были одинаковыми, и теперь все выглядит как чемпион.
<errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="ELMAH" applicationName="MyAppName" />
ELMAH расшифровывается как Модули регистрации ошибок и обработчики - ссылаясь, конечно, на IHttpModule
а также IHttpHandler
,
Консольные приложения не используют HTTP, поэтому, как правило, не смогут извлечь большую пользу из модулей и обработчиков, созданных для HTTP.