diff --git a/README.markdown b/README.markdown index 27daab7..570537e 100644 --- a/README.markdown +++ b/README.markdown @@ -16,4 +16,104 @@ How To Use PM> Install-Package Metrics -Work in progress, nothing to see here. \ No newline at end of file +**Second** ... + +Metrics comes with five types of metrics: + +* **Gauges** are instantaneous readings of values (e.g., a queue depth). +* **Counters** are 64-bit integers which can be incremented or decremented. +* **Meters** are increment-only counters which keep track of the rate of events. + They provide mean rates, plus exponentially-weighted moving averages which + use the same formula that the UNIX 1-, 5-, and 15-minute load averages use. +* **Histograms** capture distribution measurements about a metric: the count, + maximum, minimum, mean, standard deviation, median, 75th percentile, 95th + percentile, 98th percentile, 99th percentile, and 99.9th percentile of the + recorded values. (They do so using a method called reservoir sampling which + allows them to efficiently keep a small, statistically representative sample + of all the measurements.) +* **Timers** record the duration as well as the rate of events. In addition to + the rate information that meters provide, timers also provide the same metrics + as histograms about the recorded durations. (The samples that timers keep in + order to calculate percentiles and such are biased towards more recent data, + since you probably care more about how your application is doing *now* as + opposed to how it's done historically.) + +Metrics also has support for health checks: + + HealthChecks.Register("database", () => + { + if (Database.IsConnected) + { + return HealthCheck.Healthy; + } + else + { + return HealthCheck.Unhealthy("Not connected to database"); + } + }); + +**Third**, start collecting your metrics. + +If you're simply running a benchmark, you can print registered metrics to +standard error every 10s like this: + + // Print to Console.Error every 10 seconds + Metrics.EnableConsoleReporting(10, TimeUnit.Seconds) + +If you're writing a ASP.NET MVC-based web service, you can reference `Metrics.AspNetMvc` in +your web application project and register default routes: + + using metrics; + + public class MvcApplication : HttpApplication + { + // ... + + protected void Application_Start() + { + AspNetMvc.Metrics.RegisterRoutes(); + + // ... + } + + // ... + } + +The default routes will respond to the following URIs: + +* `/metrics`: A JSON object of all registered metrics and a host of CLR metrics. +* `/ping`: A simple `text/plain` "pong" for load-balancers. +* `/healthcheck`: Runs through all registered `HealthCheck` instances and reports the results. Returns a `200 OK` if all succeeded, or a `500 Internal Server Error` if any failed. +* `/threads`: A `text/plain` dump of all threads and their stack traces. + +The URIs of these resources can be configured by setting properties prior to registering routes. +You may also choose to protect these URIs with HTTP Basic authentication: + + using metrics; + + public class MvcApplication : HttpApplication + { + // ... + + protected void Application_Start() + { + AspNetMvc.Metrics.HealthCheckPath = "my-healthcheck-uri"; + AspNetMvc.Metrics.PingPath = "my-ping-uri"; + AspNetMvc.Metrics.MetricsPath = "my-metrics-uri"; + AspNetMvc.Metrics.ThreadsPath = "my-threads-uri"; + + AspNetMvc.Metrics.RegisterRoutes("username", "password"); + + // ... + } + + // ... + } + +License +------- +The original Metrics project is Copyright (c) 2010-2011 Coda Hale, Yammer.com + +This idiomatic port of Metrics to C# and .NET is Copyright (c) 2011 Daniel Crenna, Wildbit.com + +Both works are published under The MIT License, see LICENSE \ No newline at end of file diff --git a/metrics.Tests/Core/CLRProfilerTests.cs b/metrics.Tests/Core/CLRProfilerTests.cs new file mode 100644 index 0000000..0c2c23e --- /dev/null +++ b/metrics.Tests/Core/CLRProfilerTests.cs @@ -0,0 +1,26 @@ +using System.Diagnostics; +using metrics.Core; +using NUnit.Framework; + +namespace metrics.Tests.Core +{ + [TestFixture] + public class CLRProfilerTests + { + [Test] + public void Can_get_heap_usage() + { + var heap = CLRProfiler.HeapUsage; + Assert.IsNotNull(heap); + Trace.WriteLine(heap); + } + + [Test] + public void Can_get_uptime() + { + var heap = CLRProfiler.Uptime; + Assert.IsNotNull(heap); + Trace.WriteLine(heap); + } + } +} diff --git a/metrics.Tests/metrics.Tests.csproj b/metrics.Tests/metrics.Tests.csproj index 0278c4f..adf72cb 100644 --- a/metrics.Tests/metrics.Tests.csproj +++ b/metrics.Tests/metrics.Tests.csproj @@ -55,6 +55,7 @@ + diff --git a/metrics/Core/ClrProfiler.cs b/metrics/Core/ClrProfiler.cs index ff22fce..ffe5306 100644 --- a/metrics/Core/ClrProfiler.cs +++ b/metrics/Core/ClrProfiler.cs @@ -1,12 +1,71 @@ using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; namespace metrics.Core { public static class CLRProfiler { + private const string CategoryMemory = ".NET CLR Memory"; + private static readonly Process _process; + private static readonly IDictionary> _counters; + + static CLRProfiler() + { + _process = Process.GetCurrentProcess(); + _counters = new Dictionary> + { + {_process, new Dictionary()} + }; + } + public static string DumpThreads() { - throw new NotImplementedException(); + return "Not implemented; how about a fork?"; + } + + /// + /// Returns the number of seconds the CLR process has been running + /// + public static long Uptime + { + get { return Convert.ToInt64(_process.TotalProcessorTime.TotalSeconds); } + } + + /// + /// Returns the percentage of the CLR's heap which is being used + /// + public static double HeapUsage + { + get + { + var counter = GetOrInstallCounter("HeapUsage", CategoryMemory); + var used = WaitForNextRawValue(counter); + + var available = _process.PrivateMemorySize64; + var usage = (double)used / available; + return usage; + } + } + + private static long WaitForNextRawValue(PerformanceCounter counter) + { + long used; + while((used = counter.NextSample().RawValue) == 0) + { + Thread.Sleep(10); + } + return used; + } + + private static PerformanceCounter GetOrInstallCounter(string property, string category) + { + if (!_counters[_process].ContainsKey(property)) + { + _counters[_process].Add(property, new PerformanceCounter(category, "# bytes in all heaps", _process.ProcessName)); + } + return _counters[_process][property]; } } -} +} \ No newline at end of file diff --git a/metrics/Core/GaugeMetric.cs b/metrics/Core/GaugeMetric.cs index 57e8424..4416f90 100644 --- a/metrics/Core/GaugeMetric.cs +++ b/metrics/Core/GaugeMetric.cs @@ -7,7 +7,7 @@ namespace metrics.Core /// A gauge metric is an instantaneous reading of a partiular value. To /// instrument a queue's depth, for example: /// - /// + /// /// var queue = new Queue(); /// var gauge = new GaugeMetric(() => queue.Count); /// diff --git a/metrics/Core/HealthCheck.cs b/metrics/Core/HealthCheck.cs index 65cfdc2..54b6239 100644 --- a/metrics/Core/HealthCheck.cs +++ b/metrics/Core/HealthCheck.cs @@ -7,6 +7,10 @@ namespace metrics /// public class HealthCheck { + public static Result Healthy { get { return Result.Healthy; } } + public static Result Unhealthy(string message) { return Result.Unhealthy(message); } + public static Result Unhealthy(Exception error) { return Result.Unhealthy(error); } + private readonly Func _check; public String Name { get; private set; } diff --git a/metrics/Serialization/MetricsConverter.cs b/metrics/Serialization/MetricsConverter.cs index 961dda3..b99251e 100644 --- a/metrics/Serialization/MetricsConverter.cs +++ b/metrics/Serialization/MetricsConverter.cs @@ -6,17 +6,17 @@ namespace metrics.Serialization { - internal class MetricItem - { - public string Name { get; set; } - public IMetric Metric { get; set; } - } - /// /// Properly serializes a metrics hash /// internal class MetricsConverter : JsonConverter { + internal class MetricItem + { + public string Name { get; set; } + public IMetric Metric { get; set; } + } + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { if (!(value is IDictionary)) diff --git a/metrics/metrics.csproj b/metrics/metrics.csproj index e43f02b..83f2740 100644 --- a/metrics/metrics.csproj +++ b/metrics/metrics.csproj @@ -43,7 +43,7 @@ - +