Exceptions of my Azure Function are logged 3 times in Application Insights - c#

I have created a Blob Trigger Azure Function, and I wanted to be able to create mail alerts when I have an exception, and among the information sent in the mail alert, there should be the name of the file the exception occurred on.
I noticed that exceptions are automatically logged in Azure, but I found no way of customizing the message or the information sent along the exception. So I decided to inject a telemetry service in my Function App, and add the file name as a custom property, as you can see in the code below :
public class Function1
{
private readonly IGremlinService _gremlinService;
private readonly TelemetryClient _telemetryClient;
public Function1(IGremlinService gremlinService, TelemetryConfiguration telemetryConfiguration)
{
this._gremlinService = gremlinService;
this._telemetryClient = new TelemetryClient(telemetryConfiguration);
}
[FunctionName(nameof(Function1))]
public async Task Run([BlobTrigger("files/{directory}/{name}.00.pdf", Connection = "AzureWebJobsStorage")] Stream myBlob, string name, ILogger logger)
{
try
{
//some code not related to the issue
}
catch (Exception e)
{
var properties = new Dictionary<string, string>
{{"Filename", name}};
_telemetryClient.TrackException(e, properties);
if (e is ResponseException)
{
ResponseException re = (ResponseException) e;
var statusCode = (long) re.StatusAttributes["x-ms-status-code"];
_telemetryClient.TrackTrace("Error on file " + name + ". Status code: " + statusCode + " " + re.StackTrace, SeverityLevel.Error, properties);
}
else
{
_telemetryClient.TrackTrace("Error on file " + name, SeverityLevel.Error, properties);
}
throw;
}
}
}
}
But I still cannot customize the message to provide the user with additional information. I know I can send alerts on trace messages instead, and send customized messages this way, and this is currently what I'm doing, but I would find it cleaner to send alert on exceptions.
My second issue is that my exceptions are still logged automatically on top of being logged by the telemetry service, and for some reason I can't understand, they are logged twice, as you can see in the screenshot below from Application Insights :
Is there a way I can turn off the automatic logging of exceptions ? Or is there a way to customize these exceptions messages that I'm not aware of, instead of using the telemetry service ?

I believe, the 3 exceptions are being logged due to following reasons:
The implementation service of IGremlinService throwing exception which is being logged.
You are logging via _telemetryClient.TrackException(e, properties);
Azure infrastructure is handling when throw is invoked.
Now coming to your question
I found no way of customizing the message or the information sent along the exception
I would suggest you to use ILogger for LogException and use BeginScope (read here) to define the scope properties which will be logged as Custom Properties in application insights for all the logs which are invoked during the lifetime of created scope.
Using the ILogger object, your code will be simplified as follows and all traces and exceptions inside scope will have FileName as custom property in application insights.
[FunctionName(nameof(Function1))]
public async Task Run([BlobTrigger("files/{directory}/{name}.00.pdf", Connection = "AzureWebJobsStorage")] Stream myBlob, string name, ILogger logger)
{
using (logger.BeginScope(new Dictionary<string, object>()
{
["FileName"] = name,
}))
{
try
{
//some code not related to the issue
}
catch (Exception e)
{
logger.LogError(e, "Error occurred with {StatusCode}", (long) re.StatusAttributes["x-ms-status-code"]);
throw;
}
}
}
Following summarizes the definition of statement logger.LogException(e, "Error occurred with {StatusCode}", (long) re.StatusAttributes["x-ms-status-code"]);
e represents the actual exception which occurred.
the code part {StatusCode} will be logged the StatusCode as Custom Property in application insights for the logged exception so you don't need to create any dictionary.
FileName will be logged as Custom property as defined by Scope.
You can view a sample implementation at here.

Related

Tracking down unhandled exceptions in Blazor Webassembly

Any idea on how to get the line number of an unhanded exception in blazor webassembly?
There was a discussion long ago on some work that still needs to be done by the team to have it working. I think that died down if I'm not mistaken.
Consider the message below. It leaves one completely in the dark with no guidance on where to start looking.
Thanks in advance.
Not to be rude, but it is in the Blazor documentation to always handle exceptions yourself when interacting with components: https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/handle-errors?view=aspnetcore-5.0&pivots=webassembly#global-exception-handling
Simply wrap your all your method bodies (even the lifecycle methods) with a try/catch block. Bonus points to inject the ILogger<MyComponent> for friendly logging.
Example:
#inject ILogger<MyComponent> Logger
<h1>#UserName</h1>
#code {
private string UserName { get; set; } = string.Empty;
protected override async Task OnInitializedAsync()
{
try
{
NavigationManager.LocationChanged += OnLocationChanged;
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
UserName = $"Hi, {authState.User.Claims.FirstOrDefault(x => x.Type == "display_name")?.Value}!";
}
catch (Exception ex)
{
Logger.LogError($"Failed to initialize MyComponent. Error: {ex}");
}
}
}

How can i get the object that was null as (shown in the picture)

