Created
April 17, 2014 20:11
-
-
Save johndobrien/11008774 to your computer and use it in GitHub Desktop.
Statsd metrics
This file contains hidden or 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.Configuration; | |
using System.Diagnostics; | |
using System.Linq; | |
using System.Net; | |
using System.Net.Sockets; | |
using Common.Logging; | |
namespace Common.Metrics | |
{ | |
public static class Metrics | |
{ | |
private static bool _clientError; | |
public static MetricsConfigurationSection Config | |
{ | |
get | |
{ | |
return (MetricsConfigurationSection)ConfigurationManager.GetSection("metrics"); | |
} | |
} | |
private static IPEndPoint _hostEndPoint; | |
private static IPEndPoint HostEndPoint | |
{ | |
get | |
{ | |
if (_hostEndPoint == null) | |
{ | |
var ip = Dns.GetHostEntry((string) Config.Server.Host).AddressList[0]; | |
_hostEndPoint = new IPEndPoint(ip, Config.Server.Port); | |
return _hostEndPoint; | |
} | |
return _hostEndPoint; | |
} | |
} | |
private static UdpClient _udpClient; | |
private static UdpClient Client | |
{ | |
get | |
{ | |
if (_udpClient == null) | |
{ | |
_udpClient = new UdpClient(); | |
_udpClient.Connect(HostEndPoint); | |
} | |
return _udpClient; | |
} | |
} | |
/// <summary> | |
/// Sends timing statistics. | |
/// </summary> | |
/// <param name="stat">Name of statistic being updated.</param> | |
/// <param name="time">The timing it took to complete.</param> | |
/// <param name="sample_rate">Tells StatsD how often to sample this value. Defaults to 1 (send all values).</param> | |
/// <param name="callback">A callback for when the send is complete. Defaults to null.</param> | |
public static void Timing(string stat, long time, double sample_rate = 1, AsyncCallback callback = null) | |
{ | |
if (_clientError || !Config.Enabled) return; | |
var data = new Dictionary<string, string> { { stat, string.Format("{0}|ms", time) } }; | |
Send(data, sample_rate, callback); | |
} | |
/// <summary> | |
/// Increments a counter | |
/// </summary> | |
/// <param name="stat">Name of statistic being updated.</param> | |
/// <param name="sample_rate">Tells StatsD how often to sample this value. Defaults to 1 (send all values).</param> | |
/// <param name="callback">A callback for when the send is complete. Defaults to null.</param> | |
public static void Increment(string stat, double sample_rate = 1, AsyncCallback callback = null) | |
{ | |
if (_clientError || !Config.Enabled) return; | |
UpdateStats(stat, 1, sample_rate, callback); | |
} | |
/// <summary> | |
/// Decrements a counter | |
/// </summary> | |
/// <param name="stat">Name of statistic being updated.</param> | |
/// <param name="sample_rate">Tells StatsD how often to sample this value. Defaults to 1 (send all values).</param> | |
/// <param name="callback">A callback for when the send is complete. Defaults to null.</param> | |
public static void Decrement(string stat, double sample_rate = 1, AsyncCallback callback = null) | |
{ | |
if (_clientError || !Config.Enabled) return; | |
UpdateStats(stat, -1, sample_rate, callback); | |
} | |
/// <summary> | |
/// Updates a counter by an arbitrary amount | |
/// </summary> | |
/// <param name="stat">Name of statistic being updated.</param> | |
/// <param name="value">The value of the metric.</param> | |
/// <param name="sample_rate">Tells StatsD how often to sample this value. Defaults to 1 (send all values).</param> | |
/// <param name="callback">A callback for when the send is complete. Defaults to null.</param> | |
public static void Gauge(string stat, int value, double sample_rate = 1, AsyncCallback callback = null) | |
{ | |
if (_clientError || !Config.Enabled) return; | |
var data = new Dictionary<string, string> { { stat, string.Format("{0}|g", value) } }; | |
Send(data, sample_rate, callback); | |
} | |
/// <summary> | |
/// Updates a counter by an arbitrary amount | |
/// </summary> | |
/// <param name="stat">Name of statistic(s) being updated.</param> | |
/// <param name="delta">The amount to adjust the counter</param> | |
/// <param name="sample_rate">Tells StatsD how often to sample this value. Defaults to 1 (send all values).</param> | |
/// <param name="callback">A callback for when the send is complete. Defaults to null.</param> | |
public static void UpdateStats(string stat, int delta = 1, double sample_rate = 1, AsyncCallback callback = null) | |
{ | |
if (_clientError || !Config.Enabled) return; | |
var dictionary = new Dictionary<string, string> { { stat, string.Format("{0}|c", delta) } }; | |
Send(dictionary, sample_rate, callback); | |
} | |
private static readonly Random _random = new Random(); | |
private static void Send(Dictionary<string, string> data, double sample_rate, AsyncCallback callback) | |
{ | |
if (sample_rate < 1) | |
{ | |
var nextRand = _random.NextDouble(); | |
if (nextRand <= sample_rate) | |
{ | |
data = data.Keys.ToDictionary(stat => stat, | |
stat => string.Format("{0}|@{1}", data[stat], sample_rate)); | |
} | |
} | |
SendToStatsD(data, callback); | |
} | |
private static void SendToStatsD(Dictionary<string, string> sampled_data, AsyncCallback callback) | |
{ | |
var prefix = Config.Server.Prefix; | |
foreach (var send_data in from stat in sampled_data.Keys | |
let encoding = new System.Text.ASCIIEncoding() | |
let stat_string = string.Format("{0}.{1}:{2}", prefix, stat, sampled_data[stat]) | |
select encoding.GetBytes(stat_string)) | |
{ | |
if (!_clientError) | |
{ | |
try | |
{ | |
Client.BeginSend(send_data, send_data.Length, callback, null); | |
} | |
catch | |
{ | |
Logger.Error("Metrics Disabled due to client Error."); | |
_clientError = true; | |
break; | |
} | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment