How to intercept an Azure WebJob failure / exception - c#

Currently in Azure when a a WebJob throws an exception, the exception gets caught and handled by the JobHost (somehow) and then logs the exception to the dashboard that's available through the blade of the Web App in which the webjob is hosted. Is there any way to intercept the error handling or override it so that I can plug in my Application Insights instance ?

You can use the Azure WebJobs SDK Extensions : there is an ErrorTrigger so that you can use to intercept unhandled exceptions :
public class UnhandledErrorTrigger : IDisposable
{
private readonly TelemetryClient _telemetryClient;
public UnhandledErrorTrigger(TelemetryClient telemetryClient)
{
_telemetryClient = telemetryClient;
}
public void UnHandledException([ErrorTrigger("0:01:00", 1)] TraceFilter filter, TextWriter log)
{
foreach (var traceEvent in filter.Events)
{
_telemetryClient.TrackException(traceEvent.Exception);
}
// log the last detailed errors to the Dashboard
log.WriteLine(filter.GetDetailedMessage(1));
}
public void Dispose()
{
_telemetryClient.Flush();
}
}
To register the Error extensions, call config.UseCore() in your startup code :
private static void Main()
{
var config = new JobHostConfiguration();
config.UseCore();
...
new JobHost(config).RunAndBlock();
}
So if you are using an IoC container, you can easily inject your TelemetryClient. To configure a job activator for the webjob you can look at this post:
Dependency injection using Azure WebJobs SDK?

Have a look at some azure docs here. You can attach a handler to the AppDomain handling unknown exceptions (taken from the link above):
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
// ...
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
ExceptionTelemetry excTelemetry = new ExceptionTelemetry((Exception)e.ExceptionObject);
excTelemetry.SeverityLevel = SeverityLevel.Critical;
excTelemetry.HandledAt = ExceptionHandledAt.Unhandled;
telemetryClient.TrackException(excTelemetry);
telemetryClient.Flush();
}

Related

Nservicebus unity Endpoint failed to start

