Getting the Id of an error in Elmah after calling .Raise()

拈花ヽ惹草 提交于 2019-12-09 09:59:51

问题


I'm working on an MVC3 application and I'm using Elmah to handle my error logging. What I want in my application is to carry the Elmah Id onto the custom error page as I will provide a link which allows a user to specifically report it in the event that it is a repeat error (in their opinion).

Now, I've read similar questions on here and they suggest adding the following code (or similar) to the Global.asax.cs file:

void ErrorLog_Logged(object sender, ErrorLoggedEventArgs args)
{
    string sessionId = Session.SessionID;
    Session["ElmahId_" + sessionId] = args.Entry.Id;
}

This is what I'm using at the moment, with the SessionID allowing for added flexibility in making the Session stored object unique. However, this may still cause issues if more than one error occurs at (virtually) the same time.

Instead, I decided to work on my own HandleErrorAttribute that looks something like this:

public class ElmahHandleErrorAttribute : FilterAttribute, IExceptionFilter
{
    public void OnException(ExceptionContext filterContext)
    {
        if (filterContext == null)
            throw new ArgumentNullException("filterContext");

        if (filterContext.IsChildAction && (!filterContext.ExceptionHandled
            && filterContext.HttpContext.IsCustomErrorEnabled))
        {
            Elmah.ErrorSignal.FromCurrentContext().Raise(filterContext.Exception);

            // get error id here
            string errorId = null;

            string areaName = (String)filterContext.RouteData.Values["area"];
            string controllerName = (String)filterContext.RouteData.Values["controller"];
            string actionName = (String)filterContext.RouteData.Values["action"];

            var model = new ErrorDetail
            {
                Area = areaName,
                Controller = controllerName,
                Action = actionName,
                ErrorId = errorId,
                Exception = filterContext.Exception
            };

            ViewResult result = new ViewResult
            {
                ViewName = "Error",,
                ViewData = new ViewDataDictionary<ErrorDetail>(model),
                TempData = filterContext.Controller.TempData
            };

            filterContext.Result = result;
            filterContext.ExceptionHandled = true;
            filterContext.HttpContext.Response.Clear();
            filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
        }
    }
}

where ErrorDetail is a custom model which just has the public properties that are being set here as strings. This data can then be shown in the model for admin's at a quick glance and the errorId can be used to create the 'Report Error' link.

So my question is does anyone know of a way of getting the Id after the line

Elmah.ErrorSignal.FromCurrentContext().Raise(filterContext.Exception)

without using the Logged event in the global.asax.cs?

Any thoughts are much appreciated.


回答1:


After reading Dupin's comments it seems logical that it isn't quite possible. I tried digging around the Elmah source code and came up with a couple of alternatives that might be worth sharing.

The obvious alternative is stick with my original option of using the Logged event:

void ErrorLog_Logged(object sender, ErrorLoggedEventArgs args)
{
    string sessionId = Session.SessionID;
    Session["ElmahId_" + sessionId] = args.Entry.Id;
}

For a more direct solution it is possible to manually log the error with the following:

string errorId = Elmah.ErrorLog.GetDefault(HttpContext.Current)
    .Log(new Elmah.Error(filterContext.Exception));

However, using this approach won't hit your filters or mail module and so on.

After doing a bit of thinking and a little more searching, I came up with a new compromise. Still using the logged event but I've found a way to create a new unique key that can be passed to the view, by adding my own data to the exception.

string loggingKey = "ElmahId_" + Guid.NewGuid().ToString();
filterContext.Exception.Data.Add("LoggingKey", loggingKey);

This way I can pass the exception in my view model, which has this key value in the Data collection. The logged event would be changed to something like this:

void ErrorLog_Logged(object sender, ErrorLoggedEventArgs args)
{
    string key = args.Entry.Error.Exception.Data["LoggingKey"].ToString();
    Session[key] = args.Entry.Id;
}

Then in the view I get the key from the model to then pull the Id from the Session collection.




回答2:


Maybe not very helpful but I suspect you can't get the error id at that point and you will need to use the logged event.

When you call

Elmah.ErrorSignal.FromCurrentContext().Raise(filterContext.Exception)

You're just raising the error. Depending on how you've configured ELMAH you might be logging the error or you might just send an email or a tweet.

There's no direct link between a raised error and an Id. That will only come with logging which, if you're feeling funny, you could be doing in multiple places and so creating multiple ids.




回答3:


http://code.google.com/p/elmah/issues/detail?id=148#c3 is an identical request and a proposed patch on the Elmah project site




回答4:


The solution above only works only if there is a Session object (website scenario). We needed it to work in an Azure WorkerRole, or a console / desktop app type setup. This solution will also work for web and save some session memory. There isn't a perfect solution, but one that worked for us to be able to log the error and retrieve the stored ID AND fire off an email is to:

  1. Store the error using ErrorLog.Log(error) (see: Using ELMAH in a console application)
  2. Raise the error skipping the logging (SQL or otherwise)

For the second part, we used the implementation of ElmahExtension given here: https://stackoverflow.com/a/2473580/476400

and REMOVED the following lines adding the logging:

(ErrorLog as IHttpModule).Init(httpApplication);
errorFilter.HookFiltering(ErrorLog);  //removed!

The entire call from our client code looks like this:

ErrorLog errorLog = ErrorLog.GetDefault(null);
errorLog.ApplicationName = "YourAppName";
Error error = new Error(ex);
string errorResult = errorLog.Log(error);
Guid errorId = new Guid(errorResult);

ex.LogToElmah(); //this is just going to send the email

You might want to call that extention method something else, like RaiseToElmahNoStorage(), or something to indicate it is skipping the storage component.



来源:https://stackoverflow.com/questions/7895271/getting-the-id-of-an-error-in-elmah-after-calling-raise

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!