目录

Promethues Windows 温度监控

目录

https://github.com/openhardwaremonitor/openhardwaremonitor

https://stackoverflow.com/questions/3262603/accessing-cpu-temperature-in-python

collector/coretemp.go

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// https://github.com/prometheus-community/windows_exporter/pull/727
package collector

import (
	"bytes"
	"fmt"
	"strings"
	"syscall"
	"unsafe"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
	"golang.org/x/sys/windows"
)

// technical documentation for the shared memory structure: https://www.alcpu.com/CoreTemp/developers.html

var (
	kernel32         = syscall.NewLazyDLL("KERNEL32.dll")
	msvcrt           = syscall.NewLazyDLL("msvcrt.dll")
	createMutexW     = kernel32.NewProc("CreateMutexW")
	releaseMutex     = kernel32.NewProc("ReleaseMutex")
	openFileMappingW = kernel32.NewProc("OpenFileMappingW")
	closeHandle      = kernel32.NewProc("CloseHandle")
	mapViewOfFile    = kernel32.NewProc("MapViewOfFile")
	unmapViewOfFile  = kernel32.NewProc("UnmapViewOfFile")
	memcpy_s         = msvcrt.NewProc("memcpy_s")
)

func init() {
	registerCollector("coretemp", NewCoreTempCollector)
}

// A coreTempCollector is a Prometheus collector for CoreTemp shared data metrics
type coreTempCollector struct {
	Temperature *prometheus.Desc
	Load        *prometheus.Desc
}

// NewCoreTempCollector ...
func NewCoreTempCollector() (Collector, error) {
	const subsystem = "coretemp"
	return &coreTempCollector{

		Temperature: prometheus.NewDesc(
			prometheus.BuildFQName(Namespace, subsystem, "temperature_celsius"),
			"(Temperature)",
			[]string{"name", "core"},
			nil,
		),

		Load: prometheus.NewDesc(
			prometheus.BuildFQName(Namespace, subsystem, "load"),
			"(Load)",
			[]string{"name", "core"},
			nil,
		),
	}, nil
}

// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *coreTempCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
	if desc, err := c.collect(ch); err != nil {
		log.Error("failed collecting coretemp metrics:", desc, err)
		return err
	}
	return nil
}

func (c *coreTempCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {

	s, err := readCoreTempSharedData()
	if err != nil {
		return nil, err
	}

	cpuName := s.GetCPUName()

	for i := uint32(0); i < s.CoreCount; i++ {

		// convert to celsius if internal unit is fahrenheit
		temp := float64(s.Temp[i])
		if s.IsFahrenheit {
			temp = (temp - 32) * 5 / 9
		}

		ch <- prometheus.MustNewConstMetric(
			c.Temperature,
			prometheus.GaugeValue,
			temp,
			cpuName,
			fmt.Sprintf("%d", i),
		)

		ch <- prometheus.MustNewConstMetric(
			c.Load,
			prometheus.GaugeValue,
			float64(s.Load[i]),
			cpuName,
			fmt.Sprintf("%d", i),
		)
	}

	return nil, nil
}

// read memory from CoreTemp to fetch cpu data
func readCoreTempSharedData() (*coreTempSharedData, error) {

	mutexName, _ := windows.UTF16PtrFromString("CoreTempMutexObject")
	mutexObject, _, err := createMutexW.Call(0, 0, uintptr(unsafe.Pointer(mutexName)))
	if err == nil {
		return nil, fmt.Errorf("CoreTempMutexObject not found. make sure Core Temp is running")
	}
	defer releaseMutex.Call(mutexObject)

	mappingName, _ := windows.UTF16PtrFromString("CoreTempMappingObject")
	mappingObject, _, err := CoreTempMappingObject.Call(4, 1, uintptr(unsafe.Pointer(mappingName)))
	if mappingObject == uintptr(0) {
		return nil, err
	}
	defer closeHandle.Call(mappingObject)

	mapView, _, err := mapViewOfFile.Call(mappingObject, 4, 0, 0, 0)
	if mapView == uintptr(0) {
		return nil, err
	}
	defer unmapViewOfFile.Call(mapView)

	data := coreTempSharedData{}
	_, _, _ = memcpy_s.Call(uintptr(unsafe.Pointer(&data)), 0xa80, mapView, 0xa80)

	return &data, nil
}

type coreTempSharedData struct {
	Load           [256]uint32
	TjMax          [128]uint32
	CoreCount      uint32
	CPUCount       uint32
	Temp           [256]float32
	VID            float32
	CPUSpeed       float32
	FSBSpeed       float32
	Multipier      float32
	CPUName        [100]byte
	IsFahrenheit   bool // if true, true, the temperature is reported in Fahrenheit
	IsDeltaToTjMax bool // if true, the temperature reported represents the distance from TjMax
}

func (s *coreTempSharedData) GetCPUName() string {
	n := bytes.IndexByte(s.CPUName[:], 0)
	return strings.TrimSpace(string(s.CPUName[:n]))
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
collectors:
  enabled: coretemp

collector:
  service:
    services-where: "Name='windows_exporter'"

telemetry:
  addr: ":9092"
  path: "/metrics"
  max-requests: 5

scrape:
  timeout-margin: 0.5

log:
  level: warn