Categories
App Service Azure ErrorTrigger extension Microsoft WebJobs

Monitoring errors in Azure WebJobs with the ErrorTrigger extension

We will learn in a simple scenario how to monitor errors in an Azure WebJob.

When developing jobs with Azure WebJob, it’s a good practice to implement error monitoring in case something goes wrong when a job is executed.

The WebJobs ErrorTrigger extension, part of the Core extensions, will help us achieve that.

 

Today we will learn how to monitor errors with Azure WebJob using the ErrorTrigger extension. As a base, we will create a project with the Azure WebJob template in Visual Studio.

 

Creation

The first thing you will need is to add the following NuGet package to your project: Microsoft.Azure.WebJobs.Extensions

 

To enable the ErrorTrigger extension in your WebJob you will need to configure the JobHostConfiguration like the following:

using Microsoft.Azure.WebJobs;

namespace AzureWebJobs.ErrorTriggerExtension
{
	// To learn more about Microsoft Azure WebJobs SDK, please see https://go.microsoft.com/fwlink/?LinkID=320976
	class Program
	{
		// Please set the following connection strings in app.config for this WebJob to run:
		// AzureWebJobsDashboard and AzureWebJobsStorage
		static void Main()
		{
			var config = new JobHostConfiguration();

			if (config.IsDevelopment)
			{
				config.UseDevelopmentSettings();
			}

			config.UseCore();

			var host = new JobHost(config);
			// The following code ensures that the WebJob will be running continuously
			host.RunAndBlock();
		}
	}
}

The important part here is to call the extension named UseCore. This will enable the ErrorTrigger extension.

 

Now we will create two functions ProcessQueueAMessage and ProcessQueueBMessage using the default ProcessQueueMessage function that uses a QueueTrigger.

Both will call the ProcessMessage method. This method checks if the message is empty. If it is, an exception will be thrown.

ProcessQueueAMessage will simply call ProcessMessage, ProcessQueueBMessage will also call it but catching exceptions and logging them using the TraceWriter.

Then we create a function named GlobalErrorMonitor, giving the following code:

using System;
using System.IO;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions;
using Microsoft.Azure.WebJobs.Host;

namespace AzureWebJobs.ErrorTriggerExtension
{
	public class Functions
	{
		// This function will get triggered/executed when a new message is written 
		// on an Azure Queue called queue.
		public static void ProcessQueueAMessage([QueueTrigger("queuea")] string message, TextWriter log)
		{
			log.WriteLine(message);

			ProcessMessage(message);
		}

		public static void ProcessQueueBMessage([QueueTrigger("queueb")] string message, TraceWriter logger)
		{
			logger.Info(message);

			try
			{
				ProcessMessage(message);
			}
			catch (Exception ex)
			{
				logger.Error($"An error occurred in: '{nameof(ProcessQueueBMessage)}'", ex, nameof(Functions));
			}
		}

		/// <summary>
		/// Triggered when an error is reported in other functions.
		/// Called whenever 2 errors occur within a 3 minutes sliding window (throttled at a maximum of 2 notifications per 10 minutes).
		/// </summary>
		public static void GlobalErrorMonitor([ErrorTrigger("0:03:00", 2, Throttle = "0:10:00")] TraceFilter filter, TextWriter log)
		{
			Console.Error.WriteLine("An error has been detected in a function.");

			log.WriteLine(filter.GetDetailedMessage(1));
		}

		private static void ProcessMessage(string message)
		{
			if (string.IsNullOrEmpty(message))
			{
				throw new ArgumentNullException(nameof(message));
			}

			//Do some work here...
		}
	}
}

