فهرست منبع

Graphite Tag Support

Fix tests

Fix tag formatting

Add app and host to tags
Nick Babcock 7 سال پیش
والد
کامیت
f5551935e1

+ 17 - 4
OhmGraphite.Test/FormatMetricsTest.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Globalization;
 using System.Globalization;
 using System.Threading;
 using System.Threading;
+using OpenHardwareMonitor.Hardware;
 using Xunit;
 using Xunit;
 
 
 namespace OhmGraphite.Test
 namespace OhmGraphite.Test
@@ -10,15 +11,17 @@ namespace OhmGraphite.Test
         [Fact]
         [Fact]
         public void FormatGraphiteIdentifier()
         public void FormatGraphiteIdentifier()
         {
         {
+            var writer = new GraphiteWriter("localhost", 2003, "MY-PC", false);
             var epoch = new DateTimeOffset(new DateTime(2001, 1, 13), TimeSpan.Zero).ToUnixTimeSeconds();
             var epoch = new DateTimeOffset(new DateTime(2001, 1, 13), TimeSpan.Zero).ToUnixTimeSeconds();
-            var sensor = new Sensor("my.cpu.identifier", "voltage", 1.06f);
-            string actual = GraphiteWriter.FormatGraphiteData("MY-PC", epoch, sensor);
+            var sensor = new ReportedValue("/my/cpu/identifier/1", "voltage", 1.06f, SensorType.Voltage, "cpu", HardwareType.CPU, 1);
+            string actual = writer.FormatGraphiteData(epoch, sensor);
             Assert.Equal("ohm.MY-PC.my.cpu.identifier.voltage 1.06 979344000", actual);
             Assert.Equal("ohm.MY-PC.my.cpu.identifier.voltage 1.06 979344000", actual);
         }
         }
 
 
         [Fact]
         [Fact]
         public void FormatCultureInvariant()
         public void FormatCultureInvariant()
         {
         {
+            var writer = new GraphiteWriter("localhost", 2003, "MY-PC", false);
             CultureInfo original = Thread.CurrentThread.CurrentCulture;
             CultureInfo original = Thread.CurrentThread.CurrentCulture;
             try
             try
             {
             {
@@ -26,8 +29,8 @@ namespace OhmGraphite.Test
                 Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("de-DE");
                 Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("de-DE");
 
 
                 var epoch = new DateTimeOffset(new DateTime(2001, 1, 13), TimeSpan.Zero).ToUnixTimeSeconds();
                 var epoch = new DateTimeOffset(new DateTime(2001, 1, 13), TimeSpan.Zero).ToUnixTimeSeconds();
-                var sensor = new Sensor("my.cpu.identifier", "voltage", 1.06f);
-                string actual = GraphiteWriter.FormatGraphiteData("MY-PC", epoch, sensor);
+                var sensor = new ReportedValue("/my/cpu/identifier/1", "voltage", 1.06f, SensorType.Voltage, "cpu", HardwareType.CPU, 1);
+                string actual = writer.FormatGraphiteData(epoch, sensor);
                 Assert.Equal("ohm.MY-PC.my.cpu.identifier.voltage 1.06 979344000", actual);
                 Assert.Equal("ohm.MY-PC.my.cpu.identifier.voltage 1.06 979344000", actual);
             }
             }
             finally
             finally
@@ -35,5 +38,15 @@ namespace OhmGraphite.Test
                 Thread.CurrentThread.CurrentCulture = original;
                 Thread.CurrentThread.CurrentCulture = original;
             }
             }
         }
         }
+
+        [Fact]
+        public void FormatGraphiteTags()
+        {
+            var writer = new GraphiteWriter("localhost", 2003, "MY-PC", true);
+            var epoch = new DateTimeOffset(new DateTime(2001, 1, 13), TimeSpan.Zero).ToUnixTimeSeconds();
+            var sensor = new ReportedValue("/my/cpu/identifier/1", "voltage", 1.06f, SensorType.Voltage, "cpu", HardwareType.CPU, 1);
+            string actual = writer.FormatGraphiteData(epoch, sensor);
+            Assert.Equal("ohm.MY-PC.my.cpu.identifier.voltage;host=MY-PC;app=ohm;hardware=cpu;hardware_type=CPU;sensor_type=Voltage;sensor_index=1;raw_name=voltage 1.06 979344000", actual);
+        }
     }
     }
 }
 }

