Elmah not logging manual errors - c#

Elmah is working alright catching exceptions that are unhandled. However, in one place I am using the following code to manually log error incase a webapi action causes exception. This error is somehow not displayed in elmah.axd wonder why?
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
Here is something I didn't notice earlier, log in debug window:
Exception thrown: 'System.Web.HttpRequestValidationException' in System.Web.dll
System.Web.HttpRequestValidationException (0x80004005): A potentially dangerous Request.Form value was detected from the client (headers="...8241F for <x+152#mail....").
at System.Web.HttpRequest.ValidateString(String value, String collectionKey, RequestValidationSource requestCollection)
at System.Web.HttpRequest.<>c__DisplayClass280_0.<ValidateHttpValueCollection>b__0(String key, String value)
at System.Web.HttpValueCollection.EnsureKeyValidated(String key)
at System.Web.HttpValueCollection.GetValues(Int32 index)
at System.Collections.Specialized.NameValueCollection.Add(NameValueCollection c)
at Elmah.Error.CopyCollection(NameValueCollection collection)
at Elmah.Error..ctor(Exception e, HttpContext context)
at Elmah.ErrorLogModule.LogException(Exception e, HttpContext context)
More Context to the question:
Basically, I have an action method in webapi which parses and processes incoming email parse from sendgrid. If any type of exception occurs, it is manually logged with ELMAH and Application Insights in Azure. Application Insight logging works while ELMAH is failing with the above error message.

Related

Losing USER information in ELMAH when using ADFS

I have an MVC web application using ELMAH to log unhandled exception. I'm now moving from windows authentication to ADFS authentication. The application has been adapted and is working fine. However now when I check the error, the user information is not there anymore. Which makes sense as ELMAH is using the context identity and not the claims to retrieve this info. Does anyone of you have an idea how I could do to get this information logged again?
You can enrich ELMAH errors using error filtering hook if you can accept a small hack. In short, you need to implement the ErrorLog_Filtering method in the Global.asax.cs file:
void ErrorLog_Filtering(object sender, ExceptionFilterEventArgs args)
{
var httpContext = args.Context as HttpContext;
if (httpContext != null)
{
var error = new Error(args.Exception, httpContext);
error.User = GetUserFromDatabase();
ErrorLog.GetDefault(httpContext).Log(error);
args.Dismiss();
}
}
In the example, I update the User property with the value of a dummy method. How you want to implement this depends on how you would get the currently logged in user from ADFS. Finally, I log the error again and dismiss the initial (user-less) error.

.Net Core Identity 2 Provider login Cancel leads to unhandled exception

