Skip to content

Instantly share code, notes, and snippets.

@tonesandtones
Created January 18, 2018 07:39
Show Gist options
  • Save tonesandtones/8f45ab89e90d4474b108a35898a0d636 to your computer and use it in GitHub Desktop.
Save tonesandtones/8f45ab89e90d4474b108a35898a0d636 to your computer and use it in GitHub Desktop.
Hangfire stats collector Azure Function
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Flurl.Http;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.Extensibility;
using Newtonsoft.Json;
namespace HangfireStatsCollector
{
public static class HangfireStatsCollector
{
private static readonly string key;
private static readonly TelemetryClient telemetry;
static HangfireStatsCollector()
{
key = ConfigurationManager.AppSettings["HangfireMetricsAppInsightsSinkKey"];
if (!string.IsNullOrEmpty(key))
{
TelemetryConfiguration.Active.InstrumentationKey = key;
telemetry = new TelemetryClient() { InstrumentationKey = key };
}
}
[FunctionName("HangfireStatsCollector")]
public static async Task Run(
[TimerTrigger("0 */1 * * * *", RunOnStartup = true)] TimerInfo myTimer,
TraceWriter log)
{
log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
if (telemetry == null)
{
log.Error("No App Insights instrumentation key available. Cannot publish metrics.");
return;
}
IList<string> paramsToFetch = new List<string>
{
"enqueued:count-or-null",
"retries:count",
"recurring:count",
"servers:count",
"succeeded:count",
"failed:count"
};
string ps = "metrics[]=" + string.Join("&metrics[]=", paramsToFetch);
string statsUrl = ConfigurationManager.AppSettings["HangfireStatsUrl"];
if (string.IsNullOrEmpty(statsUrl))
{
log.Error("No Hangfire stats target URL specified. Cannot fetch metrics");
return;
}
HangfireStatsWrapper stats;
try
{
stats =
await statsUrl
.PostAsync(
new StringContent(ps, Encoding.UTF8, "application/x-www-form-urlencoded"))
.ReceiveJson<HangfireStatsWrapper>();
}
catch (Exception e)
{
log.Error("Unexpected exception", e);
telemetry.TrackException(e);
throw;
}
log.Info($"Got Hangfire stats: Succeeded = {stats.Succeeded.Value}, Retries = {stats.Retries.Value}, Failed = {stats.Failed.Value}");
if (stats.Retries.ValueInt.HasValue) telemetry.TrackMetric("Hangfire-Retries", stats.Retries.ValueInt.Value);
if (stats.Succeeded.ValueInt.HasValue) telemetry.TrackMetric("Hangfire-Succeeded", stats.Succeeded.ValueInt.Value);
if (stats.Failed.ValueInt.HasValue) telemetry.TrackMetric("Hangfire-Failed", stats.Failed.ValueInt.Value);
if (stats.Servers.ValueInt.HasValue) telemetry.TrackMetric("Hangfire-Servers", stats.Servers.ValueInt.Value);
telemetry.Flush();
}
}
public class HangfireStatsWrapper
{
[JsonProperty("enqueued:count-or-null")]
public HangfireStat Enqueued { get; set; }
[JsonProperty("retries:count")]
public HangfireStat Retries { get; set; }
[JsonProperty("recurring:count")]
public HangfireStat Recurring { get; set; }
[JsonProperty("servers:count")]
public HangfireStat Servers { get; set; }
[JsonProperty("succeeded:count")]
public HangfireStat Succeeded { get; set; }
[JsonProperty("failed:count")]
public HangfireStat Failed { get; set; }
}
public class HangfireStat
{
public string Value { get; set; }
[JsonIgnore]
public int? ValueInt
{
get
{
if (int.TryParse(Value, NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var valueResult))
{
return valueResult;
}
else
{
return null;
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment