瀏覽代碼

Follow prometheus metric name best practices

**Breaking Change for Prometheus**: OhmGraphite has not been following [Prometheus best practices](https://prometheus.io/docs/practices/naming/) when it came to naming metrics. Metric names now look like "ohm_cpu_celsius" with only the "hardware" and "sensor" labels remaining. The following changes have been implemented:
  * `app` metric label has been removed in favor of a metric namespace prefix of "ohm"
  * `hardware_type` metric label has been removed in favor of encapsulating it into the metric name (eg: "cpu", "nic").
  * `sensor_index` metric label has been removed. This label proved superfluous as every sensor can be uniquely identified by it's name.
  * `host` metric label has been removed: This falls in line with other prometheus exporters like node_exporter, which does not export the host as a label.
  * base unit included in metric name (like "bytes", "revolutions per minute", etc)
  * The value that is exported to Prometheus is now converted into base units, such as converting GB (2^30) and MB (2^20) into bytes, MHz into hertz. Other units are unaffected. There are two candidates for this conversion that were unaffected:
    - Flow rate is still liters per hour, even though liters per second may seem more "base-unity", but grafana contained the former but not the latter.
    - Fan speed remains revolutions per minute, as I'm unaware of any manufacturer reporting fan speed as revolutions per second.
  * Side note: OhmGraphite now follows the [official data model naming scheme](https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels) by replacing invalid characters with an underscore.
Nick Babcock 6 年之前
父節點
當前提交
2e795d8797
共有 2 個文件被更改,包括 62 次插入51 次删除
  1. 3 3
      OhmGraphite.Test/PrometheusTest.cs
  2. 59 48
      OhmGraphite/PrometheusCollection.cs

+ 3 - 3
OhmGraphite.Test/PrometheusTest.cs

@@ -12,7 +12,7 @@ namespace OhmGraphite.Test
         public async void PrometheusTestServer()
         {
             var collector = new TestSensorCreator();
-            var prometheusCollection = new PrometheusCollection(collector, Metrics.DefaultRegistry);
+            _ = new PrometheusCollection(collector, Metrics.DefaultRegistry);
             var mserver = new MetricServer("localhost", 21881);
             var server = new PrometheusServer(mserver, collector);
             try
@@ -22,7 +22,7 @@ namespace OhmGraphite.Test
                 var resp = await client.GetAsync("http://localhost:21881/metrics");
                 Assert.True(resp.IsSuccessStatusCode);
                 var content = await resp.Content.ReadAsStringAsync();
-                Assert.Contains("# HELP ohm_cpu_temperature_celsius Metric reported by open hardware sensor", content);
+                Assert.Contains("# HELP ohm_cpu_celsius Metric reported by open hardware sensor", content);
             }
             finally
             {
@@ -34,7 +34,7 @@ namespace OhmGraphite.Test
         public async void PrometheusNicGuid()
         {
             var collector = new NicGuidSensor();
-            var prometheusCollection = new PrometheusCollection(collector, Metrics.DefaultRegistry);
+            _ = new PrometheusCollection(collector, Metrics.DefaultRegistry);
             var mserver = new MetricServer("localhost", 21882);
             var server = new PrometheusServer(mserver, collector);
             try

+ 59 - 48
OhmGraphite/PrometheusCollection.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Text.RegularExpressions;
 using NLog;
 using OpenHardwareMonitor.Hardware;
 using Prometheus;
@@ -8,6 +9,7 @@ namespace OhmGraphite
     public class PrometheusCollection
     {
         private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
+        private static readonly Regex rx = new Regex("[^a-zA-Z0-9_:]", RegexOptions.Compiled);
         private readonly IGiveSensors _collector;
         private MetricFactory _metrics;
 
@@ -23,65 +25,74 @@ namespace OhmGraphite
             Logger.LogAction("prometheus update metrics", PollSensors);
         }
 
-        private string SuffixForSensorType(SensorType type, out float multiplier)
+        private (string, double) BaseReport(ReportedValue report)
         {
-            multiplier = 1.0f;
-            switch (type)
+            // Convert reported value into a base value by converting MB and GB into bytes, etc.
+            // Flow rate is still liters per hour, even though liters per second may seem more
+            // "base-unity", as grafana contained the former but not the latter. Fan speed remains
+            // revolutions per minute, as I'm unaware of any manufacturer reporting fan speed as
+            // revolutions per second.
+            double BaseValue()
             {
-                case SensorType.Voltage: // V
-                    return "voltage_volts";
-
-                case SensorType.Clock: // MHz
-                    multiplier = 1000000;
-                    return "clock_hertz";
-
-                case SensorType.Temperature: // °C
-                    return "temperature_celsius";
-
-                case SensorType.Frequency: // Hz
-                    return "frequency_hertz";
-
-                case SensorType.Power: // W
-                    return "power_watts";
-
-                case SensorType.Data: // GB = 2^30 Bytes
-                    multiplier = 1073741824;
-                    return "bytes";
-
-                case SensorType.SmallData: // MB = 2^20 Bytes
-                    multiplier = 1048576;
-                    return "bytes";
-
-                case SensorType.Throughput: // B/s
-                    return "throughput_bytes_per_second";
+                double value = report.Value;
+                switch (report.SensorType)
+                {
+                    case SensorType.Data: // GB = 2^30 Bytes
+                        return value * (1L << 30);
+                    case SensorType.SmallData: // MB = 2^20 Bytes
+                        return value * (1L << 20);
+                    case SensorType.Clock: // MHz
+                        return value * 1000000;
+                    default:
+                        return value;
+                }
+            }
 
-                case SensorType.Load: // %
-                case SensorType.Control: // %
-                case SensorType.Level: // %
-                case SensorType.Fan: // RPM
-                case SensorType.Flow: // L/h
-                case SensorType.Factor: // 1
-                default:
-                    return type.ToString().ToLower();
+            string BaseUnit()
+            {
+                switch (report.SensorType)
+                {
+                    case SensorType.Voltage: // V
+                        return "volts";
+                    case SensorType.Frequency: // Hz
+                    case SensorType.Clock: // MHz
+                        return "hertz";
+                    case SensorType.Temperature: // °C
+                        return "celsius";
+                    case SensorType.Power: // W
+                        return "watts";
+                    case SensorType.Data: // GB = 2^30 Bytes
+                    case SensorType.SmallData: // MB = 2^20 Bytes
+                        return "bytes";
+                    case SensorType.Throughput: // B/s
+                        return "bytes_per_second";
+                    case SensorType.Load: // %
+                    case SensorType.Control: // %
+                    case SensorType.Level: // %
+                        return "percent";
+                    case SensorType.Fan: // RPM
+                        return "revolutions_per_minute";
+                    case SensorType.Flow: // L/h
+                        return "liters_per_hour";
+                    case SensorType.Factor: // 1
+                    default:
+                        return report.SensorType.ToString().ToLowerInvariant();
+                }
             }
+
+            return (BaseUnit(), BaseValue());
         }
 
         private void PollSensors()
         {
             foreach (var sensor in _collector.ReadAllSensors())
             {
-                string suffix = SuffixForSensorType(sensor.SensorType, out float multiplier);
-
-                _metrics.CreateGauge(
-                        String.Format(
-                            "ohm_{0}_{1}",
-                            Enum.GetName(typeof(HardwareType), sensor.HardwareType).ToLower(),
-                            suffix
-                        ),
-                        "Metric reported by open hardware sensor",
-                        "hardware", "sensor")
+                var (unit, value) = BaseReport(sensor);
+                var hw = Enum.GetName(typeof(HardwareType), sensor.HardwareType).ToLowerInvariant();
+                var name = rx.Replace($"ohm_{hw}_{unit}", "_");
+                _metrics.CreateGauge(name, "Metric reported by open hardware sensor", "hardware", "sensor")
                     .WithLabels(sensor.Hardware, sensor.Sensor)
-                    .Set(sensor.Value * multiplier);
+                    .Set(value);
             }
         }
     }