I've added LinkedIn as a provider. I have implemented the login and register with LinkedIn without any issue. In the use case where the user CANCELS from within the provider Pages (either linkedIn login or cancels the authorization of the app) the identity middleware seems to throw an unhandled exception:
An unhandled exception occurred while processing the request.
Exception: user_cancelled_login;Description=The user cancelled LinkedIn login
Unknown location
Exception: An error was encountered while handling the remote login.
Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler.HandleRequestAsync()
System.Exception: user_cancelled_login;Description=The user cancelled LinkedIn login
Exception: An error was encountered while handling the remote login.
The provider setup in startup defines the callback:
services.AddAuthentication().AddOAuth("LinkedIn", "LinkedIn", c =>
{
c.ClientId = Configuration["Authentication:LinkedIn:ClientId"];
c.ClientSecret = Configuration["Authentication:LinkedIn:ClientSecret"];
c.Scope.Add("r_basicprofile");
c.Scope.Add("r_emailaddress");
c.CallbackPath = "/signin-linkedin";
....
And As I have said the middleware seems to handled ALL other cases except where the user cancels within the LinkedIn pages. The return URL from LinkedIn looks correct:
https://localhost:44372/signin-linkedin?error=user_cancelled_login&error_description=The+user+cancelled+LinkedIn+login&state=CfDJ8MA7iQUuXmhBuZKmyWK9xeAgBBkQvnhf1akLhCIn9bsajCPUf7Wg22oeZBH9jZOIY3YrchMSWZ4dH7NQ1UngLKEuqgU-IHfBglbgJDtS-wc4Z-6DnW66uR0G1ubvNVqatFJHYv17pgqZT98suVkvKgihcJdbNEw7b1ThkuFbn9-5EcYhQ5ln6ImoTgthT8io1DOcCfc_-nBVfOa93a6CpUJTsZc9w93i70jn5dKKXSLntZe0VyRSA0r0PKc5spu5En-0R1rxiLjsjo4dy89PV3A
But never gets to my ExternalCallback controller method where the other cases like successful login/authorization are handled??
I'm wondering if this is working for anyone else with 3rd part providers?
There's a Github issue that explains what's happening here in more detail, with a bit of information as to why it's happening and even an indication that this won't be "fixed":
Handling the RemoteFailure event is the right thing to do. We should update our docs/samples to show how to handle that event and at least show a more appropriate message to the user. The error page sample could include a link to enabled the user to try logging in again.
Unfortunately it's difficult to implement this event in a very generic way that's also super useful because each remote auth provider has its own behavior for different types of failures.
The workaround for this (as quoted above) is to handle the RemoteFailure event:
services.AddAuthentication().AddOAuth("LinkedIn", "LinkedIn", c => {
// ...
c.Events.OnRemoteFailure = ctx =>
{
// React to the error here. See the notes below.
return Task.CompletedTask;
}
// ...
});
ctx is an instance of RemoteFailureContext, which includes an Exception property describing what went wrong. ctx also contains a HttpContext property, allowing you to perform redirects, etc, in response to such exceptions.
I've found the following to work well for me, based on this and similar to Kirk Larkin's answer. The part that took a little figuring out was where to redirect to, without causing problems for subsequent login attempts.
services.AddAuthentication().AddOAuth("LinkedIn", "LinkedIn", c =>
{
...
c.Events = new OAuthEvents()
{
OnRemoteFailure = (context) =>
{
context.Response.Redirect(context.Properties.GetString("returnUrl"));
context.HandleResponse();
return Task.CompletedTask;
}
};
};

What to do/throw when you have hit a API limit

I am using a API with a limit, there is a high possibility that I could hit the limit.
I am wondering how people handle this? Do they check if they hit the limit and then throw an exception? If so what type of exception?
Are there any best practices?
I am able to see if I hit the limit like below:
if (!string.IsNullOrEmpty(result.error))
{
// we have hit a limit
}
This API is used for a MVC application.
I am caching the ActionResult methods with the API content ([OutputCache]). If the Action method does not recieve the API result then the view will be empty, but if i throw something it will end up on the custom errors page.
You should first log what happened using the information you receive from result.error and then create a custom exception and throw it including the original error message.
if (!string.IsNullOrEmpty(result.error))
{
// we have hit a limit
// log error
// throw new MyException(result.error);
}
Normally you can't do much in this type of error so you log it, throw the exception and it's somebody's else problem. When the exception is caught, you could decide, based on the exception type, to wait and retry.

HttpContext.Current.Request.Url.AbsolutePath generate error in global.asax file

I am am using HttpContext.Current.Request.Url.AbsolutePath in global.asax file in one of teh functions to get page related details from database but it fails with following error.
`string _result = Helper.GetPageDetails( HttpContext.Current.Request.Url.AbsolutePath);`
Can i use this HttpContext.Current.Request.Url.AbsolutePath here i need to get the absolutePath of URL and i am getting following error
An exception of type 'System.Web.HttpException' occurred in System.Web.dll but was not handled in user code
Additional information: Request is not available in this context
I catch this in try{} i get following error message details
ex = {"Request is not available in this context"}
This means there is no request associated with the current method call (thread). I am guessing you are calling HttpContext.Current.Request.Url.AbsolutePath from a seperate thread than the one from the request. Without further info thats all i can say
In global.asax you use
Context.Request
as a replacement for
HttpContext.Current.Request

Who is invoking Error view and why?

I got a MVC4 web application running and functioning properly since almost a year. Lately while reviewing production error logs, I found a lot of mysterious exception entries of same type. I checked logs and found these exceptions occur on both Beta and Production environment but not in local (seems like a hint - see findings 1 below).
There are two subsequent entries:
One is:
Thread Id: 56, Message: Server_Exception - The view 'Error' or its
master was not found or no view engine supports the searched
locations. The following locations were searched:
~/Views/OrganizationUser/Error.cshtml
~/Views/OrganizationUser/Error.vbhtml ~/Views/Shared/Error.cshtml
~/Views/Shared/Error.vbhtml
Second log entry is:
Thread Id: 56, Message: Server_Exception - Stack Trace - at
System.Web.Mvc.ViewResult.FindView(ControllerContext context) at
System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context)
at
System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass25.b__22(IAsyncResult
asyncResult) at
System.Web.Mvc.Controller.<>c__DisplayClass1d.b__18(IAsyncResult
asyncResult) at
System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass4.b__3(IAsyncResult
ar) at System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult
asyncResult) at
System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass4.b__3(IAsyncResult
ar) at
System.Web.Mvc.MvcHandler.<>c__DisplayClass8.b__3(IAsyncResult
asyncResult) at
System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass4.b__3(IAsyncResult
ar) at
System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step,
Boolean& completedSynchronously)
The exception is logged from Application_Error() in global.asax.cs like so:
var ex = Server.GetLastError();
if (ex != null)
{
logger.Log(string.Format("Server_Exception - {0}", ex.Message), LogCategory.Error);
logger.Log(string.Format("Server_Exception - Stack Trace - {0}", ex.StackTrace), LogCategory.Error);
}
And here is the snapshot of DB log messages:
Finding/Analysis:
Since the errors are logged only on remote server, it tells me it has got to do with how MVC handles remote errors? CustomErrors? - I don't have it defined it in web.config. Should I?
But the main question is, what caused the error to happen? And who is looking for view name Error? I know its hard for you to guess it but just checking my luck.
Another finding is the exceptions are logged after user has logged out. In my Login get form, I check isAuthenticated and if user is not authenticated I log a message 'Request not authenticated, showing login form.' and display login form. I can see this message in logged prior to above exception messages. So what's going on on login.cshtml that causes a contact to server which generates an error? I checked, login page and _Layout which has lots of server side code embedded but could not figure out what could cause server to try to load Error page.
I think internally MVC is calling Error page which does not exist in my source code hence these exception. But why 'Error' page? I thought to get advice on what to check in this case. Please advise if you want me to share anything that could help you understand my problem.
I suspect you are using the System.Web.Mvc.HandleErrorAttribute as this attribute will attempt to look for an Error view under certain conditions when an exception is thrown inside a controller action:
The currently executing action is not a child action
The exception has not already been handled or custom errors are enabled
When wrapped in a HttpException, the exception has a 500 HTTP status code
The exception is an instance of System.Exception
When all of the above are true, System.Web.Mvc.HandleErrorAttribute creates a System.Web.Mvc.HandleErrorInfo instance, sets the ExceptionContext.Result property to a ViewResult which by default has the ViewName property set to Error.
Putting all of this together, In one of your controllers, an exception must be being thrown that meets all of the above points, in a controller action that has the [HandleError] attribute applied (maybe it's applied globally), which is attempting to find the Error view and failing to do so.
To know what the original exception is, you could put an Error view into your solution and write out the Exception message of the HandleErrorInfo model instance passed to the view (if the current user has the appropriate access controls).

Categories