Skip to content

Instantly share code, notes, and snippets.

@johndobrien
Created April 17, 2014 20:11
Show Gist options
  • Save johndobrien/11008774 to your computer and use it in GitHub Desktop.
Save johndobrien/11008774 to your computer and use it in GitHub Desktop.
Statsd metrics
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