+ 50 - 13
OhmGraphite/GraphiteWriter.cs

@@ -2,6 +2,8 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.IO;
 using System.IO;
 using System.Net.Sockets;
 using System.Net.Sockets;
+using System.Text;
+using OpenHardwareMonitor.Hardware;
 using static System.FormattableString;
 using static System.FormattableString;
 
 
 namespace OhmGraphite
 namespace OhmGraphite
@@ -11,15 +13,17 @@ namespace OhmGraphite
         private readonly string _localHost;
         private readonly string _localHost;
         private readonly string _remoteHost;
         private readonly string _remoteHost;
         private readonly int _remotePort;
         private readonly int _remotePort;
+        private readonly bool _tags;
 
 
-        public GraphiteWriter(string remoteHost, int remotePort)
+        public GraphiteWriter(string remoteHost, int remotePort, string localHost, bool tags)
         {
         {
             _remoteHost = remoteHost;
             _remoteHost = remoteHost;
             _remotePort = remotePort;
             _remotePort = remotePort;
-            _localHost = Environment.MachineName;
+            _tags = tags;
+            _localHost = localHost;
         }
         }
 
 
-        public void ReportMetrics(DateTime reportTime, IEnumerable<Sensor> sensors)
+        public void ReportMetrics(DateTime reportTime, IEnumerable<ReportedValue> sensors)
         {
         {
             // We don't want to transmit metrics across multiple seconds as they
             // We don't want to transmit metrics across multiple seconds as they
             // are being retrieved so calculate the timestamp of the signaled event
             // are being retrieved so calculate the timestamp of the signaled event
@@ -31,16 +35,12 @@ namespace OhmGraphite
             {
             {
                 foreach (var sensor in sensors)
                 foreach (var sensor in sensors)
                 {
                 {
-                    var data = Normalize(sensor);
-
-                    // Graphite API wants <metric> <value> <timestamp>. We prefix the metric
-                    // with `ohm` as to not overwrite potentially existing metrics
-                    writer.WriteLine(FormatGraphiteData(_localHost, epoch, data));
+                    writer.WriteLine(FormatGraphiteData(epoch, sensor));
                 }
                 }
             }
             }
         }
         }
 
 
-        private static Sensor Normalize(Sensor sensor)
+        private static string NormalizedIdentifier(string host, ReportedValue sensor)
         {
         {
             // Take the sensor's identifier (eg. /nvidiagpu/0/load/0)
             // Take the sensor's identifier (eg. /nvidiagpu/0/load/0)
             // and tranform into nvidiagpu.0.load.<name> where <name>
             // and tranform into nvidiagpu.0.load.<name> where <name>
@@ -50,13 +50,50 @@ namespace OhmGraphite
             // separate metrics by replacing "#" with "."
             // separate metrics by replacing "#" with "."
             string identifier = sensor.Identifier.Replace('/', '.').Substring(1);
             string identifier = sensor.Identifier.Replace('/', '.').Substring(1);
             identifier = identifier.Remove(identifier.LastIndexOf('.'));
             identifier = identifier.Remove(identifier.LastIndexOf('.'));
-            string name = sensor.Name.ToLower().Replace(" ", null).Replace('#', '.');
-            return new Sensor(identifier, name, sensor.Value);
+            string name = sensor.Sensor.ToLower().Replace(" ", null).Replace('#', '.');
+            return $"ohm.{host}.{identifier}.{name}";
         }
         }
 
 
