This article will show you how easy it is to log something to DataDog from Azure Function using a standard ILogger interface from Microsoft.Extensions.Logging.Abstractions NuGet package and Serilog logging library.

First of all, let's check our function:

[assembly: WebJobsStartup(typeof(Startup))]
namespace IvanDerevianko.AzureFunction
{
    public static class SomeFunction
    {
        [FunctionName("SomeFunction")]
        public static async Task Run([TimerTrigger("0 */1 * * * *")]TimerInfo myTimer, ILogger log, CancellationToken token)
        {
            log.LogInformation($"{nameof(SomeFunction)} function executing at: {DateTime.UtcNow}");

            await someService.DoLogicAsync(token);

            log.LogInformation($"{nameof(SomeFunction)} function finished at: {DateTime.UtcNow}");
        }
    }
}

As you can see, we have specified the WebJobsStartup assembly attribute. By using this attribute we can define custom Startup class that will define an action that should be performed as part of a host startup. But we will get to it a bit later.

Let's install the necessary NuGet packages:

Install-Package Serilog
Install-Package Serilog.Sinks.Datadog.Logs
Install-Package Serilog.Extensions.Logging

Now we need to add Serilog logger to the DI container. Do to that, create a Startup class that implements IWebJobsStartup interface (the one we used in the WebJobsStartup attribute).

using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Serilog.Events;
using Serilog.Sinks.Datadog.Logs;

namespace IvanDerevianko.AzureFunction
{
	public class Startup : IWebJobsStartup
	{
		public void Configure(IWebJobsBuilder builder)
		{
			ConfigureServices(builder.Services).BuildServiceProvider(true);
		}

		private IServiceCollection ConfigureServices(IServiceCollection services)
		{
			services
				.AddLogging(loggingBuilder =>
					loggingBuilder.AddSerilog(
						new LoggerConfiguration()
							.WriteTo.DatadogLogs(
								apiKey: "REPLACE - DataDog API Key",
								host: Environment.MachineName,
								source: "REPLACE - Log-Source",
								service: GetServiceName(),
								configuration: new DatadogConfiguration(),
								logLevel: LogEventLevel.Infomation
							)
							.CreateLogger())
				);

			return services;
		}

		private string GetServiceName()
		{
			var service = Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME");

			if (string.IsNullOrEmpty(service))
			{
				service = "LocalService";
			}

			return service;
		}
	}
}

This should be familiar to people who worked with ASP.NET Core before. Here we can register custom classes in the service collection. This is essential part:

services
	.AddLogging(loggingBuilder =>
		loggingBuilder.AddSerilog(
			new LoggerConfiguration()
				.WriteTo.DatadogLogs(
					apiKey: "REPLACE - DataDog API Key",
					host: Environment.MachineName,
					source: "REPLACE - Log-Source",
					service: GetServiceName(),
					configuration: new DatadogConfiguration(),
					logLevel: LogEventLevel.Infomation
				)
				.CreateLogger())
	);

Here we add new Serilog to the service collection and using DataDog sink.

Do not forget to replace apiKey value with the one you get from DataDog and, if needed, change the logLevel to not span DataDog with unnecessary logs from the Azure Function host.