Using .NET 4.5.2, Visual studio 2017, C# 7.1, Unity, NServiceBus 6.
I receive the following error:
My application is a console app, here's some of the Program.cs code:
private static async Task ConfigureUnity()
{
IUnityContainer container = new UnityContainer();
var endpointConfiguration = new EndpointConfiguration("NSB.ChannelAdvisorService");
var transport = endpointConfiguration.UseTransport<LearningTransport>();
endpointConfiguration.AssemblyScanner().ExcludeAssemblies("netstandard");
endpointConfiguration.UseContainer<UnityBuilder>(
customizations =>
{
customizations.UseExistingContainer(container);
});
var endpointInstance = Endpoint.Start(endpointConfiguration).GetAwaiter().GetResult();
//register
container.RegisterType(typeof(IGenericHttpRequestRepository<>), typeof(GenericHttpRequestRepository<>), new TransientLifetimeManager());
container.RegisterType<IOrderRepository, OrderRepository>();
container.RegisterType<IShipmentRepository, ShipmentRepository>();
container.RegisterType<IOrderProcessService, OrderProcessService>();
container.RegisterType<IShipmentService, ShipmentService>();
container.RegisterInstance(endpointConfiguration);
//resolve
var orderProcessService = container.Resolve<IOrderProcessService>();
var shipmentService = container.Resolve<IShipmentService>();
.....
As you can see I'm using Unity and NServiceBus, this is to register DI and also use it withing NServicebus so i can DI it into my service to send a command.
The service trys to DI "IEndpointInstance"
public class OrderProcessService : IOrderProcessService
{
private static Logger logger = LogManager.GetCurrentClassLogger();
private readonly IEndpointInstance _endpoint;
public OrderProcessService(IEndpointInstance endpoint)
{
_endpoint = endpoint;
}
public async Task PostNewOrderBatch()
{
var list = _orderRepository.GetBatchedOrders();
foreach(var item in list)// parallel this?
{
await _endpoint.Send(item.ToObject<ProcessBatchOrdersCommand>()).ConfigureAwait(false);
_orderRepository.DeleteFile(item.Property("FilePath").Value.ToString());
}
}
}
I get the feeling it could be an issue about the order of things, I don't think I've missed anything out as far as i can tell in some examples?
In NServiceBus v6 and later the endpoint instance is no longer automatically registered in the container. You need to register the endpoint instance returned from Endpoint.Start(configuration) on the existing container.
See https://docs.particular.net/nservicebus/dependency-injection/#using-an-existing-instance-endpoint-resolution

How to configure WebJob ServiceBusTrigger retry policy

Edit: I will accept Azure configuration related changes as an answer to this question.
I am attempting to setup a retry policy to prevent instantly retrying a message when a 3rd party service is temporarily unavailable.
Currently the job is retried immediately multiple times and fails each time due to the temporary outage of the 3rd party service.
How do I set a retry delay for these messages?
I have the following code for Main:
class Program
{
static void Main()
{
var config = new JobHostConfiguration();
if (config.IsDevelopment)
config.UseDevelopmentSettings();
config.UseCore();
config.UseServiceBus(new ServiceBusConfiguration()
{
ConnectionString = Configuration.GetAppSetting("Microsoft.ServiceBus.ConnectionString"),
MessageOptions = new OnMessageOptions()
{
}
});
var host = new JobHost(config);
LogManager.GetCurrentClassLogger().Information("F1.Birst.Automation web job starting.");
// The following code ensures that the WebJob will be running continuously
host.RunAndBlock();
}
}
I have an ErrorMonitor setup which properly logs errors:
public class ExceptionHandler
{
private static readonly ILogger Log = LogManager.GetCurrentClassLogger();
public void Handle([ErrorTrigger] TraceFilter message, TextWriter log)
{
foreach (var exception in message.GetEvents())
Log.Error(exception.Exception.InnerException, exception.Message);
}
}
And my message handler looks like this:
public class ChurchCodeChangedEventHandler : ChurchSpaceHandler
{
private static readonly ILogger Log = LogManager.GetCurrentClassLogger();
public void Handle([ServiceBusTrigger(nameof(ChurchCodeChangedEvent), "F1.Birst.Automation.ChurchCodeChangedEvent")] ChurchCodeChangedEvent message, TextWriter log)
{
Log.Information(LogTemplates.ChurchCodeChanged, message.ChurchId);
// snip
}
}
How do I set a retry delay for these messages?
Webjobs do not support the concept of delayed retries. You can only control a few things using ServiceBusConfiguration, but those are not retries looking at the source code.
You could use frameworks like NServiceBus or MassTransit to get delayed retries. There's an example of how to use NServiceBus with WebJobs and you can run it locally to see how delayed retries would work.

Error handling in Bot Framework dialogs

How can I catch all exceptions in dialogs? Is there something like ASP.NET Exception Filter?
I want to send different messages to user depending on exception type.
Thank you
You are right about the fact that you can use ExceptionFilter.
You just have to do the following:
Create your ExceptionFilter class, for example to force the tracking of the exception in Application Insights (or in your case handle specific exception types):
using Microsoft.ApplicationInsights;
using System.Net.Http;
using System.Web.Http.Filters;
namespace BotDemo.App_Start
{
public class ExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext ctx)
{
HandleError(ctx);
}
private static void HandleError(HttpActionExecutedContext ctx)
{
ctx.Response = new HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError)
{
Content = new StringContent(ctx.Exception.Message)
};
var client = new TelemetryClient();
client.TrackException(ctx.Exception);
}
}
}
Don't forget to define your exception filter in your Application_Start():
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configuration.Filters.Add(new ExceptionFilter());
...
That's it.
In fact Bot Framework template is using ASP.Net, so you have all the normal features.

C# Web App: Custom Logger for external logging

Hello c# experts,
I'm writing an AWS lambda function in c# which requires external logging (I have to write all my logs to an external endpoint). I have been reading many articles about the best practices. Also I did some research on c# libraries such as NLog, Log4Net etc (Some libraries aren't compatible with .Net Core).
My main requirement is that I do not want to pass a Logger object around everywhere in my code. Instead, I should be able to write an external log more like a static method call (Ex: Logger.sendLog("log message");).
Considering the above requirement and the threading issues, I have decided to implement my Logger as a singleton which works fine at the moment. But, I'm curious to know if there are any particular issues with this design. Please let me know if there is a better way to implement a custom Logger class which writes external logs.
Thank you.
interface ILogger
{
void init();
void setUserInfo(UserInfo userInfo);
void sendLog(Dictionary<LogKey, object> payload);
}
public sealed class Logger : ILogger
{
private static readonly Logger instance = new Logger();
private static LogObject logObject;
public static Logger Instance
{
get
{
return instance;
}
}
public void init()
{
logObject = new LogObject();
logObject.appId = AppConfig.appId;
logObject.application = AppConfig.appName;
logObject.version = AppConfig.appVersion;
logObject.environment = EnvConfig.Instance.clientConfig.environment;
logObject.clientName = EnvConfig.Instance.clientConfig.clientName;
}
public void setUserInfo(UserInfo userInfo)
{
logObject.userId = userInfo.userId;
logObject.userName = userInfo.userName;
}
public void sendLog(Dictionary<LogKey, object> payload)
{
setTimeStamp();
logObject.log_level = LogLevel.INFO.ToString();
logObject.payload = payload;
deliverLog(JsonConvert.SerializeObject(logObject));
resetLogObj();
}
private void deliverLog(string logStr)
{
// External API call here
}
private void setTimeStamp()
{
logObject.timestamp = DateTime.UtcNow;
}
private void resetLogObj()
{
logObject.payload = null;
}
}

MVC global exceptions

I am coding an MVC 5 internet application, and I have a question in regards to handling exceptions globally.
I have my Application_Error setup in my global.asax file. This caters to errors such as 404 HttpExceptions.
How can I send all errors that occur in a controller to the Application_Error function? An example is the following exception:
System.Web.HttpRequestValidationException: A potentially dangerous
Request.Form value was detected from the client (name="").
I have written a OnException(ExceptionContext filterContext) for my controller, but am not sure on how to get the Application_Error function to handle these errors. Do I need to pass the exception from the OnException function, or is this the wrong approach?
Thanks in advance.
You can create a global filter by adding the following class to your App_Start folder:-
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
}
HandleErrorAttribute can be replaced with your own custom Exception Filter.
All you then need to do is make sure you add the following line of code to the App_Start method of your Gloabal.asax :-
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
//AreaRegistration.RegisterAllAreas();
//RouteConfig.RegisterRoutes(RouteTable.Routes);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
}
}
Hope this helps.
I'm using some kind of http-module which gives me exactly what you are asking for:
public class MyModule : IHttpModule {
public void Init(HttpApplication context) {
context.Error += OnRequestError;
}
private void OnRequestError(object sender, EventArgs e) {
var context = ((HttpApplication)sender).Context;
var error = context.Error;
if (error == null)
return;
var errorType = error.GetType();
if (errorType == typeof(HttpException))
// do something
// this is what you are looking for
if (errorType = typeof(HttpRequestValidationException))
// do something, whatever you want
// works for me, so should work to you too
}
}
To get the module to work, you can use web.config or DynamicModuleHelper:
Install Microsoft.Web.Infrastructure and WebActivatorEx via nuget
Add a Bootstrapper class to your project
Register module at PreApplicationStartMethod
Sample:
// File: Bootstrapper.cs (contains class Bootstrapper)
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using WebActivatorEx;
using WhatEver.It.Is;
[assembly: PreApplicationStartMethod(typeof(Bootstrapper), "Bootstrap")]
namespace WhatEver.It.Is {
public class Bootstrapper {
public static void Bootstrap() {
// Do what do you need just before the application get started
// like registering modules, etc...
DynamicModuleUtility.RegisterModule(typeof(MyModule));
}
}
}

Categories