Files

128 lines
4.0 KiB
C#

using System.Net;
using System.Text;
using LibreHardwareMonitor.Hardware;
var port = Environment.GetEnvironmentVariable("PORT");
if (!int.TryParse(port, out var httpPort)) {
httpPort = 9646; // default port
}
var listener = new HttpListener();
listener.Prefixes.Add($"http://*:{httpPort}/");
listener.Start();
var computer =
new Computer { IsCpuEnabled = true, IsGpuEnabled = true,
IsMemoryEnabled = true, IsMotherboardEnabled = true,
IsControllerEnabled = true, IsNetworkEnabled = true,
IsStorageEnabled = true };
computer.Open();
Console.WriteLine(
$"LibreHardwareMonitorExporter listening on http://localhost:{httpPort}/metrics");
while (true) {
var context = await listener.GetContextAsync();
if (context.Request.HttpMethod != "GET") {
context.Response.StatusCode = 405;
context.Response.Close();
continue;
}
if (context.Request.Url?.AbsolutePath != "/metrics") {
context.Response.StatusCode = 404;
context.Response.Close();
continue;
}
var body = new StringBuilder();
AppendMetrics(body, computer);
var text = body.ToString();
// Normalize all line endings to '\n' for Prometheus compatibility
text = text.Replace("\r\n", "\n").Replace("\r", "\n");
if (!text.EndsWith("\n")) text += "\n";
var bytes = Encoding.UTF8.GetBytes(text);
context.Response.StatusCode = 200;
context.Response.ContentType = "text/plain; version=0.0.4";
context.Response.ContentEncoding = Encoding.UTF8;
context.Response.ContentLength64 = bytes.Length;
await context.Response.OutputStream.WriteAsync(bytes, 0, bytes.Length);
context.Response.Close();
}
static void AppendMetrics(StringBuilder output, Computer computer) {
var updateVisitor = new UpdateVisitor();
computer.Accept(updateVisitor);
// Basic example metrics for temperature, load, clock, power, fan
foreach (var hardware in computer.Hardware) {
EmitHardwareMetrics(output, hardware);
foreach (var sub in hardware.SubHardware) {
EmitHardwareMetrics(output, sub);
}
}
}
static void EmitHardwareMetrics(StringBuilder output, IHardware hardware) {
var hardwareLabel = SanitizeLabel(hardware.Name);
foreach (var sensor in hardware.Sensors) {
if (sensor.Value is null)
continue;
var sensorLabel = SanitizeLabel(sensor.Name);
var value = sensor.Value.Value;
var metricName = sensor.SensorType switch {
SensorType.Temperature => "lhm_temperature_celsius",
SensorType.Load => "lhm_load_percent",
SensorType.Clock => "lhm_clock_mhz",
SensorType.Power => "lhm_power_watts",
SensorType.Fan => "lhm_fan_rpm",
SensorType.Voltage => "lhm_voltage_volts",
SensorType.Data => "lhm_data",
SensorType.Level => "lhm_level",
SensorType.Throughput => "lhm_throughput_bytes_per_sec",
SensorType.Factor => "lhm_factor",
SensorType.Flow => "lhm_flow_lph",
SensorType.Control => "lhm_control_percent",
_ => null
};
if (metricName == null)
continue;
output.Append(metricName);
output.Append("{");
output.Append(
$"hardware=\"{hardwareLabel}\",sensor=\"{sensorLabel}\",type=\"{sensor.SensorType}\"");
output.Append("} ");
output.AppendLine(
value.ToString(System.Globalization.CultureInfo.InvariantCulture));
}
}
static string SanitizeLabel(string raw) {
var sb = new StringBuilder(raw.Length);
foreach (var ch in raw) {
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9') || ch == '_' || ch == ':' || ch == '.') {
sb.Append(ch);
} else {
sb.Append('_');
}
}
return sb.ToString();
}
sealed class UpdateVisitor : IVisitor {
public void VisitComputer(IComputer computer) { computer.Traverse(this); }
public void VisitHardware(IHardware hardware) {
hardware.Update();
foreach (var sub in hardware.SubHardware) {
sub.Accept(this);
}
}
public void VisitSensor(ISensor sensor) {}
public void VisitParameter(IParameter parameter) {}
}