You can notice two things here:

  • We use the ErrorTrigger attribute to be able to use ErrorTrigger extension in the GlobalErrorMonitor function.
  • The error trigger is configured to be called whenever 2 errors occur within a 3 minutes sliding window (throttled at a maximum of 2 notifications per 10 minutes.

 

Example of use

Once the WebJob ready and properly configured, we will launch it:

Found the following functions:
AzureWebJobs.ErrorTriggerExtension.Functions.ProcessQueueAMessage
AzureWebJobs.ErrorTriggerExtension.Functions.ProcessQueueBMessage
AzureWebJobs.ErrorTriggerExtension.Functions.GlobalErrorMonitor
Job host started

Now add an empty message to the queuea. The ProcessQueueAMessage function will be called 5 times and the message will be moved to the queuea-poison.

After the second exception you can notice that GlobalErrorMonitor is triggered:

Executing 'Functions.ProcessQueueAMessage' (Reason='New queue message detected on 'queuea'.', Id=8ee22555-15df-458e-8a0e-cdd1b9b2a97a)
Executing 'Functions.GlobalErrorMonitor' (Reason='Error trigger fired', Id=8b368696-7dab-4908-af53-18018abd8f3e)
An error has been detected in a function.
2 events at level 'Error' or lower have occurred within time window 00:03:00.

04/11/2017 23:29:43 Error Exception while executing function: Functions.ProcessQueueAMessage WebJobs.Execution Microsoft.Azure.WebJobs.Host.FunctionInvocationException: Exception while executing function: Functions.ProcessQueueAMessage ---> System.ArgumentNullException: Value cannot be null.
Parameter name: message
 at AzureWebJobs.ErrorTriggerExtension
 ...

Executed 'Functions.GlobalErrorMonitor' (Succeeded, Id=8b368696-7dab-4908-af53-18018abd8f3e)
Executed 'Functions.ProcessQueueBMessage' (Succeeded, Id=8ee22555-15df-458e-8a0e-cdd1b9b2a97a)

 

Now restart the WebJob and add an empty message to the queueb. The ProcessQueueBMessage function will be called once as we are catching and logging the ArgumentNullException.

Notice that the GlobalErrorMonitor is not executed as it is configured to be triggered when at least 2 errors occur.

Now add another empty message to the queueb and you will now notice that GlobalErrorMonitor is triggered:

Executing 'Functions.ProcessQueueBMessage' (Reason='New queue message detected on 'queueb'.', Id=9ba82a41-fe9c-4227-a306-9af2dc9db7c2)
Executing 'Functions.GlobalErrorMonitor' (Reason='Error trigger fired', Id=03a3c8ea-16cf-46e9-a6c5-074a4f47c997)
An error has been detected in a function.
2 events at level 'Error' or lower have occurred within time window 00:03:00.

04/11/2017 23:43:05 Error An error occurred in: 'ProcessQueueBMessage' Functions System.ArgumentNullException: Value cannot be null.
Parameter name: message
 at AzureWebJobs.ErrorTriggerExtension
 ...

Executed 'Functions.GlobalErrorMonitor' (Succeeded, Id=03a3c8ea-16cf-46e9-a6c5-074a4f47c997)
Executed 'Functions.ProcessQueueBMessage' (Succeeded, Id=9ba82a41-fe9c-4227-a306-9af2dc9db7c2)

 

To go further

Here we have setup a global error handler and you could setup more handlers, but you can also have function specific error handlers.

To create a function specific handler use the naming convention based on the “ErrorHandler” suffix.

For example, if we want to have a specific handler for the function ProcessQueueAMessage we will would create the following:

public static void ProcessQueueAMessageErrorHandler([ErrorTrigger("0:03:00", 2, Throttle = "0:10:00")] TraceFilter filter, TextWriter log)

In that case only errors coming from the function ProcessQueueAMessage will trigger the ErrorTrigger extension.

 

Summary

We have learned how to monitor errors with Azure WebJob using the ErrorTrigger extension.

 

You can download the example solution here:

Download full sources

Or

Browse the GitHub repository

(Note that the project uses Microsoft.Azure.WebJobs version 2.0.0)

 

Please feel free to comment or contact me if you have any question about this article.

Categories
Azure Microsoft SendGrid extension WebJobs

How to send emails with SendGrid in Azure WebJobs using the SendGrid extension

Do you want to send emails in an Azure WebJob? In a simple scenario we will discover how to do it with SendGrid.

When you need to send emails from an Azure WebJob, the following extension is very helpful:

WebJobs SendGrid extension

Today we will take a look at the version 2.0 that came along with the release of the Azure WebJobs SDK 2.0.

In this article, we will discover how to send a simple email from an Azure WebJob with the SenGrid extension. As a base, we will create a project with the Azure WebJob template in Visual Studio.

 

Creation

The first thing you will need is to add the following NuGet package to your project: Microsoft.Azure.WebJobs.Extensions.SendGrid

 

To enable the SendGrid extension in your WebJob you will need to configure the JobHostConfiguration like the following:

using Microsoft.Azure.WebJobs;

namespace AzureWebJobs.SendGridExtension
{
	// To learn more about Microsoft Azure WebJobs SDK, please see https://go.microsoft.com/fwlink/?LinkID=320976
	class Program
	{
		// Please set the following connection strings in app.config for this WebJob to run:
		// AzureWebJobsDashboard and AzureWebJobsStorage
		static void Main()
		{
			var config = new JobHostConfiguration();

			if (config.IsDevelopment)
			{
				config.UseDevelopmentSettings();
			}

			config.UseSendGrid();

			var host = new JobHost(config);
			// The following code ensures that the WebJob will be running continuously
			host.RunAndBlock();
		}
	}
}

The important part here is to call the extension named UseSendGrid. This will enable the SendGrid extension.

 

We will now add a new application settings to be able to connect to SendGrid:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <!-- The format of the connection string is "DefaultEndpointsProtocol=https;AccountName=NAME;AccountKey=KEY" -->
    <!-- For local execution, the value can be set either in this config file or through environment variables -->
    <add name="AzureWebJobsDashboard" connectionString="" />
    <add name="AzureWebJobsStorage" connectionString="" />
  </connectionStrings>
  <appSettings>
    <add key="AzureWebJobsSendGridApiKey" value="" />
  </appSettings>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
  </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Azure.KeyVault.Core" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.WindowsAzure.Storage" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-8.1.1.0" newVersion="8.1.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>
  • AzureWebJobsSendGridApiKey: an API Key related to your SendGrid account.

 

Now using the default ProcessQueueMessage function that uses a QueueTrigger, we want to send an email when a message is added to the queue:

using System.IO;
using Microsoft.Azure.WebJobs;
using SendGrid.Helpers.Mail;

namespace AzureWebJobs.SendGridExtension
{
	public class Functions
	{
		// This function will get triggered/executed when a new message is written 
		// on an Azure Queue called queue.
		public static void ProcessQueueMessage([QueueTrigger("queue")] string message, TextWriter log, [SendGrid(From = "no-reply@anydomainxyz.com", To = "anybody@anydomainxyz.com")] out Mail mail)
		{
			log.WriteLine(message);

			mail = new Mail();

			mail.Subject = "WebJob - Queue message processed successfully";
			mail.AddContent(new Content("text/plain", $"The message '{message}' was successfully processed."));
		}
	}
}

You can notice several things here:

  • We use the SendGrid attribute to be able to use SendGrid in the function.
  • The out contextual keyword with the mail parameter allow us to pass the email to the extension that will then send it to SendGrid.
  • The Mail object is coming directly from the SendGrid library.
  • Update the To address, so you can receive the emails.

 

Example of use

Once the WebJob ready and properly configured, we will launch it and add a message to the queue:

Found the following functions:
AzureWebJobs.SendGridExtension.Functions.ProcessQueueMessage
Job host started
Executing 'Functions.ProcessQueueMessage' (Reason='New queue message detected on 'queue'.', Id=ef4a15bf-ce79-4be1-8b56-a3237b929b50)
My queue test message
Executed 'Functions.ProcessQueueMessage' (Succeeded, Id=ef4a15bf-ce79-4be1-8b56-a3237b929b50)

If everything goes well, you should see the same kind of message in console output as above.

You should also receive an email to the address you’ve specified in the To property of the SendGrid attribute. The title will be as WebJob – Queue message processed successfully, and the body will contain the message coming from the queue like the following: The message ‘My queue test message’ was successfully processed.

 

To go further

If you want to send more than one email, we can use the WebJob IAsyncCollector like the following:

public static async Task ProcessQueueMessage([QueueTrigger("queue")] string message, TextWriter log, [SendGrid(From = "no-reply@anydomainxyz.com", To = "anybody@anydomainxyz.com")] IAsyncCollector mails)

There are many other ways to send emails from the extension, more details can be found here:

SendGrid Extension Samples

 

Summary

We have seen how to send emails from an Azure WebJob using the SendGrid extension.

 

You can download the example solution here:

Download full sources

Or

Browse the GitHub repository

(Note that the project uses Microsoft.Azure.WebJobs version 2.0.0)

 

Please feel free to comment or contact me if you have any question about this article.