|
@@ -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}";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|