-        public static string FormatGraphiteData(string host, long epoch, Sensor data)
+        public static string GraphiteEscape(string src)
         {
         {
-            return Invariant($"ohm.{host}.{data.Identifier}.{data.Name} {data.Value} {epoch:d}");
+            // Formula for escaping graphite data is taken from
+            // collectd's utils_format_graphite.c
+            var builder = new StringBuilder(src.Length);
+            foreach (char c in src)
+            {
+                if (c == '.' || char.IsWhiteSpace(c) || char.IsControl(c))
+                {
+                    builder.Append('-');
+                }
+                else
+                {
+                    builder.Append(c);
+                }
+            }
+
+            return builder.ToString();
+        }
+
+        public string FormatGraphiteData(long epoch, ReportedValue data)
+        {
+            // Graphite API wants <metric> <value> <timestamp>. We prefix the metric
+            // with `ohm` as to not overwrite potentially existing metrics
+            string id = NormalizedIdentifier(_localHost, data);
+
+            if (!_tags)
+            {
+                return Invariant($"{id} {data.Value} {epoch:d}");
+            }
+
+            return $"{id};" +
+                   $"host={_localHost};" +
+                   "app=ohm;" +
+                   $"hardware={GraphiteEscape(data.Hardware)};" +
+                   $"hardware_type={Enum.GetName(typeof(HardwareType), data.HardwareType)};" +
+                   $"sensor_type={Enum.GetName(typeof(SensorType), data.SensorType)};" +
+                   $"sensor_index={data.SensorIndex};" +
+                   $"raw_name={GraphiteEscape(data.Sensor)} " +
+                   $"{data.Value} {epoch:d}";
         }
         }
     }
     }
 }
 }

+ 9 - 2
OhmGraphite/MetricConfig.cs

@@ -5,16 +5,18 @@ namespace OhmGraphite
 {
 {
     public class MetricConfig
     public class MetricConfig
     {
     {
-        public MetricConfig(string host, int port, TimeSpan interval)
+        public MetricConfig(string host, int port, TimeSpan interval, bool tags)
         {
         {
             Host = host;
             Host = host;
             Port = port;
             Port = port;
             Interval = interval;
             Interval = interval;
+            Tags = tags;
         }
         }
 
 
         public string Host { get; }
         public string Host { get; }
         public int Port { get; }
         public int Port { get; }
         public TimeSpan Interval { get; }
         public TimeSpan Interval { get; }
+        public bool Tags { get; }
 
 
         public static MetricConfig ParseAppSettings()
         public static MetricConfig ParseAppSettings()
         {
         {
@@ -24,13 +26,18 @@ namespace OhmGraphite
                 port = 2003;
                 port = 2003;
             }
             }
 
 
+            if (!bool.TryParse(ConfigurationManager.AppSettings["tags"], out bool tags))
+            {
+                tags = false;
+            }
+
             if (!int.TryParse(ConfigurationManager.AppSettings["interval"], out int seconds))
             if (!int.TryParse(ConfigurationManager.AppSettings["interval"], out int seconds))
             {
             {
                 seconds = 5;
                 seconds = 5;
             }
             }
 
 
             var interval = TimeSpan.FromSeconds(seconds);
             var interval = TimeSpan.FromSeconds(seconds);
-            return new MetricConfig(host, port, interval);
+            return new MetricConfig(host, port, interval, tags);
         }
         }
     }
     }
 }
 }

+ 4 - 3
OhmGraphite/Program.cs

@@ -1,4 +1,5 @@
-using NLog;
+using System;
+using NLog;
 using OpenHardwareMonitor.Hardware;
 using OpenHardwareMonitor.Hardware;
 using Topshelf;
 using Topshelf;
 
 
@@ -18,7 +19,7 @@ namespace OhmGraphite
                     // to poll the hardware
                     // to poll the hardware
                     var config = Logger.LogFunction("parse config", MetricConfig.ParseAppSettings);
                     var config = Logger.LogFunction("parse config", MetricConfig.ParseAppSettings);
                     double seconds = config.Interval.TotalSeconds;
                     double seconds = config.Interval.TotalSeconds;
