267

Is it possible to do the following using ELMAH?

logger.Log(" something");

I'm doing something like this:

try 
{
    // Code that might throw an exception 
}
catch(Exception ex)
{
    // I need to log error here...
}

This exception will not be automatically logged by ELMAH, because it was handled.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Omu
  • 64,955
  • 87
  • 259
  • 396
  • 1
    For future reference, I wrote a post about exactly that: [Logging errors programmatically](http://docs.elmah.io/logging-errors-programmatically/). My [ELMAH Tutorial](http://blog.elmah.io/elmah-tutorial/) also have some information about this. – ThomasArdal Sep 01 '16 at 19:24

10 Answers10

421

Direct log writing method, working since ELMAH 1.0:

try 
{
    some code 
}
catch(Exception ex)
{
    Elmah.ErrorLog.GetDefault(HttpContext.Current).Log(new Elmah.Error(ex));
}

ELMAH 1.2 introduces a more flexible API:

try 
{
    some code 
}
catch(Exception ex)
{
    Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
}

There is a difference between the two solutions:

  • Raise method applies ELMAH filtering rules to the exception. Log method does not.
  • Raise is subscription based and is able to log one exception into the several loggers.
Zo Has
  • 11,503
  • 20
  • 78
  • 144
Andrey Kamaev
  • 28,034
  • 6
  • 85
  • 85
  • 1
    what's the difference between your method and the others? – Omu Sep 16 '11 at 06:52
  • is there any difference between logging your way and rising ? – Omu Sep 16 '11 at 07:05
  • 3
    These log the error in Elmah without causing the application to stop working. It allows for you to catch common exceptions, handle them properly, but still be able to log them. – PCasagrande Jan 15 '13 at 19:39
  • 7
    I found that Elmah.ErrorSignal wasn't logging when POST back contains unsafe Html for Mvc4 .Net 4.5, in my example a POST back from Windows Access Control Services with a SignInResponseMessage. Elmah.ErrorLog.GetDefault did work in that scenario – Adam Feb 27 '13 at 01:17
  • 1
    I had same issue with unsafe Html. ErrorLog.GetDefault did the trick – hgirish Apr 25 '13 at 22:44
  • 4
    One big caveat when using `Elmah.ErrorLog.Log()`: it throws in case the log call itself fails, possibly bringing down the whole web app. `Raise()` fails silently. For instance: if there is a misconfiguration issue on the server-side (e.g. Elmah is configured to save the errors to disk, but does not have the correct access to the logs folder), the `.Log()` method will throw. (This is good for debugging though, e.g. why doesn't `.Raise()` log anything?) – Cristian Diaconescu Feb 09 '16 at 11:22
  • 1
    Yeah the choice of Raise() and Log() is an important one. Raise is great for silent background logging, but should only be used where you want to potentially trace things after the fact: Log is better if your logging is part of your audit trail and you'd rather stop the user than allow them do act without logs. – Jon Story May 25 '16 at 16:03
  • 1
    Another caveat: the first example (which uses `HttpContext.Current`) works in cases where you have background threads/processes, provided you save `HttpContext.Current` into a variable and have it available to whatever code executes the block in this answer. – ashes999 Sep 29 '16 at 20:30
91

I'd recommend wrapping the call to Elmah in a simple wrapper class of your own.

using Elmah;

public static class ErrorLog
{
    /// <summary>
    /// Log error to Elmah
    /// </summary>
    public static void LogError(Exception ex, string contextualMessage=null)
    {
        try
        {
            // log error to Elmah
            if (contextualMessage != null) 
            {
                // log exception with contextual information that's visible when 
                // clicking on the error in the Elmah log
                var annotatedException = new Exception(contextualMessage, ex); 
                ErrorSignal.FromCurrentContext().Raise(annotatedException, HttpContext.Current);
            }
            else 
            {
                ErrorSignal.FromCurrentContext().Raise(ex, HttpContext.Current);
            }

            // send errors to ErrorWS (my own legacy service)
            // using (ErrorWSSoapClient client = new ErrorWSSoapClient())
            // {
            //    client.LogErrors(...);
            // }
        }
        catch (Exception)
        {
            // uh oh! just keep going
        }
    }
}

Then just call it whenever you need to log an error.

try {
   ...
} 
catch (Exception ex) 
{
    // log this and continue
    ErrorLog.LogError(ex, "Error sending email for order " + orderID);
}

This has the following benefits:

  • You don't need to remember this slightly archaic syntax of the Elmah call
  • If you have many DLLs you don't need to reference Elmah Core from every single one - and just put this in your own 'System' DLL.
  • If you ever need to do any special handling or just want to put in a breakpoint to debug errors you have it all one place.
  • If you ever move away from Elmah you can just change one place.
  • If you have legacy error logging you want to retain (I just happen to have a simple error logging mechanism that's tied into some UIs that I dont immediately have time to remove).

Note: I've added a 'contextualMessage' property for contextual information. You can omit this if you prefer but I find it very useful. Elmah automatically unwraps exceptions so the underlying exception will still be reported in the log but the contextualMessage will be visible when you click on it.

Simon_Weaver
  • 120,240
  • 73
  • 577
  • 618
  • 1
    Great answer. Maybe ELMAH should implement something similar out of the box. Sometimes is really difficult to debug an error without context. – ra00l Aug 31 '13 at 18:04
  • 2
    I like all but swallowing any secondary errors with the `// uh oh! just keep going`. If my error handling is failing I want to know. I want it to make some noise. – Jeremy Cook Jan 14 '14 at 16:46
  • 3
    @JeremyCook I agree, but with the caveat that if you're not careful failed error handling routines tend to end up calling themselves and then blowing up (oh and I was also actually calling a third party API here to log the error). I probably shouldn't have left this in for this answer but I'd had bad experiences with such a thing happening before – Simon_Weaver Jul 29 '14 at 21:21
  • @Simon_Weaver well stated, a recursive call could end up blowing up the app pool potentially affecting more than one site. – jaybro Aug 07 '14 at 20:58
  • @jaybro but yes a better comment that 'uh oh!' wouldn't have gone amiss – Simon_Weaver Oct 04 '14 at 18:53
  • 1
    It would be even better imho as an extension method. – Stephen Kennedy Nov 17 '14 at 21:16
  • 1
    You might be thinking: nah, how many manual errors could I possibly try to log? I thought that, about a year ago. Long story short: use this wrapper! – nmit026 Jan 04 '17 at 08:20
29

You can use the Elmah.ErrorSignal() method to log an issue without raising an exception.

try
{
    // Some code
}
catch(Exception ex)
{
    // Log error
    Elmah.ErrorSignal.FromCurrentContext().Raise(ex);

    // Continue
}
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
bigtv
  • 2,353
  • 2
  • 27
  • 38
19
catch(Exception ex)
{
    Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
}
Darin Dimitrov
  • 960,118
  • 257
  • 3,196
  • 2,876
14

Yes, it is possible. ELMAH was designed to intercept unhandled exceptions. However you can signal an exception to ELMAH via the ErrorSignal class. Those exceptions are not thrown (don't bubble up), but are only sent out to ELMAH (and to subscribers of the Raise event of the ErrorSignal class).

A small example:

protected void ThrowExceptionAndSignalElmah()
{
    ErrorSignal.FromCurrentContext().Raise(new NotSupportedException());
}
Christophe Geers
  • 7,468
  • 3
  • 31
  • 47
13

I was looking to do this same thing in a thread I had started to queue mail from within my MVC4 application, as such I did not have the HttpContext available when an exception was raised. To do this I ended up with the following based on this question and another answer found on here: elmah: exceptions without HttpContext?

In the config file I specified an application name:

<elmah>
    <security allowRemoteAccess="false" />
    <errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="ELMAH" applicationName="myApplication"/>   
</elmah>

Then in code (like the answer provided above, but without the HttpContext) you can pass null instead of an HttpContext:

ThreadPool.QueueUserWorkItem(t => {
     try {
         ...
         mySmtpClient.Send(message);
     } catch (SomeException e) {
         Elmah.ErrorLog.GetDefault(null).Log(new Elmah.Error(e));
     }
 });
Community
  • 1
  • 1
Matthew
  • 9,580
  • 4
  • 40
  • 74
  • I like your solution; however, I am unable to resolve "Elmah." in my project. I tried adding "using Elmah;" in my code, but it does not exist in my current context. – Taersious Dec 10 '13 at 18:20
  • @Taersious What does your `packages.config` look like? Do you see something like: `` `` `'`? Did you install with NuGET? – Matthew Dec 10 '13 at 20:36
  • I would like to say yes, but my project is currently locked in source control. I implemented elmah manually from sample config file in project. – Taersious Dec 10 '13 at 21:21
  • @Taersious If doing it manually, did you add the Elmah reference to the project before invoking the using... I would expect it to work either way, but I know that when nuget adds it the lines above get added to the `packages.config` – Matthew Dec 10 '13 at 22:27
6

I am on ASP.NET core and I use ElmahCore.

To manually log errors with HttpContext (in controller) simply write:

using ElmahCore;
...
HttpContext.RiseError(new Exception("Your Exception"));

In another part of your application without HttpContext:

using ElmahCore;
...
ElmahExtensions.RiseError(new Exception("Your Exception"));
A. Morel
  • 5,749
  • 41
  • 40
3

Sometimes CurrentHttpContext may not be available.

Define

public class ElmahLogger : ILogger
{
    public void LogError(Exception ex, string contextualMessage = null, bool withinHttpContext = true)
    {
        try
        {
            var exc = contextualMessage == null 
                      ? ex 
                      : new ContextualElmahException(contextualMessage, ex);
            if (withinHttpContext)
                ErrorSignal.FromCurrentContext().Raise(exc);
            else
                ErrorLog.GetDefault(null).Log(new Error(exc));
        }
        catch { }
    }
}

Use

public class MyClass
{
    readonly ILogger _logger;

    public MyClass(ILogger logger)
    {
        _logger = logger;
    }

    public void MethodOne()
    {
        try
        {

        }
        catch (Exception ex)
        {
            _logger.LogError(ex, withinHttpContext: false);
        }
    }
}
tchelidze
  • 7,334
  • 1
  • 25
  • 44
0

I was trying to write custom messages into elmah logs using Signal.FromCurrentContext().Raise(ex); and found that these exceptions are bubbled up, eg:

try
{
    ...
}
catch (Exception ex)
{
    Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
    // this will write to the log AND throw the exception
}

Besides I don't see how elmah supports different levels of logging - is it possible to switch off verbose logging by a web.config setting?

Tunaki
  • 116,530
  • 39
  • 281
  • 370
Valery Gavrilov
  • 138
  • 1
  • 3
  • 1
    If you catch an exception and don't throw it again, exceptions doesn'ts bubble up. Maybe I misunderstand? ELMAH doesn't support different levels of logging. It's for errors only. – ThomasArdal Mar 09 '17 at 19:08
  • Thanks, Thomas. That's exactly what I was trying to confirm – Valery Gavrilov Mar 12 '17 at 23:38
0

Used this line and it works perfectly fine.

 try{
            //Code which may throw an error
    }
    catch(Exception ex){
            ErrorLog.GetDefault(HttpContext.Current).Log(new Elmah.Error(ex));
    }
Prajakta Kale
  • 344
  • 3
  • 19