Reputation: 456477
I'm trying to get a custom IExceptionHandler
to work with my Azure Function (C# class library). The idea is to have my own exception handler for unexpected exceptions that will contain my own in-memory trace log; to clarify, I do want these sent to the client browser and displayed to the user.
Details follow.
With this simple custom exception handler:
public sealed class CustomExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
context.Result = new ResponseMessageResult(
context.Request.CreateErrorResponse(HttpStatusCode.BadRequest,
"custom message string"));
}
}
I've tried to install it as such:
[FunctionName("Function1")]
public static HttpResponseMessage Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "works")]
HttpRequestMessage req)
{
req.GetConfiguration().Services.Replace(typeof(IExceptionHandler),
new CustomExceptionHandler());
throw new Exception("unexpected exception");
}
but when deployed, I just get the generic "operation failed" error message (e.g., XML-formatted from Chrome):
<ApiErrorModel xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Microsoft.Azure.WebJobs.Script.WebHost.Models">
<Arguments xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" i:nil="true"/>
<ErrorCode>0</ErrorCode>
<ErrorDetails i:nil="true"/>
<Id>dec07825-cf4e-49cc-a80e-bef0dbd01ba0</Id>
<Message>An error has occurred. For more information, please check the logs for error ID dec07825-cf4e-49cc-a80e-bef0dbd01ba0</Message>
<RequestId>51df1fec-c1c2-4635-b82c-b00d179d2e50</RequestId>
<StatusCode>InternalServerError</StatusCode>
</ApiErrorModel>
If I turn on Diagnostic Logs -> Detailed error messages
in the AF UI and try to ensure detailed error messages are always written:
[FunctionName("Function1")]
public static HttpResponseMessage Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "works")]
HttpRequestMessage req)
{
req.GetRequestContext().IncludeErrorDetail = true;
req.GetRequestContext().IsLocal = true;
req.GetConfiguration().Services.Replace(typeof(IExceptionHandler),
new CustomExceptionHandler());
throw new Exception("unexpected exception");
}
then I do get exception details, but still apparently handled by the builtin exception handler (XML-formatted from Chrome):
<ApiErrorModel xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Microsoft.Azure.WebJobs.Script.WebHost.Models">
<Arguments xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" i:nil="true"/>
<ErrorCode>0</ErrorCode>
<ErrorDetails>Microsoft.Azure.WebJobs.Host.FunctionInvocationException : Exception while executing function: Function1 ---> System.Exception : unexpected exception at ...</ErrorDetails>
<Id>5e752375-002b-4ab7-a348-20edad64a3fe</Id>
<Message>Exception while executing function: Function1 -> unexpected exception</Message>
<RequestId>b6611456-f3f1-4591-97e4-920dae59b5ff</RequestId>
<StatusCode>InternalServerError</StatusCode>
</ApiErrorModel>
so this is exposing the stack trace and exception message, but I don't have my in-memory trace log.
What I want to do is create the response message for unexpected exceptions myself. For example, this is what my custom error response looks like for an expected exception (JSON-formatted, from Postman):
{
"message": "An error has occurred.",
"exceptionMessage": "Could not find package `nito.bob`",
"exceptionType": "Common.ExpectedException",
"stackTrace": " at ...",
"log": [
"Received request for jsonVersion=4, packageId=`nito.bob`, packageVersion=``, targetFramework=``",
"Looking up latest package version for `nito.bob`",
"No non-prerelease package version found for `nito.bob`; looking up latest prerelease package version",
"No package version found for `nito.bob`",
"Returning 404: Could not find package `nito.bob`"
],
"requestId": "b2df08cb-1071-4e47-bd24-bf74152e4b2a"
}
This is just the basic HttpError
-formatted exception (with details) that I augment with log
and requestId
. Since this is an expected exception, this is not being done in an IExceptionHandler
; I'm just returning the HttpResponseMessage
.
The fun part: my custom IExceptionHandler
works just fine when hosting the AF locally. It just doesn't work when deployed. If I take the code and run it locally, I get back the response I expect (JSON-formatted from Postman):
{
"Message": "custom message string"
}
Alternative approaches that won't work for me:
Exception
s and returning HttpRequestMessage.CreateErrorResponse
. I'm already doing this for expected exceptions, and that works fine. This isn't a good solution for unexpected exceptions because I want the function execution to clearly be marked as "failed". Returning an HttpResponseMessage
(even with a 500 status code) is treated by the AF runtime as "successful", and does not show as "failed" in the dashboard or logs (or presumably by App Insights, though I haven't explicitly checked that).Upvotes: 33
Views: 2019
Reputation: 10390
I think what you are looking for is FunctionExceptionFilterAttribute
.
Here is a simple code sample for how to implement the abstract class:
public class YourErrorHandlerAttribute : FunctionExceptionFilterAttribute
{
public override async Task OnExceptionAsync(FunctionExceptionContext exceptionContext, CancellationToken cancellationToken)
{
var fn_name = exceptionContext.FunctionName;
var fn_instanceId = exceptionContext.FunctionInstanceId;
var ex = exceptionContext.Exception;
// TODO: your logic here.
}
}
Here is how to use it in your function:
[FunctionName("Function1")]
[YourErrorHandler]
public static HttpResponseMessage Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "works")]
HttpRequestMessage req)
{
// TODO: your Azure Function logic / implementation.
}
It behaves similarly to ActionFilterAttribute
in ASP.NET MVC
Upvotes: 1