Last active
March 26, 2021 08:36
-
-
Save marcduiker/f15143b79efea6a74fdf17385bd7a0d7 to your computer and use it in GitHub Desktop.
Custom Application Insights Availability Metrics for a Function App
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Threading.Tasks; | |
using Azure.HealthCheck; | |
using Microsoft.ApplicationInsights.Extensibility; | |
using Microsoft.Extensions.Logging; | |
namespace FunctionApp.Application.HealthCheck | |
{ | |
public class FunctionAppHealthCheck : HealthCheckBase | |
{ | |
private readonly IHealthCheckSettings _healthCheckSettings; | |
public FunctionAppHealthCheck(IHealthCheckSettings healthCheckSettings, | |
TelemetryConfiguration telemetryConfiguration, | |
ILogger<FunctionAppHealthCheck> logger) : base(telemetryConfiguration, logger) | |
{ | |
_healthCheckSettings = healthCheckSettings; | |
AddAvailabilityChecks(); | |
SetHealthCheckName(nameof(FunctionAppHealthCheck)); | |
} | |
protected override void AddAvailabilityChecks() | |
{ | |
var cosmosDBAction = new Func<Task>(() => CheckCosmosDBAsync()); | |
var blobStorageAction = new Func<Task>(() => CheckJsonBlobStorageAsync()); | |
AvailabilityChecks.Add(cosmosDBAction); | |
AvailabilityChecks.Add(blobStorageAction); | |
} | |
private async Task CheckCosmosDBAsync() | |
{ | |
await _healthCheckSettings.DataCosmosContainer.ReadContainerAsync(); | |
LogHealthInformation("CosmosDB data collection is healthy."); | |
} | |
private Task CheckJsonBlobStorageAsync() | |
{ | |
if (!_healthCheckSettings.JsonBlobContainerClient.Exists()) | |
{ | |
throw new BlobContainerNotFoundException($"BlobContainer named { _healthCheckSettings.JsonBlobContainerClient.Name } is not available."); | |
} | |
LogHealthInformation("BlobStorage JSON container is healthy."); | |
return Task.CompletedTask; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.Linq; | |
using System.Reflection; | |
using System.Threading.Tasks; | |
using Microsoft.ApplicationInsights; | |
using Microsoft.ApplicationInsights.DataContracts; | |
using Microsoft.ApplicationInsights.Extensibility; | |
using Microsoft.Extensions.Logging; | |
namespace Azure.HealthCheck | |
{ | |
public abstract class HealthCheckBase : IHealthCheck | |
{ | |
private const string HealthCheckLogFormat = "{ApplicationHealthCheck} - {Message}"; | |
private readonly TelemetryClient _telemetryClient; | |
private readonly ILogger _logger; | |
private readonly string _location; | |
private string _healthCheckClass; | |
protected HealthCheckBase( | |
TelemetryConfiguration telemetryConfiguration, | |
ILogger<HealthCheckBase> logger) | |
{ | |
_telemetryClient = new TelemetryClient(telemetryConfiguration); | |
_logger = logger; | |
// REGION_NAME is a default environment variable that comes with App Services. | |
_location = Environment.GetEnvironmentVariable("REGION_NAME") ?? "local"; | |
AvailabilityChecks = new List<Func<Task>>(); | |
} | |
/// <summary> | |
/// Gets or sets the functions which check the availability of the dependencies of the application. | |
/// </summary> | |
protected List<Func<Task>> AvailabilityChecks { get; private set; } | |
/// <summary> | |
/// Set the name of the HealthCheck which will be visible in Application Insights. | |
/// </summary> | |
protected void SetHealthCheckName(string healthCheckClassName) | |
{ | |
_healthCheckClass = healthCheckClassName; | |
} | |
/// <summary> | |
/// Method to be implemented by the class that inherits from this class to add items to the AvailabilityChecks list. | |
/// </summary> | |
protected abstract void AddAvailabilityChecks(); | |
public async Task RunHealthCheckAsync() | |
{ | |
if (string.IsNullOrEmpty(_healthCheckClass)) | |
{ | |
_healthCheckClass = nameof(HealthCheckBase); | |
_logger.LogWarning(HealthCheckLogFormat, _healthCheckClass, "HealthCheckClass name is not set."); | |
} | |
string operationId = Guid.NewGuid().ToString("N"); | |
var availability = new AvailabilityTelemetry | |
{ | |
Id = operationId, | |
Name = _healthCheckClass, | |
RunLocation = _location, | |
Success = false | |
}; | |
var stopwatch = new Stopwatch(); | |
stopwatch.Start(); | |
try | |
{ | |
await RunAvailabilityChecksAsync(); | |
availability.Success = true; | |
} | |
catch (Exception exception) | |
{ | |
availability.Message = exception.Message; | |
_logger.LogError(HealthCheckLogFormat, _healthCheckClass, exception.Message); | |
TrackException(_location, operationId, exception); | |
} | |
finally | |
{ | |
stopwatch.Stop(); | |
availability.Duration = stopwatch.Elapsed; | |
availability.Timestamp = DateTimeOffset.UtcNow; | |
_telemetryClient.TrackAvailability(availability); | |
_telemetryClient.Flush(); | |
} | |
} | |
protected void LogHealthInformation(string message) | |
{ | |
_logger.LogInformation(HealthCheckLogFormat, _healthCheckClass, message); | |
} | |
private async Task RunAvailabilityChecksAsync() | |
{ | |
if (!AvailabilityChecks.Any()) | |
{ | |
_logger.LogWarning(HealthCheckLogFormat, _healthCheckClass, "Availability checks are missing."); | |
} | |
foreach (Func<Task> func in AvailabilityChecks) | |
{ | |
await func.Invoke(); | |
} | |
} | |
private void TrackException(string location, string operationId, Exception exception) | |
{ | |
var exceptionTelemetry = new ExceptionTelemetry(exception); | |
exceptionTelemetry.Context.Operation.Id = operationId; | |
exceptionTelemetry.Properties.Add("HealthCheck", _healthCheckClass); | |
exceptionTelemetry.Properties.Add("Location", location); | |
_telemetryClient.TrackException(exceptionTelemetry); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Threading.Tasks; | |
using Azure.HealthCheck; | |
using Microsoft.Azure.WebJobs; | |
namespace FunctionApp.Application.HealthCheck | |
{ | |
public class HealthCheckTimerTrigger | |
{ | |
private readonly IHealthCheck _healthCheck; | |
public HealthCheckTimerTrigger( | |
IHealthCheck healthCheck) | |
{ | |
_healthCheck = healthCheck; | |
} | |
[FunctionName(nameof(HealthCheckTimerTrigger))] | |
public async Task RunAsync( | |
[TimerTrigger("%HealthCheckInterval%")] TimerInfo timer) | |
{ | |
await _healthCheck.RunHealthCheckAsync(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"version": "2.0", | |
"logging": { | |
"applicationInsights": { | |
"samplingExcludedTypes": "Request", | |
"samplingSettings": { | |
"isEnabled": false | |
} | |
}, | |
"logLevel": { | |
"FunctionApp.Application.HealthCheck.FunctionAppHealthCheck": "Information" | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Threading.Tasks; | |
namespace Azure.HealthCheck | |
{ | |
public interface IHealthCheck | |
{ | |
/// <summary> | |
/// Starts the health check of the application. | |
/// </summary> | |
/// <returns></returns> | |
Task RunHealthCheckAsync(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Azure.Storage.Blobs; | |
namespace FunctionApp.Application.HealthCheck | |
{ | |
public interface IHealthCheckSettings | |
{ | |
/// <summary> | |
/// Gets and sets the DataCosmosContainer to check is the data collection exists. | |
/// </summary> | |
public ICosmosContainer DataCosmosContainer { get; set; } | |
/// <summary> | |
/// Gets and sets the JsonBlobContainerClient to check if the blob container exists. | |
/// </summary> | |
BlobContainerClient JsonBlobContainerClient { get; set; } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment