Skip to content

Commit

Permalink
Added metrics to record system startup and configuration. Added addit…
Browse files Browse the repository at this point in the history
…ional error tracking for the TCP listener.
  • Loading branch information
lukevenediger committed Nov 28, 2013
1 parent 97bc146 commit 58d5fb7
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 59 deletions.
6 changes: 5 additions & 1 deletion statsd.net-Tests/StatsdTestSuite.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using statsd.net;
using statsd.net.Framework;
using statsd.net.shared;
using statsd.net.shared.Messages;
using statsd.net.shared.Services;
using statsd.net_Tests.Infrastructure;
using System;
using System.Collections.Generic;
Expand All @@ -21,6 +23,7 @@ public abstract class StatsdTestSuite
protected StatsdClient.Statsd _client;
protected ControllableIntervalService _intervalService;
protected OutputBufferBlock<GraphiteLine> _outputBlock;
protected ISystemMetricsService _systemMetrics;

[TestInitialize]
public void Setup()
Expand All @@ -33,7 +36,8 @@ public void Setup()
_outputBlock = new OutputBufferBlock<GraphiteLine>();
_client = new StatsdClient.Statsd("", 0, outputChannel : new InAppListenerOutputChannel(_listener));
_statsd.AddListener(_listener);
_statsd.AddBackend(_backend);
_statsd.AddBackend(_backend, _systemMetrics, "testing");
_systemMetrics = new Mock<ISystemMetricsService>().Object;
}

[TestCleanup]
Expand Down
104 changes: 68 additions & 36 deletions statsd.net.shared/Listeners/HttpStatsListener.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,29 @@ public void OnRequest(HttpRequestHead head,
{
if (head.Method.ToUpperInvariant() == "POST")
{
ProcessPOSTRequest(body, response);
}
else if (head.Method.ToUpperInvariant() == "GET" && head.Uri == "/crossdomain.xml")
{
ProcessCrossDomainRequest(body, response);
}
else if (head.Method.ToUpperInvariant() == "GET" && head.QueryString.Contains("metrics"))
{
ProcessGETRequest(body, head, response);
}
else if (head.Method.ToUpperInvariant() == "GET" && head.Uri == "/")
{
ProcessLoadBalancerRequest(body, response);
}
else
{
ProcessFileNotFound(body, response);
}
}


private void ProcessPOSTRequest(IDataProducer body, IHttpResponseDelegate response)
{
body.Connect(new BufferedConsumer(
(payload) =>
{
Expand All @@ -106,61 +129,70 @@ public void OnRequest(HttpRequestHead head,
{
Respond(response, "500 Internal server error");
}));
}
else if (head.Method.ToUpperInvariant() == "GET" && head.Uri == "/crossdomain.xml")
}

private void ProcessCrossDomainRequest(IDataProducer body, IHttpResponseDelegate response)
{
var responseHead = new HttpResponseHead()
{
var responseHead = new HttpResponseHead()
{
Status = "200 OK",
Headers = new Dictionary<string, string>
Status = "200 OK",
Headers = new Dictionary<string, string>
{
{ "Content-Type", "application-xml" },
{ "Content-Length", Encoding.UTF8.GetByteCount(FLASH_CROSSDOMAIN).ToString() },
{ "Access-Control-Allow-Origin", "*"}
}
};
response.OnResponse(responseHead, new BufferedProducer(FLASH_CROSSDOMAIN));
}
else if (head.Method.ToUpperInvariant() == "GET" && head.QueryString.Contains("metrics"))
{
var qs = head.QueryString.Split(new string[] { "&" }, StringSplitOptions.RemoveEmptyEntries)
.Select(p => p.Split(new string[] { "=" }, StringSplitOptions.None))
.ToDictionary(p => p[0], p => HttpUtility.UrlDecode(p[1]));
};
response.OnResponse(responseHead, new BufferedProducer(FLASH_CROSSDOMAIN));
}

string[] lines = qs["metrics"].Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
for (int index = 0; index < lines.Length; index++)
{
_parent._target.Post(lines[index]);
}
_parent._systemMetrics.LogCount("listeners.http.lines", lines.Length);
_parent._systemMetrics.LogCount("listeners.http.bytes", Encoding.UTF8.GetByteCount(qs["metrics"]));
private void ProcessGETRequest(IDataProducer body, HttpRequestHead head, IHttpResponseDelegate response)
{
var qs = head.QueryString.Split(new string[] { "&" }, StringSplitOptions.RemoveEmptyEntries)
.Select(p => p.Split(new string[] { "=" }, StringSplitOptions.None))
.ToDictionary(p => p[0], p => HttpUtility.UrlDecode(p[1]));

var responseHead = new HttpResponseHead()
{
Status = "200 OK",
Headers = new Dictionary<string, string>
string[] lines = qs["metrics"].Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
for (int index = 0; index < lines.Length; index++)
{
_parent._target.Post(lines[index]);
}
_parent._systemMetrics.LogCount("listeners.http.lines", lines.Length);
_parent._systemMetrics.LogCount("listeners.http.bytes", Encoding.UTF8.GetByteCount(qs["metrics"]));

var responseHead = new HttpResponseHead()
{
Status = "200 OK",
Headers = new Dictionary<string, string>
{
{ "Content-Type", "application-xml" },
{ "Content-Length", "0" },
{ "Access-Control-Allow-Origin", "*"}
}
};
response.OnResponse(responseHead, new EmptyResponse());
}
else
};
response.OnResponse(responseHead, new EmptyResponse());
}

private void ProcessLoadBalancerRequest(IDataProducer body, IHttpResponseDelegate response)
{
_parent._systemMetrics.LogCount("listeners.http.loadbalancer");
Respond(response, "200 OK");
}

private void ProcessFileNotFound(IDataProducer body, IHttpResponseDelegate response)
{
_parent._systemMetrics.LogCount("listeners.http.404");
var headers = new HttpResponseHead()
{
var headers = new HttpResponseHead()
{
Status = "404 Not Found",
Headers = new Dictionary<string, string>
Status = "404 Not Found",
Headers = new Dictionary<string, string>
{
{ "Content-Type", "text/plain" },
{ "Content-Length", Encoding.UTF8.GetByteCount("not found").ToString() },
{ "Access-Control-Allow-Origin", "*"}
}
};
response.OnResponse(headers, new BufferedProducer("not found"));
}
};
response.OnResponse(headers, new BufferedProducer("not found"));
}