Exception Description with annotation
I am creating a middleware exception handler for an ASP.NET Core project and I am trying to get the source of the exception (as in the object that caused it). I can see the object when I am using the debugger, but i can't get it programmatically.
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ErrorHandlingMiddleware> _logger;
public ErrorHandlingMiddleware(RequestDelegate next, ILogger<ErrorHandlingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private Task HandleExceptionAsync(HttpContext context, Exception ex)
{
_logger.LogError(ex, ex.Message);
var code = HttpStatusCode.InternalServerError; // 500 if unexpected
var result = JsonConvert.SerializeObject(new { Error = ex.Message, Soure = ex.Source });
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)code;
return context.Response.WriteAsync(result);
}
I expected the source property in the exception to contain it, but it just shows the class.
To my best knowledge, there is only one way to do what you want:
Write a try...catch block for each and every property. This is simply not feasible.
Lookup is actually not even the soruce of this exception. It comes from somewhere in the function resolution mechanic. I am honestly not sure wich code precisely is throwing it, but I know the variable lookup did it's job (correctly taking, storing and returning "null") and is in no way at fault for this.
null values are right up there with Boneheaded Exceptions. As in it is literally mentioned. Go and prevent it from ever happening: "Boneheaded exceptions are your own darn fault, you could have prevented them and therefore they are bugs in your code. You should not catch them; doing so is hiding a bug in your code. Rather, you should write your code so that the exception cannot possibly happen in the first place, and therefore does not need to be caught. That argument is null, that typecast is bad, that index is out of range, you're trying to divide by zero – these are all problems that you could have prevented very easily in the first place, so prevent the mess in the first place rather than trying to clean it up."
Honestly, I can not really make sense of this function. Are you trying to put a Admin user into the DB? If so what does "AdminResponse" mean in this context?

Handle Error with NLog and Try Catch

I log errors in my Actions using NLog to store errors with additional information, for example:
using NLog;
private static Logger _logger = LogManager.GetCurrentClassLogger();
public virtual ActionResult Edit(Client client)
{
try
{
// FORCE ERROR
var x = 0;
x /= x;
return RedirectToAction(MVC.Client.Index());
}
catch (Exception e)
{
_logger.Error("[Error in ClientController.Edit - id: " + client.Id + " - Error: " + e.Message + "]");
}
}
And I have Error handling configured in Web.config:
<customErrors mode="On" />
But I don't get redirected to the Error.cshtml when I execute the Action (the page remains in the same place), why?
Can I use Elmah to do the same thing? (logging additional information like client Id)
First of all, most people solve this error by not catching the exception. This way, the exception propagates to ASP.NET, which displays a "500 Internal Error" webpage, and all the pertinent information is logged.
If your server is configured for production, the error page will just say "an error occurred, details were logged."
If the server is configured for development, then you will get the famous yellow page with the exception type, the message, and the stack trace.
Swallowing the exception and manually redirecting to an error page is a bad practice because it hides errors. There are tools that examine your logs and give you nice statistics, for example about percentages of successful/failed requests, and these won't work any more.
So, not swallowing the exception is what people do, and at the very least, it solves your problem.
Now, I find this very clunky, because I do not like manually looking for the source files mentioned in the yellow page and manually going to the mentioned line numbers. I practically have no use for the yellow page, it might just as well just say "an error occurred, cry me a river, nah-nah-nah." I don't read the yellow page.
Instead, I do like to log exceptions on my own, and I have my logger begin each line with full-path-to-source-filename(line):, so that every line on the debug log in visual studio is clickable, and clicking on a line automatically opens up the right source file, and scrolls to the exact line that issued the log message. If you want this luxury, then go ahead and catch the exception, but right after logging the exception you have to rethrow it, so that things can follow their normal course.
Amendment
Here is some information that was added in comments:
So, you can do the following:
try
{
...
}
catch (Exception e)
{
log( "information" );
throw; //special syntax which preserves original stack trace
}
Or
try
{
...
}
catch (Exception e)
{
throw new Exception( "information", e ); //also preserves original stack trace
}
Do not do this: catch( Exception e ) { log( "information" ); throw e; } because it loses the original stack trace information of e.
In your code, error occur at the division portion(x/=x) so no execution of redirect line(index page) and jump to catch portion executing the logger. You have to define the redirect to Error.cshtml in catch portion also.
Note: when you use try catch block error will not occur at ASP.NET level resulting no redirect to Error.cshtml page
using NLog;
private static Logger _logger = LogManager.GetCurrentClassLogger();
public virtual ActionResult Edit(Client client)
{
try
{
// FORCE ERROR
var x = 0;
x /= x; /// error occur here
return RedirectToAction(MVC.Client.Index()); /// no execution of this line
}
catch (Exception e)
{
_logger.Error("[Error in ClientController.Edit - id: " + client.Id + " - Error: " + e.Message + "]");
/// add redirect link here
return RedirectToAction(MVC.Client.Error()); /// this is needed since the catch block execute mean no error at ASP.net level resulting no redirect to default error page
}
}
This will streamline your exception handling and allow you to manage the process more succinctly. Create an attribute like this:
public class HandleExceptionAttribute : System.Web.Mvc.HandleErrorAttribute
{
// Pass in necessary data, etc
private string _data;
public string Data
{
get { return _data; }
set { _data = value; }
}
public override void OnException(System.Web.Mvc.ExceptionContext filterContext)
{
// Logging code here
// Do something with the passed-in properties (Data in this code)
// Use the filterContext to retrieve all sorts of info about the request
// Direct the user
base.OnException(filterContext);
}
}
Now you can use it on a controller or method level with an attribute like this:
[HandleException(Data="SomeValue", View="Error")]
Or, register it globally (global.asax) like this:
GlobalFilters.Filters.Add(new HandleExceptionAttribute());