-                    Logger.Info($"Host: {config.Host} port: {config.Port} interval: {seconds}");
+                    Logger.Info($"Host: {config.Host} port: {config.Port} interval: {seconds} tags: {config.Tags}");
 
 
                     // We'll want to capture all available hardware metrics
                     // We'll want to capture all available hardware metrics
                     // to send to graphite
                     // to send to graphite
@@ -33,7 +34,7 @@ namespace OhmGraphite
                     };
                     };
 
 
                     var collector = new SensorCollector(computer);
                     var collector = new SensorCollector(computer);
-                    var writer = new GraphiteWriter(config.Host, config.Port);
+                    var writer = new GraphiteWriter(config.Host, config.Port, Environment.MachineName, config.Tags);
 
 
                     s.ConstructUsing(name =>
                     s.ConstructUsing(name =>
                         Logger.LogFunction("creating timer",
                         Logger.LogFunction("creating timer",

+ 32 - 0
OhmGraphite/ReportedValue.cs

@@ -0,0 +1,32 @@
+using OpenHardwareMonitor.Hardware;
+
+namespace OhmGraphite
+{
+    public class ReportedValue
+    {
+        public ReportedValue(string identifier,
+            string sensor,
+            float value,
+            SensorType sensorType,
+            string hardware,
+            HardwareType hardwareType,
+            int sensorIndex)
+        {
+            Identifier = identifier;
+            Sensor = sensor;
+            Value = value;
+            SensorType = sensorType;
+            Hardware = hardware;
+            HardwareType = hardwareType;
+            SensorIndex = sensorIndex;
+        }
+
+        public string Identifier { get; }
+        public string Sensor { get; }
+        public float Value { get; }
+        public SensorType SensorType { get; }
+        public string Hardware { get; }
+        public HardwareType HardwareType { get; }
+        public int SensorIndex { get; }
+    }
+}

+ 0 - 16
OhmGraphite/Sensor.cs

@@ -1,16 +0,0 @@
-namespace OhmGraphite
-{
-    public class Sensor
-    {
-        public Sensor(string identifier, string name, float value)
-        {
-            Identifier = identifier;
-            Name = name;
-            Value = value;
-        }
-
-        public string Identifier { get; }
-        public string Name { get; }
-        public float Value { get; }
-    }
-}

+ 9 - 3
OhmGraphite/SensorCollector.cs

@@ -13,7 +13,7 @@ namespace OhmGraphite
         public SensorCollector(Computer computer) => _computer = computer;
         public SensorCollector(Computer computer) => _computer = computer;
         public void Open() => _computer.Open();
         public void Open() => _computer.Open();
         public void Close() => _computer.Close();
         public void Close() => _computer.Close();
-        public IEnumerable<Sensor> ReadAllSensors() => ReadHardware().SelectMany(ReadSensors);
+        public IEnumerable<ReportedValue> ReadAllSensors() => ReadHardware().SelectMany(ReadSensors);
 
 
         private IEnumerable<IHardware> ReadHardware()
         private IEnumerable<IHardware> ReadHardware()
         {
         {
@@ -25,7 +25,7 @@ namespace OhmGraphite
             }
             }
         }
         }
 
 
-        private static IEnumerable<Sensor> ReadSensors(IHardware hardware)
+        private static IEnumerable<ReportedValue> ReadSensors(IHardware hardware)
         {
         {
             hardware.Update();
             hardware.Update();
             foreach (var sensor in hardware.Sensors)
             foreach (var sensor in hardware.Sensors)
@@ -37,7 +37,13 @@ namespace OhmGraphite
                 // fans really spinning at 0 RPM or was the value not read.
                 // fans really spinning at 0 RPM or was the value not read.
                 if (sensor.Value.HasValue)
                 if (sensor.Value.HasValue)
                 {
                 {
-                    yield return new Sensor(id, sensor.Name, sensor.Value.Value);
+                    yield return new ReportedValue(id,
+                        sensor.Name,
+                        sensor.Value.Value,
+                        sensor.SensorType,
+                        sensor.Hardware.Name,
+                        sensor.Hardware.HardwareType,
+                        sensor.Index);
                 }
                 }
                 else
                 else
                 {
                 {