private void Respond(IHttpResponseDelegate response, string status)
Expand Down
20 changes: 17 additions & 3 deletions statsd.net.shared/Listeners/TcpStatsListener.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ private void ProcessIncomingConnection(TcpClient tcpClient)
_systemMetrics.LogCount("tcp.connection.open");
using (var networkStream = tcpClient.GetStream())
{
// Set an aggressive read timeout
networkStream.ReadTimeout = 1000; /* one second */
var buffer = new byte[4096];
while (!_token.IsCancellationRequested)
{
Expand All @@ -65,6 +67,7 @@ private void ProcessIncomingConnection(TcpClient tcpClient)
{
return;
}
_systemMetrics.LogCount("tcp.reads");
_systemMetrics.LogCount("tcp.bytes", byteCount);
var lines = Encoding.UTF8.GetString(buffer, 0, byteCount).Replace("\r", "").Split('\n');
// Post what we have
Expand All @@ -78,19 +81,30 @@ private void ProcessIncomingConnection(TcpClient tcpClient)
}
}
}
catch (SocketException)
catch (SocketException se)
{
// oops, we're done
_systemMetrics.LogCount("tcp.error.SocketException." + se.SocketErrorCode.ToString());
}
catch (IOException)
{
// Not much we can do here.
_systemMetrics.LogCount("tcp.error.IOException");
}
finally
{
tcpClient.Close();
try
{
tcpClient.Close();
}
catch
{
// Do nothing but log that this happened
_systemMetrics.LogCount("tcp.error.closeThrewException");
}

_systemMetrics.LogCount("tcp.connection.closed");
Interlocked.Increment(ref _activeConnections);
Interlocked.Decrement(ref _activeConnections);
_systemMetrics.LogGauge("tcp.activeConnections", _activeConnections);
}
}
Expand Down
5 changes: 3 additions & 2 deletions statsd.net/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.1.2.0")]
[assembly: AssemblyFileVersion("1.1.2.0")]
// Uses SEMVER: http://semver.org/
[assembly: AssemblyVersion("1.2.0.0")]
[assembly: AssemblyFileVersion("1.2.0.0")]
Loading

0 comments on commit 58d5fb7

Please sign in to comment.