Using ELMAH in a console application
Asked Answered
S

7

65

I just started using ELMAH and am a fan. My team supports a large number of web applications and I'm particularly excited that ELMAH lets us save exceptions from each application to the same MS SQL database table.

We also support a few console, DLL and desktop applications. Is it possible to use the ELMAH DLL to log exceptions in these apps to that same location?

Schmeltzer answered 8/5, 2009 at 19:38 Comment(1)
I'm planning to implement this in a Windows Service as well. Just wanted to check if there might be any performance issues/any other issues if we do so? It seems to me like this is primarily for web based applications. I haven't found too much material on how to implement it in windows applications. Thanks SoniImmediately
M
69

We have exactly the same situation here. Running ELMAH for all our web applications. A few of them have console based schedulers.

After doing some digging through the source code, the following code seems to work:

            ErrorLog errorLog = ErrorLog.GetDefault(null);
            errorLog.ApplicationName = "/LM/W3SVC/1/ROOT/AppName";
            errorLog.Log(new Error(ex));

The only real problem with the above is that you need to keep the application name somewhere in your config to be able to see the entries on the ELMAH.axd viewer.

So in our generic error handling code we do:

        if (HttpContext.Current != null)
            ErrorSignal.FromCurrentContext().Raise(ex);
        else
        {
            ErrorLog errorLog = ErrorLog.GetDefault(null);
            errorLog.ApplicationName = ErrorHandling.Application;
            errorLog.Log(new Error(ex));
        }
Meaghan answered 1/7, 2009 at 10:26 Comment(3)
Hmm - pretty cool, but I can't seem to get it to send emails when the error occurs in the console app. Any ideas?Claudclauddetta
Unlikely, the errorLog.Log seems to bypass all the other listeners. Anyone know any better?Meaghan
@Duke I liked this approach but Unfortunately My module sits in a separate project . I figured out if you reference elmah like this to use it in a non web applicaiton it wont compile until you add System.Web Dll to it .Fonteyn
C
78

We needed the ability to log from a console app and a windows service in addition to our ASP.NET site. I used the answer (ErrorLog.GetDefault(null);) which worked well until I needed to email too.

So, here is my solution. It handles the log, email, tweet and filtering (both in the config file and in code). I have also wrapped the main call as an extension to Exception so it can be called like: catch(Exception ex) { ex.LogToElmah(); }

To filter in code, hook the corresponding .Filtering event: ElmahExtension.ErrorLog.Filtering += new ExceptionFilterEventHandler(ErrorLog_Filtering);

Code:

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);
                }
            }
    }
}

In addition, you will need to add a reference to the System.Web.dll in your project for this to work.

EDIT: As per the comments, this code will send emails only if your config file has <errorMail async="false"/>. Refer to this code snippet should you want to keep <errorMail async="true"/> in your config file (to be used when HttpContext.Current is available).

Cordie answered 18/3, 2010 at 21:52 Comment(8)
Awesome post. This worked perfectly for me. Just had to put the normal stuff from the web.config in my app.config and smooth sailing!Eyeleen
This wasn't working for me in a unit test until I set <elmah><errorMail async="false" /></elmah>. I had async previously set to true.Amoy
Thanks for the post--worked great! We hooked up many events including ErrorLog_Filtering, ErrorLog_Logged, ErrorMail_Filtering, ErrorMail_Mailing, ErrorMail_Mailed, ErrorMail_DisposingMail using the appropriate classes (ExceptionFilterEventHandler, ErrorLoggedEventHandler, etc.)Evapotranspiration
I copied your code and in my exception I do this ex.LogToElmah(); and it goes through the code but when I look in elmah.axd nothing is ever logged. On local host I don't use email sending or anything so I am not sure if that is the cause or not. Nothing crashes though. Do I have to hook something else up?Gastineau
Using this extension. Adding the configsection and the elmah configuration to app.config, setting <errorMail async="false"/>, made it work for me.Braggart
The errors were not appearing in my log either (I'm using a SQL server log). I had to set the applicationName attribute in my App.config to get it to work. I.e. <errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="elmah-sqlserver" applicationName="/LM/W3SVC/3/ROOT" />Ernestinaernestine
I get this error: System.ArgumentException: Path cannot be the empty string or all whitespace., does anyone know why ? There's no google search records for this!!Tiebout
Am a little confused, do I just add a separate class with the above code, add errorlog settings in App.config. Is there anything else I need to do? Moreover, I am not able to find applicationName in main Project's Web.Config to be included in App.config of console app.Quintet
M
69

We have exactly the same situation here. Running ELMAH for all our web applications. A few of them have console based schedulers.

After doing some digging through the source code, the following code seems to work:

            ErrorLog errorLog = ErrorLog.GetDefault(null);
            errorLog.ApplicationName = "/LM/W3SVC/1/ROOT/AppName";
            errorLog.Log(new Error(ex));

The only real problem with the above is that you need to keep the application name somewhere in your config to be able to see the entries on the ELMAH.axd viewer.

So in our generic error handling code we do:

        if (HttpContext.Current != null)
            ErrorSignal.FromCurrentContext().Raise(ex);
        else
        {
            ErrorLog errorLog = ErrorLog.GetDefault(null);
            errorLog.ApplicationName = ErrorHandling.Application;
            errorLog.Log(new Error(ex));
        }
Meaghan answered 1/7, 2009 at 10:26 Comment(3)
Hmm - pretty cool, but I can't seem to get it to send emails when the error occurs in the console app. Any ideas?Claudclauddetta
Unlikely, the errorLog.Log seems to bypass all the other listeners. Anyone know any better?Meaghan
@Duke I liked this approach but Unfortunately My module sits in a separate project . I figured out if you reference elmah like this to use it in a non web applicaiton it wont compile until you add System.Web Dll to it .Fonteyn
J
14

If you just want to email the log without http you can do like this:

    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

And in terms of 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>

And

  <elmah>
    <errorLog type="Elmah.XmlFileErrorLog, Elmah" logPath="C:\Elmah.Error" applicationName="MyAwesomeApp" />
    <errorMail from="[email protected]" to="[email protected]" />
  </elmah>

And smtp settings of your choice.

All done. :-)