How to catch a web service exception

How do you catch exceptions from a Web Service that is returning a custom object?
I've seen this post but it doesn't seem to show how to get the exception that was thrown by the service.
I can pull the SOAP Exception, but I want to be able to get the original exception that the web service returned. I've looked at the variables that are set at this time and can't seem to see the exception anywhere, I just see:
"Server was unable to process request. ---> Exception of type
'RestoreCommon.ConsignmentNotFoundException' was thrown."
try
{
Consignment cons = WebServiceRequest.Instance.Service
.getConsignmentDetails(txtConsignmentNumber.Text);
lblReceiverName.Text = cons.Receiver.Name;
}
catch (ConsignmentNotFoundException)
{
MessageBox.Show("Consignment could not be found!");
}
Is this possible?
In short, no.
Web services will always throw SOAP fault. In your code,
MessageBox meant to be used in Windows forms and nowhere else.
You can throw this exception and in the client application, you will have to handle a SOAP fault.
Edit: If you do not want to send exceptions across to the client, this what you could do:
class BaseResponse
{
public bool HasErrors
{
get;
set;
}
public Collection<String> Errors
{
get;
set;
}
}
Each WebMethod response must inherit from this class. Now, this is how your WebMethod blocks would look like:
public ConcreteResponse SomeWebMethod()
{
ConcreteResponse response = new ConcreteResponse();
try
{
// Processing here
}
catch (Exception exception)
{
// Log the actual exception details somewhere
// Replace the exception with user friendly message
response.HasErrors = true;
response.Errors = new Collection<string>();
response.Errors[0] = exception.Message;
}
finally
{
// Clean ups here
}
return response;
}
This is just an example. You may need to write proper exception handling code rather than simply using generic catch block.
Note: This will take care of exceptions occurring in your application only. Any exceptions occurring during communication between client and service, will still be thrown to the client application.

How should I log the extra details in webexception, soap exception etc?

In enterprise library I wasn't getting enough detail put into my logs, so I started writing this handler to pull out of the exception specific properties and add them to the message string:
[ConfigurationElementType(typeof(CustomHandlerData))]
public class ExposeDetailExceptionHandler : IExceptionHandler
{
public Exception HandleException(Exception exception, Guid handlingInstanceId)
{
if (exception is System.Net.WebException)
return ExposeDetail((System.Net.WebException)exception);
if (exception is System.Web.Services.Protocols.SoapException)
return ExposeDetail((System.Web.Services.Protocols.SoapException)exception);
return exception;
}
private Exception ExposeDetail(System.Net.WebException Exception)
{
string details = "";
details += "System.Net.WebException: " + Exception.Message + Environment.NewLine;
details += "Status: " + Exception.Status.ToString() + Environment.NewLine;
return new Exception(details, Exception);
}
private Exception ExposeDetail(System.Web.Services.Protocols.SoapException Exception)
{
//etc
}
}
(As as aside is there a better way of picking which version of ExposeDetail gets run?)
Is this the best or accepted way to log these details, my initial thought is that I should be implementing an ExceptionFormatter but this seemed a lot simpler.
Use Exception.Data. You can collect any extra details you want to log at the point the exception is first caught and add them into Exception.Data. You can also add other information that wasn't part of the original exception such as the Url, http headers, ...
Your exception logging code can then pick up Exception.Data and add all that information to the log.
You don't need to wrap the exception nor do you need to lose any of the call stack when you handle it this way. Use throw to rethrow the original exception, catch it again further up the stack, add more context to the .Data on it and so on out until you get to your exception handler.
I think you are right: an ExceptionFormatter is probably a better way.
I would use the extended properties to add your details. I don't think that it is any more complicated than a handler.
E.g.:
public class AppTextExceptionFormatter : TextExceptionFormatter
{
public AppTextExceptionFormatter(TextWriter writer,
Exception exception,
Guid handlingInstanceId)
: base (writer, exception, handlingInstanceId)
{
if (exception is System.Net.WebException)
{
AdditionalInfo.Add("Status", ((System.Net.WebException)exception).Status.ToString());
}
else if (exception is System.Web.Services.Protocols.SoapException)
{
AdditionalInfo.Add("Actor", ((SoapException)exception).Actor);
}
}
}

Categories