Jolinejoliotcurie answered 20/6, 2012 at 5:22 Comment(1)
Hands down the answer! after trying eeeeverything this worked first try!Unilingual
P
5

Edit: This CAN be done - See this answer.


I'm pretty sure you can't do this. I'll try and dig up the relevant material.

http://groups.google.com/group/elmah/browse_thread/thread/f214c4f782dc2bf4/d96fe43b60765f0c?lnk=gst&q=app#d96fe43b60765f0c

So from what I can find searching the Google group is that it's not possible... Since ELMAH works off of HttpHandlers (an asp.net construct) it is ASP.NET only.

With that said, there are ways that you could utilize it on a console application. ELMAH provides a method to raise errors, so you could wrap ELMAH in your exception handling and then signal an error via:

ErrorSignal.FromCurrentContext().Raise(new NotSupportedException());

This would mean wrapping your entire application in an exception handler and signaling. It might take you some tweaking to get it down, but I think it's totally possible.

In case you require it, this is the link to the ELMAH code repository.

Puppet answered 8/5, 2009 at 19:47 Comment(1)
Thank you. I understand that it wasn't build around this, but it seems like a need others might have, to share a common error dbSchmeltzer
D
2

For those that need Brian Chance's answer ported to 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

However, for just logging errors to the database, this will be sufficient:

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

As a complete solution:

Public parent As Elmah.ServiceProviderQueryHandler = Nothing




' https://mcmap.net/q/112552/-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

And

Elmah.ErrorSignal.FromCurrentContext().Raise(New NotImplementedException("Test"))

will work if it is called after Initialize()

Dover answered 8/1, 2013 at 15:1 Comment(0)
W
2

Well, since I can't comment I'll post this down here and maybe someone will see it.

After following Brian's method and the commentors I was able to get email working but I still wasn't seeing the SQL messages being logged, even though I had set the applicationName. What I didn't realize is that they were actually being logged I just wasn't seeing them because the applicationName must be the same as your web.config in order to be able to view it.

My web.config didn't have applicationName specified, so it was defaulting to "/LM/W3SVC/2/ROOT", which is basically what "asgeo1" commented, though I didn't realize it had to be the same.

Since I didn't really have any errors I was concerned with, I configured applicationName in my web.config and my app.config to be the same and now everything shows up like a champ.

<errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="ELMAH" applicationName="MyAppName" />
Walter answered 23/1, 2013 at 3:19 Comment(0)
C
-5

ELMAH stands for Error Logging Modules and Handlers - referring, of course, to IHttpModule and IHttpHandler.

Console applications do not use HTTP, so would typically not be able to benefit much from Modules and Handlers built for HTTP.

Chilpancingo answered 1/7, 2009 at 13:17 Comment(1)
Some people have a hard time with the truth, hence your down-votes. I for one think ELMAH is a square peg to a console-apps round hole. It isn't that difficult to roll your own tiny Assembly that will do 2 things: 1.) Send an Email of the Exception. 2.) Log the Exception to the Database (even to the ELMAH Table). Then reference that tiny Custom Assembly in your Console Apps.Fid

© 2022 - 2024 — McMap. All rights reserved.