mi: replace all WMI calls with MI calls (#1714)

This commit is contained in:
Jan-Otto Kröpke 2024-11-03 17:23:26 +01:00 committed by GitHub
parent 45d3eabab9
commit bf233ad3e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
82 changed files with 2771 additions and 738 deletions

View File

@ -51,7 +51,7 @@ linters-settings:
forbidigo:
forbid:
- "^(fmt\\.Print(|f|ln)|print|println)$"
- p: "^syscall\\..*$"
- p: "^syscall\\.(.{1,7}|.{7}[^N]|.{9,})$"
msg: use golang.org/x/sys/windows instead of syscall
- p: "^windows\\.NewLazyDLL$"
msg: use NewLazySystemDLL instead NewLazyDLL

17
go.mod
View File

@ -14,14 +14,13 @@ require (
github.com/prometheus/common v0.60.1
github.com/prometheus/exporter-toolkit v0.13.0
github.com/stretchr/testify v1.9.0
github.com/yusufpapurcu/wmi v1.2.4
golang.org/x/sys v0.26.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 // indirect
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/cgroups/v3 v3.0.3 // indirect
@ -31,7 +30,7 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/klauspost/compress v1.17.10 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/mdlayher/socket v0.5.1 // indirect
github.com/mdlayher/vsock v1.2.1 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
@ -42,13 +41,13 @@ require (
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/crypto v0.28.0 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/text v0.18.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 // indirect
google.golang.org/grpc v1.67.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
golang.org/x/text v0.19.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect
google.golang.org/grpc v1.67.1 // indirect
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

36
go.sum
View File

@ -6,8 +6,8 @@ github.com/Microsoft/hcsshim v0.12.8 h1:BtDWYlFMcWhorrvSSo2M7z0csPdw6t7no/C3FsSv
github.com/Microsoft/hcsshim v0.12.8/go.mod h1:cibQ4BqhJ32FXDwPdQhKhwrwophnh3FuT4nwQZF907w=
github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY=
github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 h1:t3eaIm0rUkzbrIewtiFmMK5RXHej2XnoXNhxVsAYUfg=
github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs=
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vSQ6PWWSL9lK8qwHozUj03+zLoEB8O0=
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q=
@ -34,7 +34,6 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@ -65,8 +64,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0=
github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@ -113,14 +112,12 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@ -131,8 +128,8 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
@ -144,7 +141,6 @@ golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -152,8 +148,8 @@ golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@ -165,15 +161,15 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 h1:N9BgCIAUvn/M+p4NJccWPWb3BWh88+zyL0ll9HgbEeM=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw=
google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -183,8 +179,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

View File

@ -8,10 +8,10 @@ import (
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "ad"
@ -118,7 +118,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
counters := []string{
abANRPerSec,
abBrowsesPerSec,

View File

@ -8,12 +8,12 @@ import (
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "adcs"
@ -74,7 +74,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
if utils.PDHEnabled() {
counters := []string{
requestsPerSecond,

View File

@ -11,12 +11,12 @@ import (
"slices"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "adfs"
@ -107,7 +107,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
if utils.PDHEnabled() {
counters := []string{
adLoginConnectionFailures,

View File

@ -8,13 +8,13 @@ import (
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "cache"
@ -92,7 +92,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
if utils.PDHEnabled() {
counters := []string{
asyncCopyReadsTotal,

View File

@ -10,10 +10,10 @@ import (
"github.com/Microsoft/hcsshim"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "container"
@ -86,7 +86,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
c.containerAvailable = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "available"),
"Available",

View File

@ -8,12 +8,12 @@ import (
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "cpu"
@ -83,7 +83,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
if utils.PDHEnabled() {
counters := []string{
c1TimeSeconds,

View File

@ -4,14 +4,15 @@ package cpu_info
import (
"errors"
"fmt"
"log/slog"
"strconv"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "cpu_info"
@ -23,8 +24,8 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for a few WMI metrics in Win32_Processor.
type Collector struct {
config Config
wmiClient *wmi.Client
miSession *mi.Session
miQuery mi.Query
cpuInfo *prometheus.Desc
cpuCoreCount *prometheus.Desc
@ -63,12 +64,19 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error {
if miSession == nil {
return errors.New("miSession is nil")
}
c.wmiClient = wmiClient
miQuery, err := mi.NewQuery("SELECT Architecture, DeviceId, Description, Family, L2CacheSize, L3CacheSize, Name, ThreadCount, NumberOfCores, NumberOfEnabledCore, NumberOfLogicalProcessors FROM Win32_Processor")
if err != nil {
return fmt.Errorf("failed to create WMI query: %w", err)
}
c.miQuery = miQuery
c.miSession = miSession
c.cpuInfo = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, "", Name),
"Labelled CPU information as provided by Win32_Processor",
@ -133,18 +141,20 @@ func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
return nil
}
type win32Processor struct {
Architecture uint32
DeviceID string
Description string
Family uint16
L2CacheSize uint32
L3CacheSize uint32
Name string
ThreadCount uint32
NumberOfCores uint32
NumberOfEnabledCore uint32
NumberOfLogicalProcessors uint32
type miProcessor struct {
Architecture uint32 `mi:"Architecture"`
DeviceID string `mi:"DeviceID"`
Description string `mi:"Description"`
Family uint16 `mi:"Family"`
L2CacheSize uint32 `mi:"L2CacheSize"`
L3CacheSize uint32 `mi:"L3CacheSize"`
Name string `mi:"Name"`
ThreadCount uint32 `mi:"ThreadCount"`
NumberOfCores uint32 `mi:"NumberOfCores"`
NumberOfEnabledCore uint32 `mi:"NumberOfEnabledCore"`
NumberOfLogicalProcessors uint32 `mi:"NumberOfLogicalProcessors"`
Total int
}
// Collect sends the metric values for each metric
@ -163,16 +173,9 @@ func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan
}
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
var dst []win32Processor
// We use a static query here because the provided methods in wmi.go all issue a SELECT *;
// This results in the time-consuming LoadPercentage field being read which seems to measure each CPU
// serially over a 1 second interval, so the scrape time is at least 1s * num_sockets
if err := c.wmiClient.Query("SELECT Architecture, DeviceId, Description, Family, L2CacheSize, L3CacheSize, Name, ThreadCount, NumberOfCores, NumberOfEnabledCore, NumberOfLogicalProcessors FROM Win32_Processor", &dst); err != nil {
return err
}
if len(dst) == 0 {
return errors.New("WMI query returned empty result set")
var dst []miProcessor
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, c.miQuery); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
// Some CPUs end up exposing trailing spaces for certain strings, so clean them up

View File

@ -7,9 +7,9 @@ import (
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/headers/sysinfoapi"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "cs"
@ -61,7 +61,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
logger.Warn("The cs collector is deprecated and will be removed in a future release. " +
"Logical processors has been moved to cpu_info collector. " +
"Physical memory has been moved to memory collector. " +

View File

@ -10,12 +10,12 @@ import (
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "dfsr"
@ -167,7 +167,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
logger = logger.With(slog.String("collector", Name))
logger.Info("dfsr collector is in an experimental state! Metrics for this collector have not been tested.")

View File

@ -8,13 +8,13 @@ import (
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "dhcp"
@ -88,7 +88,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
if utils.PDHEnabled() {
counters := []string{
acksTotal,

View File

@ -4,19 +4,17 @@ package diskdrive
import (
"errors"
"fmt"
"log/slog"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const (
Name = "diskdrive"
win32DiskQuery = "SELECT DeviceID, Model, Caption, Name, Partitions, Size, Status, Availability FROM WIN32_DiskDrive"
)
const Name = "diskdrive"
type Config struct{}
@ -25,7 +23,8 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for a few WMI metrics in Win32_DiskDrive.
type Collector struct {
config Config
wmiClient *wmi.Client
miSession *mi.Session
miQuery mi.Query
availability *prometheus.Desc
diskInfo *prometheus.Desc
@ -62,12 +61,19 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error {
if miSession == nil {
return errors.New("miSession is nil")
}
c.wmiClient = wmiClient
miQuery, err := mi.NewQuery("SELECT DeviceID, Model, Caption, Name, Partitions, Size, Status, Availability FROM WIN32_DiskDrive")
if err != nil {
return fmt.Errorf("failed to create WMI query: %w", err)
}
c.miQuery = miQuery
c.miSession = miSession
c.diskInfo = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "info"),
"General drive information",
@ -108,14 +114,14 @@ func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
}
type win32_DiskDrive struct {
DeviceID string
Model string
Size uint64
Name string
Caption string
Partitions uint32
Status string
Availability uint16
DeviceID string `mi:"DeviceID"`
Model string `mi:"Model"`
Size uint64 `mi:"Size"`
Name string `mi:"Name"`
Caption string `mi:"Caption"`
Partitions uint32 `mi:"Partitions"`
Status string `mi:"Status"`
Availability uint16 `mi:"Availability"`
}
var (
@ -175,9 +181,8 @@ func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
var dst []win32_DiskDrive
if err := c.wmiClient.Query(win32DiskQuery, &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, c.miQuery); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
if len(dst) == 0 {

View File

@ -8,11 +8,11 @@ import (
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "dns"
@ -79,7 +79,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
counters := []string{
axfrRequestReceived,
axfrRequestSent,

View File

@ -10,11 +10,11 @@ import (
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "exchange"
@ -207,7 +207,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
if utils.PDHEnabled() {
collectorFuncs := map[string]func() error{
adAccessProcesses: c.buildADAccessProcesses,

View File

@ -12,9 +12,9 @@ import (
"github.com/alecthomas/kingpin/v2"
"github.com/bmatcuk/doublestar/v4"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "filetime"
@ -85,7 +85,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
logger.Info("filetime collector is in an experimental state! It may subject to change.",
slog.String("collector", Name),
)

View File

@ -4,13 +4,14 @@ package fsrmquota
import (
"errors"
"fmt"
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "fsrmquota"
@ -21,7 +22,8 @@ var ConfigDefaults = Config{}
type Collector struct {
config Config
wmiClient *wmi.Client
miSession *mi.Session
miQuery mi.Query
quotasCount *prometheus.Desc
peakUsage *prometheus.Desc
@ -63,12 +65,18 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error {
if miSession == nil {
return errors.New("miSession is nil")
}
c.wmiClient = wmiClient
miQuery, err := mi.NewQuery("SELECT * FROM MSFT_FSRMQuota")
if err != nil {
return fmt.Errorf("failed to create WMI query: %w", err)
}
c.miQuery = miQuery
c.miSession = miSession
c.quotasCount = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "count"),
@ -146,29 +154,28 @@ func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan
// MSFT_FSRMQuota docs:
// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/fsrm/msft-fsrmquota
type MSFT_FSRMQuota struct {
Name string
Name string `mi:"Name"`
Path string
PeakUsage uint64
Size uint64
Usage uint64
Description string
Template string
// Threshold string
Disabled bool
MatchesTemplate bool
SoftLimit bool
Path string `mi:"Path"`
PeakUsage uint64 `mi:"PeakUsage"`
Size uint64 `mi:"Size"`
Usage uint64 `mi:"Usage"`
Description string `mi:"Description"`
Template string `mi:"Template"`
// Threshold string `mi:"Threshold"`
Disabled bool `mi:"Disabled"`
MatchesTemplate bool `mi:"MatchesTemplate"`
SoftLimit bool `mi:"SoftLimit"`
}
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
var dst []MSFT_FSRMQuota
if err := c.miSession.Query(&dst, mi.NamespaceRootWindowsFSRM, c.miQuery); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
var count int
if err := c.wmiClient.Query("SELECT * FROM MSFT_FSRMQuota", &dst, nil, "root/microsoft/windows/fsrm"); err != nil {
return err
}
for _, quota := range dst {
count++
path := quota.Path

View File

@ -9,9 +9,10 @@ import (
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "hyperv"
@ -23,7 +24,7 @@ var ConfigDefaults = Config{}
// Collector is a Prometheus Collector for hyper-v.
type Collector struct {
config Config
wmiClient *wmi.Client
miSession *mi.Session
// Win32_PerfRawData_VmmsVirtualMachineStats_HyperVVirtualMachineHealthSummary
healthCritical *prometheus.Desc
@ -168,12 +169,12 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error {
if miSession == nil {
return errors.New("miSession is nil")
}
c.wmiClient = wmiClient
c.miSession = miSession
buildSubsystemName := func(component string) string { return "hyperv_" + component }
@ -858,14 +859,14 @@ func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan
// Win32_PerfRawData_VmmsVirtualMachineStats_HyperVVirtualMachineHealthSummary vm health status.
type Win32_PerfRawData_VmmsVirtualMachineStats_HyperVVirtualMachineHealthSummary struct {
HealthCritical uint32
HealthOk uint32
HealthCritical uint32 `mi:"HealthCritical"`
HealthOk uint32 `mi:"HealthOK"`
}
func (c *Collector) collectVmHealth(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_VmmsVirtualMachineStats_HyperVVirtualMachineHealthSummary
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_VmmsVirtualMachineStats_HyperVVirtualMachineHealthSummary", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_VmmsVirtualMachineStats_HyperVVirtualMachineHealthSummary"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, health := range dst {
@ -887,16 +888,16 @@ func (c *Collector) collectVmHealth(ch chan<- prometheus.Metric) error {
// Win32_PerfRawData_VidPerfProvider_HyperVVMVidPartition ..,.
type Win32_PerfRawData_VidPerfProvider_HyperVVMVidPartition struct {
Name string
PhysicalPagesAllocated uint64
PreferredNUMANodeIndex uint64
RemotePhysicalPages uint64
Name string `mi:"Name"`
PhysicalPagesAllocated uint64 `mi:"PhysicalPagesAllocated"`
PreferredNUMANodeIndex uint64 `mi:"PreferredNUMANodeIndex"`
RemotePhysicalPages uint64 `mi:"RemotePhysicalPages"`
}
func (c *Collector) collectVmVid(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_VidPerfProvider_HyperVVMVidPartition
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_VidPerfProvider_HyperVVMVidPartition", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_VidPerfProvider_HyperVVMVidPartition"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, page := range dst {
@ -931,34 +932,34 @@ func (c *Collector) collectVmVid(ch chan<- prometheus.Metric) error {
// Win32_PerfRawData_HvStats_HyperVHypervisorRootPartition ...
type Win32_PerfRawData_HvStats_HyperVHypervisorRootPartition struct {
Name string
AddressSpaces uint64
AttachedDevices uint64
DepositedPages uint64
DeviceDMAErrors uint64
DeviceInterruptErrors uint64
DeviceInterruptMappings uint64
DeviceInterruptThrottleEvents uint64
GPAPages uint64
GPASpaceModificationsPersec uint64
IOTLBFlushCost uint64
IOTLBFlushesPersec uint64
RecommendedVirtualTLBSize uint64
SkippedTimerTicks uint64
Value1Gdevicepages uint64
Value1GGPApages uint64
Value2Mdevicepages uint64
Value2MGPApages uint64
Value4Kdevicepages uint64
Value4KGPApages uint64
VirtualTLBFlushEntiresPersec uint64
VirtualTLBPages uint64
Name string `mi:"Name"`
AddressSpaces uint64 `mi:"AddressSpaces"`
AttachedDevices uint64 `mi:"AttachedDevices"`
DepositedPages uint64 `mi:"DepositedPages"`
DeviceDMAErrors uint64 `mi:"DeviceDMAErrors"`
DeviceInterruptErrors uint64 `mi:"DeviceInterruptErrors"`
DeviceInterruptMappings uint64 `mi:"DeviceInterruptMappings"`
DeviceInterruptThrottleEvents uint64 `mi:"DeviceInterruptThrottleEvents"`
GPAPages uint64 `mi:"GPAPages"`
GPASpaceModificationsPersec uint64 `mi:"GPASpaceModificationsPersec"`
IOTLBFlushCost uint64 `mi:"IOTLBFlushCost"`
IOTLBFlushesPersec uint64 `mi:"IOTLBFlushesPersec"`
RecommendedVirtualTLBSize uint64 `mi:"RecommendedVirtualTLBSize"`
SkippedTimerTicks uint64 `mi:"SkippedTimerTicks"`
Value1Gdevicepages uint64 `mi:"Value1Gdevicepages"`
Value1GGPApages uint64 `mi:"Value1GGPApages"`
Value2Mdevicepages uint64 `mi:"Value2Mdevicepages"`
Value2MGPApages uint64 `mi:"Value2MGPApages"`
Value4Kdevicepages uint64 `mi:"Value4Kdevicepages"`
Value4KGPApages uint64 `mi:"Value4KGPApages"`
VirtualTLBFlushEntiresPersec uint64 `mi:"VirtualTLBFlushEntiresPersec"`
VirtualTLBPages uint64 `mi:"VirtualTLBPages"`
}
func (c *Collector) collectVmHv(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_HvStats_HyperVHypervisorRootPartition
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_HvStats_HyperVHypervisorRootPartition", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_HvStats_HyperVHypervisorRootPartition"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, obj := range dst {
@ -1087,14 +1088,14 @@ func (c *Collector) collectVmHv(ch chan<- prometheus.Metric) error {
// Win32_PerfRawData_HvStats_HyperVHypervisor ...
type Win32_PerfRawData_HvStats_HyperVHypervisor struct {
LogicalProcessors uint64
VirtualProcessors uint64
LogicalProcessors uint64 `mi:"LogicalProcessors"`
VirtualProcessors uint64 `mi:"VirtualProcessors"`
}
func (c *Collector) collectVmProcessor(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_HvStats_HyperVHypervisor
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_HvStats_HyperVHypervisor", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_HvStats_HyperVHypervisor"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, obj := range dst {
@ -1116,16 +1117,16 @@ func (c *Collector) collectVmProcessor(ch chan<- prometheus.Metric) error {
// Win32_PerfRawData_HvStats_HyperVHypervisorLogicalProcessor ...
type Win32_PerfRawData_HvStats_HyperVHypervisorLogicalProcessor struct {
Name string
PercentGuestRunTime uint64
PercentHypervisorRunTime uint64
PercentTotalRunTime uint
Name string `mi:"Name"`
PercentGuestRunTime uint64 `mi:"PercentGuestRunTime"`
PercentHypervisorRunTime uint64 `mi:"PercentHypervisorRunTime"`
PercentTotalRunTime uint64 `mi:"PercentTotalRunTime"`
}
func (c *Collector) collectHostLPUsage(logger *slog.Logger, ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_HvStats_HyperVHypervisorLogicalProcessor
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_HvStats_HyperVHypervisorLogicalProcessor", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_HvStats_HyperVHypervisorLogicalProcessor"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, obj := range dst {
@ -1170,18 +1171,18 @@ func (c *Collector) collectHostLPUsage(logger *slog.Logger, ch chan<- prometheus
// Win32_PerfRawData_HvStats_HyperVHypervisorRootVirtualProcessor ...
type Win32_PerfRawData_HvStats_HyperVHypervisorRootVirtualProcessor struct {
Name string
PercentGuestRunTime uint64
PercentHypervisorRunTime uint64
PercentRemoteRunTime uint64
PercentTotalRunTime uint64
CPUWaitTimePerDispatch uint64
Name string `mi:"Name"`
PercentGuestRunTime uint64 `mi:"PercentGuestRunTime"`
PercentHypervisorRunTime uint64 `mi:"PercentHypervisorRunTime"`
PercentRemoteRunTime uint64 `mi:"PercentRemoteRunTime"`
PercentTotalRunTime uint64 `mi:"PercentTotalRunTime"`
CPUWaitTimePerDispatch uint64 `mi:"CPUWaitTimePerDispatch"`
}
func (c *Collector) collectHostCpuUsage(logger *slog.Logger, ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_HvStats_HyperVHypervisorRootVirtualProcessor
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_HvStats_HyperVHypervisorRootVirtualProcessor", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_HvStats_HyperVHypervisorRootVirtualProcessor"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, obj := range dst {
@ -1240,18 +1241,18 @@ func (c *Collector) collectHostCpuUsage(logger *slog.Logger, ch chan<- prometheu
// Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor ...
type Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor struct {
Name string
PercentGuestRunTime uint64
PercentHypervisorRunTime uint64
PercentRemoteRunTime uint64
PercentTotalRunTime uint64
CPUWaitTimePerDispatch uint64
Name string `mi:"Name"`
PercentGuestRunTime uint64 `mi:"PercentGuestRunTime"`
PercentHypervisorRunTime uint64 `mi:"PercentHypervisorRunTime"`
PercentRemoteRunTime uint64 `mi:"PercentRemoteRunTime"`
PercentTotalRunTime uint64 `mi:"PercentTotalRunTime"`
CPUWaitTimePerDispatch uint64 `mi:"CPUWaitTimePerDispatch"`
}
func (c *Collector) collectVmCpuUsage(logger *slog.Logger, ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, obj := range dst {
@ -1318,37 +1319,37 @@ func (c *Collector) collectVmCpuUsage(logger *slog.Logger, ch chan<- prometheus.
// Win32_PerfRawData_NvspSwitchStats_HyperVVirtualSwitch ...
type Win32_PerfRawData_NvspSwitchStats_HyperVVirtualSwitch struct {
Name string
BroadcastPacketsReceivedPersec uint64
BroadcastPacketsSentPersec uint64
BytesPersec uint64
BytesReceivedPersec uint64
BytesSentPersec uint64
DirectedPacketsReceivedPersec uint64
DirectedPacketsSentPersec uint64
DroppedPacketsIncomingPersec uint64
DroppedPacketsOutgoingPersec uint64
ExtensionsDroppedPacketsIncomingPersec uint64
ExtensionsDroppedPacketsOutgoingPersec uint64
LearnedMacAddresses uint64
LearnedMacAddressesPersec uint64
MulticastPacketsReceivedPersec uint64
MulticastPacketsSentPersec uint64
NumberofSendChannelMovesPersec uint64
NumberofVMQMovesPersec uint64
PacketsFlooded uint64
PacketsFloodedPersec uint64
PacketsPersec uint64
PacketsReceivedPersec uint64
PacketsSentPersec uint64
PurgedMacAddresses uint64
PurgedMacAddressesPersec uint64
Name string `mi:"Name"`
BroadcastPacketsReceivedPersec uint64 `mi:"BroadcastPacketsReceivedPersec"`
BroadcastPacketsSentPersec uint64 `mi:"BroadcastPacketsSentPersec"`
BytesPersec uint64 `mi:"BytesPersec"`
BytesReceivedPersec uint64 `mi:"BytesReceivedPersec"`
BytesSentPersec uint64 `mi:"BytesSentPersec"`
DirectedPacketsReceivedPersec uint64 `mi:"DirectedPacketsReceivedPersec"`
DirectedPacketsSentPersec uint64 `mi:"DirectedPacketsSentPersec"`
DroppedPacketsIncomingPersec uint64 `mi:"DroppedPacketsIncomingPersec"`
DroppedPacketsOutgoingPersec uint64 `mi:"DroppedPacketsOutgoingPersec"`
ExtensionsDroppedPacketsIncomingPersec uint64 `mi:"ExtensionsDroppedPacketsIncomingPersec"`
ExtensionsDroppedPacketsOutgoingPersec uint64 `mi:"ExtensionsDroppedPacketsOutgoingPersec"`
LearnedMacAddresses uint64 `mi:"LearnedMacAddresses"`
LearnedMacAddressesPersec uint64 `mi:"LearnedMacAddressesPersec"`
MulticastPacketsReceivedPersec uint64 `mi:"MulticastPacketsReceivedPersec"`
MulticastPacketsSentPersec uint64 `mi:"MulticastPacketsSentPersec"`
NumberofSendChannelMovesPersec uint64 `mi:"NumberofSendChannelMovesPersec"`
NumberofVMQMovesPersec uint64 `mi:"NumberofVMQMovesPersec"`
PacketsFlooded uint64 `mi:"PacketsFlooded"`
PacketsFloodedPersec uint64 `mi:"PacketsFloodedPersec"`
PacketsPersec uint64 `mi:"PacketsPersec"`
PacketsReceivedPersec uint64 `mi:"PacketsReceivedPersec"`
PacketsSentPersec uint64 `mi:"PacketsSentPersec"`
PurgedMacAddresses uint64 `mi:"PurgedMacAddresses"`
PurgedMacAddressesPersec uint64 `mi:"PurgedMacAddressesPersec"`
}
func (c *Collector) collectVmSwitch(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NvspSwitchStats_HyperVVirtualSwitch
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NvspSwitchStats_HyperVVirtualSwitch", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NvspSwitchStats_HyperVVirtualSwitch"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, obj := range dst {
@ -1500,19 +1501,19 @@ func (c *Collector) collectVmSwitch(ch chan<- prometheus.Metric) error {
// Win32_PerfRawData_EthernetPerfProvider_HyperVLegacyNetworkAdapter ...
type Win32_PerfRawData_EthernetPerfProvider_HyperVLegacyNetworkAdapter struct {
Name string
BytesDropped uint64
BytesReceivedPersec uint64
BytesSentPersec uint64
FramesDropped uint64
FramesReceivedPersec uint64
FramesSentPersec uint64
Name string `mi:"Name"`
BytesDropped uint64 `mi:"BytesDropped"`
BytesReceivedPersec uint64 `mi:"BytesReceivedPersec"`
BytesSentPersec uint64 `mi:"BytesSentPersec"`
FramesDropped uint64 `mi:"FramesDropped"`
FramesReceivedPersec uint64 `mi:"FramesReceivedPersec"`
FramesSentPersec uint64 `mi:"FramesSentPersec"`
}
func (c *Collector) collectVmEthernet(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_EthernetPerfProvider_HyperVLegacyNetworkAdapter
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_EthernetPerfProvider_HyperVLegacyNetworkAdapter", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_EthernetPerfProvider_HyperVLegacyNetworkAdapter"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, obj := range dst {
@ -1568,19 +1569,19 @@ func (c *Collector) collectVmEthernet(ch chan<- prometheus.Metric) error {
// Win32_PerfRawData_Counters_HyperVVirtualStorageDevice ...
type Win32_PerfRawData_Counters_HyperVVirtualStorageDevice struct {
Name string
ErrorCount uint64
QueueLength uint32
ReadBytesPersec uint64
ReadOperationsPerSec uint64
WriteBytesPersec uint64
WriteOperationsPerSec uint64
Name string `mi:"Name"`
ErrorCount uint64 `mi:"ErrorCount"`
QueueLength uint32 `mi:"QueueLength"`
ReadBytesPersec uint64 `mi:"ReadBytesPersec"`
ReadOperationsPerSec uint64 `mi:"ReadOperationsPerSec"`
WriteBytesPersec uint64 `mi:"WriteBytesPersec"`
WriteOperationsPerSec uint64 `mi:"WriteOperationsPerSec"`
}
func (c *Collector) collectVmStorage(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_Counters_HyperVVirtualStorageDevice
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_Counters_HyperVVirtualStorageDevice", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_Counters_HyperVVirtualStorageDevice"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, obj := range dst {
@ -1636,19 +1637,19 @@ func (c *Collector) collectVmStorage(ch chan<- prometheus.Metric) error {
// Win32_PerfRawData_NvspNicStats_HyperVVirtualNetworkAdapter ...
type Win32_PerfRawData_NvspNicStats_HyperVVirtualNetworkAdapter struct {
Name string
BytesReceivedPersec uint64
BytesSentPersec uint64
DroppedPacketsIncomingPersec uint64
DroppedPacketsOutgoingPersec uint64
PacketsReceivedPersec uint64
PacketsSentPersec uint64
Name string `mi:"Name"`
BytesReceivedPersec uint64 `mi:"BytesReceivedPersec"`
BytesSentPersec uint64 `mi:"BytesSentPersec"`
DroppedPacketsIncomingPersec uint64 `mi:"DroppedPacketsIncomingPersec"`
DroppedPacketsOutgoingPersec uint64 `mi:"DroppedPacketsOutgoingPersec"`
PacketsReceivedPersec uint64 `mi:"PacketsReceivedPersec"`
PacketsSentPersec uint64 `mi:"PacketsSentPersec"`
}
func (c *Collector) collectVmNetwork(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NvspNicStats_HyperVVirtualNetworkAdapter
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NvspNicStats_HyperVVirtualNetworkAdapter", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NvspNicStats_HyperVVirtualNetworkAdapter"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, obj := range dst {
@ -1704,23 +1705,23 @@ func (c *Collector) collectVmNetwork(ch chan<- prometheus.Metric) error {
// Win32_PerfRawData_BalancerStats_HyperVDynamicMemoryVM ...
type Win32_PerfRawData_BalancerStats_HyperVDynamicMemoryVM struct {
Name string
AddedMemory uint64
AveragePressure uint64
CurrentPressure uint64
GuestVisiblePhysicalMemory uint64
MaximumPressure uint64
MemoryAddOperations uint64
MemoryRemoveOperations uint64
MinimumPressure uint64
PhysicalMemory uint64
RemovedMemory uint64
Name string `mi:"Name"`
AddedMemory uint64 `mi:"AddedMemory"`
AveragePressure uint64 `mi:"AveragePressure"`
CurrentPressure uint64 `mi:"CurrentPressure"`
GuestVisiblePhysicalMemory uint64 `mi:"GuestVisiblePhysicalMemory"`
MaximumPressure uint64 `mi:"MaximumPressure"`
MemoryAddOperations uint64 `mi:"MemoryAddOperations"`
MemoryRemoveOperations uint64 `mi:"MemoryRemoveOperations"`
MinimumPressure uint64 `mi:"MinimumPressure"`
PhysicalMemory uint64 `mi:"PhysicalMemory"`
RemovedMemory uint64 `mi:"RemovedMemory"`
}
func (c *Collector) collectVmMemory(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_BalancerStats_HyperVDynamicMemoryVM
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_BalancerStats_HyperVDynamicMemoryVM", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_BalancerStats_HyperVDynamicMemoryVM"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, obj := range dst {

View File

@ -10,10 +10,10 @@ import (
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
"golang.org/x/sys/windows/registry"
)
@ -262,7 +262,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
logger = logger.With(slog.String("collector", Name))
c.iisVersion = getIISVersion(logger)

View File

@ -7,9 +7,9 @@ import (
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/headers/slc"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "license"
@ -61,7 +61,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
c.licenseStatus = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "status"),
"Status of windows license",

View File

@ -13,13 +13,13 @@ import (
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
"golang.org/x/sys/windows"
)
@ -141,7 +141,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
if utils.PDHEnabled() {
counters := []string{
currentDiskQueueLength,

View File

@ -8,9 +8,9 @@ import (
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/headers/secur32"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "logon"
@ -54,7 +54,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
c.sessionInfo = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "session_logon_timestamp_seconds"),
"timestamp of the logon session in seconds.",

View File

@ -12,13 +12,13 @@ import (
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/headers/sysinfoapi"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "memory"
@ -105,7 +105,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
if utils.PDHEnabled() {
counters := []string{
availableBytes,

View File

@ -8,9 +8,9 @@ import (
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "mscluster"
@ -32,7 +32,7 @@ var ConfigDefaults = Config{
// A Collector is a Prometheus Collector for WMI MSCluster_Cluster metrics.
type Collector struct {
config Config
wmiClient *wmi.Client
miSession *mi.Session
// cluster
clusterAddEvictDelay *prometheus.Desc
@ -221,16 +221,16 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error {
if len(c.config.CollectorsEnabled) == 0 {
return nil
}
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
if miSession == nil {
return errors.New("miSession is nil")
}
c.wmiClient = wmiClient
c.miSession = miSession
if slices.Contains(c.config.CollectorsEnabled, "cluster") {
c.buildCluster()

View File

@ -1,7 +1,11 @@
package mscluster
import (
"fmt"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
)
@ -10,85 +14,85 @@ const nameCluster = Name + "_cluster"
// msClusterCluster represents the MSCluster_Cluster WMI class
// - https://docs.microsoft.com/en-us/previous-versions/windows/desktop/cluswmi/mscluster-cluster
type msClusterCluster struct {
Name string
Name string `mi:"Name"`
AddEvictDelay uint
AdminAccessPoint uint
AutoAssignNodeSite uint
AutoBalancerLevel uint
AutoBalancerMode uint
BackupInProgress uint
BlockCacheSize uint
ClusSvcHangTimeout uint
ClusSvcRegroupOpeningTimeout uint
ClusSvcRegroupPruningTimeout uint
ClusSvcRegroupStageTimeout uint
ClusSvcRegroupTickInMilliseconds uint
ClusterEnforcedAntiAffinity uint
ClusterFunctionalLevel uint
ClusterGroupWaitDelay uint
ClusterLogLevel uint
ClusterLogSize uint
ClusterUpgradeVersion uint
CrossSiteDelay uint
CrossSiteThreshold uint
CrossSubnetDelay uint
CrossSubnetThreshold uint
CsvBalancer uint
DatabaseReadWriteMode uint
DefaultNetworkRole uint
DetectedCloudPlatform uint
DetectManagedEvents uint
DetectManagedEventsThreshold uint
DisableGroupPreferredOwnerRandomization uint
DrainOnShutdown uint
DynamicQuorumEnabled uint
EnableSharedVolumes uint
FixQuorum uint
GracePeriodEnabled uint
GracePeriodTimeout uint
GroupDependencyTimeout uint
HangRecoveryAction uint
IgnorePersistentStateOnStartup uint
LogResourceControls uint
LowerQuorumPriorityNodeId uint
MaxNumberOfNodes uint
MessageBufferLength uint
MinimumNeverPreemptPriority uint
MinimumPreemptorPriority uint
NetftIPSecEnabled uint
PlacementOptions uint
PlumbAllCrossSubnetRoutes uint
PreventQuorum uint
QuarantineDuration uint
QuarantineThreshold uint
QuorumArbitrationTimeMax uint
QuorumArbitrationTimeMin uint
QuorumLogFileSize uint
QuorumTypeValue uint
RequestReplyTimeout uint
ResiliencyDefaultPeriod uint
ResiliencyLevel uint
ResourceDllDeadlockPeriod uint
RootMemoryReserved uint
RouteHistoryLength uint
S2DBusTypes uint
S2DCacheDesiredState uint
S2DCacheFlashReservePercent uint
S2DCachePageSizeKBytes uint
S2DEnabled uint
S2DIOLatencyThreshold uint
S2DOptimizations uint
SameSubnetDelay uint
SameSubnetThreshold uint
SecurityLevel uint
SecurityLevelForStorage uint
SharedVolumeVssWriterOperationTimeout uint
ShutdownTimeoutInMinutes uint
UseClientAccessNetworksForSharedVolumes uint
WitnessDatabaseWriteTimeout uint
WitnessDynamicWeight uint
WitnessRestartInterval uint
AddEvictDelay uint `mi:"AddEvictDelay"`
AdminAccessPoint uint `mi:"AdminAccessPoint"`
AutoAssignNodeSite uint `mi:"AutoAssignNodeSite"`
AutoBalancerLevel uint `mi:"AutoBalancerLevel"`
AutoBalancerMode uint `mi:"AutoBalancerMode"`
BackupInProgress uint `mi:"BackupInProgress"`
BlockCacheSize uint `mi:"BlockCacheSize"`
ClusSvcHangTimeout uint `mi:"ClusSvcHangTimeout"`
ClusSvcRegroupOpeningTimeout uint `mi:"ClusSvcRegroupOpeningTimeout"`
ClusSvcRegroupPruningTimeout uint `mi:"ClusSvcRegroupPruningTimeout"`
ClusSvcRegroupStageTimeout uint `mi:"ClusSvcRegroupStageTimeout"`
ClusSvcRegroupTickInMilliseconds uint `mi:"ClusSvcRegroupTickInMilliseconds"`
ClusterEnforcedAntiAffinity uint `mi:"ClusterEnforcedAntiAffinity"`
ClusterFunctionalLevel uint `mi:"ClusterFunctionalLevel"`
ClusterGroupWaitDelay uint `mi:"ClusterGroupWaitDelay"`
ClusterLogLevel uint `mi:"ClusterLogLevel"`
ClusterLogSize uint `mi:"ClusterLogSize"`
ClusterUpgradeVersion uint `mi:"ClusterUpgradeVersion"`
CrossSiteDelay uint `mi:"CrossSiteDelay"`
CrossSiteThreshold uint `mi:"CrossSiteThreshold"`
CrossSubnetDelay uint `mi:"CrossSubnetDelay"`
CrossSubnetThreshold uint `mi:"CrossSubnetThreshold"`
CsvBalancer uint `mi:"CsvBalancer"`
DatabaseReadWriteMode uint `mi:"DatabaseReadWriteMode"`
DefaultNetworkRole uint `mi:"DefaultNetworkRole"`
DetectedCloudPlatform uint `mi:"DetectedCloudPlatform"`
DetectManagedEvents uint `mi:"DetectManagedEvents"`
DetectManagedEventsThreshold uint `mi:"DetectManagedEventsThreshold"`
DisableGroupPreferredOwnerRandomization uint `mi:"DisableGroupPreferredOwnerRandomization"`
DrainOnShutdown uint `mi:"DrainOnShutdown"`
DynamicQuorumEnabled uint `mi:"DynamicQuorumEnabled"`
EnableSharedVolumes uint `mi:"EnableSharedVolumes"`
FixQuorum uint `mi:"FixQuorum"`
GracePeriodEnabled uint `mi:"GracePeriodEnabled"`
GracePeriodTimeout uint `mi:"GracePeriodTimeout"`
GroupDependencyTimeout uint `mi:"GroupDependencyTimeout"`
HangRecoveryAction uint `mi:"HangRecoveryAction"`
IgnorePersistentStateOnStartup uint `mi:"IgnorePersistentStateOnStartup"`
LogResourceControls uint `mi:"LogResourceControls"`
LowerQuorumPriorityNodeId uint `mi:"LowerQuorumPriorityNodeId"`
MaxNumberOfNodes uint `mi:"MaxNumberOfNodes"`
MessageBufferLength uint `mi:"MessageBufferLength"`
MinimumNeverPreemptPriority uint `mi:"MinimumNeverPreemptPriority"`
MinimumPreemptorPriority uint `mi:"MinimumPreemptorPriority"`
NetftIPSecEnabled uint `mi:"NetftIPSecEnabled"`
PlacementOptions uint `mi:"PlacementOptions"`
PlumbAllCrossSubnetRoutes uint `mi:"PlumbAllCrossSubnetRoutes"`
PreventQuorum uint `mi:"PreventQuorum"`
QuarantineDuration uint `mi:"QuarantineDuration"`
QuarantineThreshold uint `mi:"QuarantineThreshold"`
QuorumArbitrationTimeMax uint `mi:"QuorumArbitrationTimeMax"`
QuorumArbitrationTimeMin uint `mi:"QuorumArbitrationTimeMin"`
QuorumLogFileSize uint `mi:"QuorumLogFileSize"`
QuorumTypeValue uint `mi:"QuorumTypeValue"`
RequestReplyTimeout uint `mi:"RequestReplyTimeout"`
ResiliencyDefaultPeriod uint `mi:"ResiliencyDefaultPeriod"`
ResiliencyLevel uint `mi:"ResiliencyLevel"`
ResourceDllDeadlockPeriod uint `mi:"ResourceDllDeadlockPeriod"`
RootMemoryReserved uint `mi:"RootMemoryReserved"`
RouteHistoryLength uint `mi:"RouteHistoryLength"`
S2DBusTypes uint `mi:"S2DBusTypes"`
S2DCacheDesiredState uint `mi:"S2DCacheDesiredState"`
S2DCacheFlashReservePercent uint `mi:"S2DCacheFlashReservePercent"`
S2DCachePageSizeKBytes uint `mi:"S2DCachePageSizeKBytes"`
S2DEnabled uint `mi:"S2DEnabled"`
S2DIOLatencyThreshold uint `mi:"S2DIOLatencyThreshold"`
S2DOptimizations uint `mi:"S2DOptimizations"`
SameSubnetDelay uint `mi:"SameSubnetDelay"`
SameSubnetThreshold uint `mi:"SameSubnetThreshold"`
SecurityLevel uint `mi:"SecurityLevel"`
SecurityLevelForStorage uint `mi:"SecurityLevelForStorage"`
SharedVolumeVssWriterOperationTimeout uint `mi:"SharedVolumeVssWriterOperationTimeout"`
ShutdownTimeoutInMinutes uint `mi:"ShutdownTimeoutInMinutes"`
UseClientAccessNetworksForSharedVolumes uint `mi:"UseClientAccessNetworksForSharedVolumes"`
WitnessDatabaseWriteTimeout uint `mi:"WitnessDatabaseWriteTimeout"`
WitnessDynamicWeight uint `mi:"WitnessDynamicWeight"`
WitnessRestartInterval uint `mi:"WitnessRestartInterval"`
}
func (c *Collector) buildCluster() {
@ -558,8 +562,8 @@ func (c *Collector) buildCluster() {
func (c *Collector) collectCluster(ch chan<- prometheus.Metric) error {
var dst []msClusterCluster
if err := c.wmiClient.Query("SELECT * FROM MSCluster_Cluster", &dst, nil, "root/MSCluster"); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootMSCluster, utils.Must(mi.NewQuery("SELECT * MSCluster_Cluster"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, v := range dst {

View File

@ -1,7 +1,11 @@
package mscluster
import (
"fmt"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
)
@ -10,13 +14,13 @@ const nameNetwork = Name + "_network"
// msClusterNetwork represents the MSCluster_Network WMI class
// - https://docs.microsoft.com/en-us/previous-versions/windows/desktop/cluswmi/mscluster-network
type msClusterNetwork struct {
Name string
Name string `mi:"Name"`
Characteristics uint
Flags uint
Metric uint
Role uint
State uint
Characteristics uint `mi:"Characteristics"`
Flags uint `mi:"Flags"`
Metric uint `mi:"Metric"`
Role uint `mi:"Role"`
State uint `mi:"State"`
}
func (c *Collector) buildNetwork() {
@ -57,8 +61,8 @@ func (c *Collector) buildNetwork() {
func (c *Collector) collectNetwork(ch chan<- prometheus.Metric) error {
var dst []msClusterNetwork
if err := c.wmiClient.Query("SELECT * FROM MSCluster_Network", &dst, nil, "root/MSCluster"); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootMSCluster, utils.Must(mi.NewQuery("SELECT * MSCluster_Node"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, v := range dst {

View File

@ -1,7 +1,11 @@
package mscluster
import (
"fmt"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
)
@ -10,22 +14,22 @@ const nameNode = Name + "_node"
// msClusterNode represents the MSCluster_Node WMI class
// - https://docs.microsoft.com/en-us/previous-versions/windows/desktop/cluswmi/mscluster-node
type msClusterNode struct {
Name string
Name string `mi:"Name"`
BuildNumber uint
Characteristics uint
DetectedCloudPlatform uint
DynamicWeight uint
Flags uint
MajorVersion uint
MinorVersion uint
NeedsPreventQuorum uint
NodeDrainStatus uint
NodeHighestVersion uint
NodeLowestVersion uint
NodeWeight uint
State uint
StatusInformation uint
BuildNumber uint `mi:"BuildNumber"`
Characteristics uint `mi:"Characteristics"`
DetectedCloudPlatform uint `mi:"DetectedCloudPlatform"`
DynamicWeight uint `mi:"DynamicWeight"`
Flags uint `mi:"Flags"`
MajorVersion uint `mi:"MajorVersion"`
MinorVersion uint `mi:"MinorVersion"`
NeedsPreventQuorum uint `mi:"NeedsPreventQuorum"`
NodeDrainStatus uint `mi:"NodeDrainStatus"`
NodeHighestVersion uint `mi:"NodeHighestVersion"`
NodeLowestVersion uint `mi:"NodeLowestVersion"`
NodeWeight uint `mi:"NodeWeight"`
State uint `mi:"State"`
StatusInformation uint `mi:"StatusInformation"`
}
func (c *Collector) buildNode() {
@ -120,8 +124,8 @@ func (c *Collector) buildNode() {
func (c *Collector) collectNode(ch chan<- prometheus.Metric) ([]string, error) {
var dst []msClusterNode
if err := c.wmiClient.Query("SELECT * FROM MSCluster_Node", &dst, nil, "root/MSCluster"); err != nil {
return nil, err
if err := c.miSession.Query(&dst, mi.NamespaceRootMSCluster, utils.Must(mi.NewQuery("SELECT * FROM MSCluster_Node"))); err != nil {
return nil, fmt.Errorf("WMI query failed: %w", err)
}
nodeNames := make([]string, 0, len(dst))

View File

@ -1,7 +1,11 @@
package mscluster
import (
"fmt"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
)
@ -10,27 +14,27 @@ const nameResource = Name + "_resource"
// msClusterResource represents the MSCluster_Resource WMI class
// - https://docs.microsoft.com/en-us/previous-versions/windows/desktop/cluswmi/mscluster-resource
type msClusterResource struct {
Name string
Type string
OwnerGroup string
OwnerNode string
Name string `mi:"Name"`
Type string `mi:"Type"`
OwnerGroup string `mi:"OwnerGroup"`
OwnerNode string `mi:"OwnerNode"`
Characteristics uint
DeadlockTimeout uint
EmbeddedFailureAction uint
Flags uint
IsAlivePollInterval uint
LooksAlivePollInterval uint
MonitorProcessId uint
PendingTimeout uint
ResourceClass uint
RestartAction uint
RestartDelay uint
RestartPeriod uint
RestartThreshold uint
RetryPeriodOnFailure uint
State uint
Subclass uint
Characteristics uint `mi:"Characteristics"`
DeadlockTimeout uint `mi:"DeadlockTimeout"`
EmbeddedFailureAction uint `mi:"EmbeddedFailureAction"`
Flags uint `mi:"Flags"`
IsAlivePollInterval uint `mi:"IsAlivePollInterval"`
LooksAlivePollInterval uint `mi:"LooksAlivePollInterval"`
MonitorProcessId uint `mi:"MonitorProcessId"`
PendingTimeout uint `mi:"PendingTimeout"`
ResourceClass uint `mi:"ResourceClass"`
RestartAction uint `mi:"RestartAction"`
RestartDelay uint `mi:"RestartDelay"`
RestartPeriod uint `mi:"RestartPeriod"`
RestartThreshold uint `mi:"RestartThreshold"`
RetryPeriodOnFailure uint `mi:"RetryPeriodOnFailure"`
State uint `mi:"State"`
Subclass uint `mi:"Subclass"`
}
func (c *Collector) buildResource() {
@ -149,8 +153,8 @@ func (c *Collector) buildResource() {
func (c *Collector) collectResource(ch chan<- prometheus.Metric, nodeNames []string) error {
var dst []msClusterResource
if err := c.wmiClient.Query("SELECT * FROM MSCluster_Resource", &dst, nil, "root/MSCluster"); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootMSCluster, utils.Must(mi.NewQuery("SELECT * FROM MSCluster_Resource"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, v := range dst {

View File

@ -1,7 +1,11 @@
package mscluster
import (
"fmt"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
)
@ -10,22 +14,22 @@ const nameResourceGroup = Name + "_resourcegroup"
// msClusterResourceGroup represents the MSCluster_ResourceGroup WMI class
// - https://docs.microsoft.com/en-us/previous-versions/windows/desktop/cluswmi/mscluster-resourcegroup
type msClusterResourceGroup struct {
Name string
Name string `mi:"Name"`
AutoFailbackType uint
Characteristics uint
ColdStartSetting uint
DefaultOwner uint
FailbackWindowEnd int
FailbackWindowStart int
FailoverPeriod uint
FailoverThreshold uint
Flags uint
GroupType uint
OwnerNode string
Priority uint
ResiliencyPeriod uint
State uint
AutoFailbackType uint `mi:"AutoFailbackType"`
Characteristics uint `mi:"Characteristics"`
ColdStartSetting uint `mi:"ColdStartSetting"`
DefaultOwner uint `mi:"DefaultOwner"`
FailbackWindowEnd int `mi:"FailbackWindowEnd"`
FailbackWindowStart int `mi:"FailbackWindowStart"`
FailoverPeriod uint `mi:"FailoverPeriod"`
FailoverThreshold uint `mi:"FailoverThreshold"`
Flags uint `mi:"Flags"`
GroupType uint `mi:"GroupType"`
OwnerNode string `mi:"OwnerNode"`
Priority uint `mi:"Priority"`
ResiliencyPeriod uint `mi:"ResiliencyPeriod"`
State uint `mi:"State"`
}
func (c *Collector) buildResourceGroup() {
@ -126,8 +130,8 @@ func (c *Collector) buildResourceGroup() {
func (c *Collector) collectResourceGroup(ch chan<- prometheus.Metric, nodeNames []string) error {
var dst []msClusterResourceGroup
if err := c.wmiClient.Query("SELECT * FROM MSCluster_ResourceGroup", &dst, nil, "root/MSCluster"); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootMSCluster, utils.Must(mi.NewQuery("SELECT * FROM MSCluster_ResourceGroup"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, v := range dst {

View File

@ -4,14 +4,15 @@ package msmq
import (
"errors"
"fmt"
"log/slog"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "msmq"
@ -27,7 +28,7 @@ var ConfigDefaults = Config{
// A Collector is a Prometheus Collector for WMI Win32_PerfRawData_MSMQ_MSMQQueue metrics.
type Collector struct {
config Config
wmiClient *wmi.Client
miSession *mi.Session
bytesInJournalQueue *prometheus.Desc
bytesInQueue *prometheus.Desc
@ -75,14 +76,14 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(logger *slog.Logger, wmiClient *wmi.Client) error {
func (c *Collector) Build(logger *slog.Logger, miSession *mi.Session) error {
logger = logger.With(slog.String("collector", Name))
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
if miSession == nil {
return errors.New("miSession is nil")
}
c.wmiClient = wmiClient
c.miSession = miSession
if *c.config.QueryWhereClause == "" {
logger.Warn("No where-clause specified for msmq collector. This will generate a very large number of metrics!")
@ -132,12 +133,12 @@ func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan
}
type msmqQueue struct {
Name string
Name string `mi:"Name"`
BytesInJournalQueue uint64
BytesInQueue uint64
MessagesInJournalQueue uint64
MessagesInQueue uint64
BytesInJournalQueue uint64 `mi:"BytesInJournalQueue"`
BytesInQueue uint64 `mi:"BytesInQueue"`
MessagesInJournalQueue uint64 `mi:"MessagesInJournalQueue"`
MessagesInQueue uint64 `mi:"MessagesInQueue"`
}
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
@ -148,8 +149,13 @@ func (c *Collector) collect(ch chan<- prometheus.Metric) error {
query += " WHERE " + *c.config.QueryWhereClause
}
if err := c.wmiClient.Query(query, &dst); err != nil {
return err
queryExpression, err := mi.NewQuery(query)
if err != nil {
return fmt.Errorf("failed to create WMI query: %w", err)
}
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, queryExpression); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, msmq := range dst {

View File

@ -13,10 +13,10 @@ import (
"time"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
"golang.org/x/sys/windows/registry"
)
@ -508,7 +508,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
// Result must order, to prevent test failures.
sort.Strings(c.config.CollectorsEnabled)

View File

@ -13,12 +13,12 @@ import (
"unsafe"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
"golang.org/x/sys/windows"
)
@ -149,7 +149,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
if utils.PDHEnabled() {
counters := []string{
BytesReceivedPerSec,

View File

@ -9,9 +9,9 @@ import (
"slices"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "netframework"
@ -47,7 +47,7 @@ const (
// A Collector is a Prometheus Collector for WMI Win32_PerfRawData_NETFramework_NETCLRExceptions metrics.
type Collector struct {
config Config
wmiClient *wmi.Client
miSession *mi.Session
// clrexceptions
numberOfExceptionsThrown *prometheus.Desc
@ -143,12 +143,12 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error {
if miSession == nil {
return errors.New("miSession is nil")
}
c.wmiClient = wmiClient
c.miSession = miSession
if slices.Contains(c.config.CollectorsEnabled, collectorClrExceptions) {
c.buildClrExceptions()

View File

@ -3,7 +3,11 @@
package netframework
import (
"fmt"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
)
@ -35,19 +39,19 @@ func (c *Collector) buildClrExceptions() {
}
type Win32_PerfRawData_NETFramework_NETCLRExceptions struct {
Name string
Name string `mi:"Name"`
NumberofExcepsThrown uint32
NumberofExcepsThrownPersec uint32
NumberofFiltersPersec uint32
NumberofFinallysPersec uint32
ThrowToCatchDepthPersec uint32
NumberofExcepsThrown uint32 `mi:"NumberofExcepsThrown"`
NumberofExcepsThrownPersec uint32 `mi:"NumberofExcepsThrownPersec"`
NumberofFiltersPersec uint32 `mi:"NumberofFiltersPersec"`
NumberofFinallysPersec uint32 `mi:"NumberofFinallysPersec"`
ThrowToCatchDepthPersec uint32 `mi:"ThrowToCatchDepthPersec"`
}
func (c *Collector) collectClrExceptions(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRExceptions
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRExceptions", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRExceptions"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, process := range dst {

View File

@ -3,7 +3,11 @@
package netframework
import (
"fmt"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
)
@ -29,19 +33,19 @@ func (c *Collector) buildClrInterop() {
}
type Win32_PerfRawData_NETFramework_NETCLRInterop struct {
Name string
Name string `mi:"Name"`
NumberofCCWs uint32
Numberofmarshalling uint32
NumberofStubs uint32
NumberofTLBexportsPersec uint32
NumberofTLBimportsPersec uint32
NumberofCCWs uint32 `mi:"NumberofCCWs"`
Numberofmarshalling uint32 `mi:"Numberofmarshalling"`
NumberofStubs uint32 `mi:"NumberofStubs"`
NumberofTLBexportsPersec uint32 `mi:"NumberofTLBexportsPersec"`
NumberofTLBimportsPersec uint32 `mi:"NumberofTLBimportsPersec"`
}
func (c *Collector) collectClrInterop(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRInterop
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRInterop", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRInterop"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, process := range dst {

View File

@ -3,7 +3,11 @@
package netframework
import (
"fmt"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
)
@ -35,21 +39,21 @@ func (c *Collector) buildClrJIT() {
}
type Win32_PerfRawData_NETFramework_NETCLRJit struct {
Name string
Name string `mi:"Name"`
Frequency_PerfTime uint32
ILBytesJittedPersec uint32
NumberofILBytesJitted uint32
NumberofMethodsJitted uint32
PercentTimeinJit uint32
StandardJitFailures uint32
TotalNumberofILBytesJitted uint32
Frequency_PerfTime uint32 `mi:"Frequency_PerfTime"`
ILBytesJittedPersec uint32 `mi:"ILBytesJittedPersec"`
NumberofILBytesJitted uint32 `mi:"NumberofILBytesJitted"`
NumberofMethodsJitted uint32 `mi:"NumberofMethodsJitted"`
PercentTimeinJit uint32 `mi:"PercentTimeinJit"`
StandardJitFailures uint32 `mi:"StandardJitFailures"`
TotalNumberofILBytesJitted uint32 `mi:"TotalNumberofILBytesJitted"`
}
func (c *Collector) collectClrJIT(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRJit
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRJit", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRJit"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, process := range dst {

View File

@ -3,7 +3,11 @@
package netframework
import (
"fmt"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
)
@ -65,30 +69,30 @@ func (c *Collector) buildClrLoading() {
}
type Win32_PerfRawData_NETFramework_NETCLRLoading struct {
Name string
Name string `mi:"Name"`
AssemblySearchLength uint32
BytesinLoaderHeap uint64
Currentappdomains uint32
CurrentAssemblies uint32
CurrentClassesLoaded uint32
PercentTimeLoading uint64
Rateofappdomains uint32
Rateofappdomainsunloaded uint32
RateofAssemblies uint32
RateofClassesLoaded uint32
RateofLoadFailures uint32
TotalAppdomains uint32
Totalappdomainsunloaded uint32
TotalAssemblies uint32
TotalClassesLoaded uint32
TotalNumberofLoadFailures uint32
AssemblySearchLength uint32 `mi:"AssemblySearchLength"`
BytesinLoaderHeap uint64 `mi:"BytesinLoaderHeap"`
Currentappdomains uint32 `mi:"Currentappdomains"`
CurrentAssemblies uint32 `mi:"CurrentAssemblies"`
CurrentClassesLoaded uint32 `mi:"CurrentClassesLoaded"`
PercentTimeLoading uint64 `mi:"PercentTimeLoading"`
Rateofappdomains uint32 `mi:"Rateofappdomains"`
Rateofappdomainsunloaded uint32 `mi:"Rateofappdomainsunloaded"`
RateofAssemblies uint32 `mi:"RateofAssemblies"`
RateofClassesLoaded uint32 `mi:"RateofClassesLoaded"`
RateofLoadFailures uint32 `mi:"RateofLoadFailures"`
TotalAppdomains uint32 `mi:"TotalAppdomains"`
Totalappdomainsunloaded uint32 `mi:"Totalappdomainsunloaded"`
TotalAssemblies uint32 `mi:"TotalAssemblies"`
TotalClassesLoaded uint32 `mi:"TotalClassesLoaded"`
TotalNumberofLoadFailures uint32 `mi:"TotalNumberofLoadFailures"`
}
func (c *Collector) collectClrLoading(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRLoading
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRLoading", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRLoading"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, process := range dst {

View File

@ -3,7 +3,11 @@
package netframework
import (
"fmt"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
)
@ -53,24 +57,24 @@ func (c *Collector) buildClrLocksAndThreads() {
}
type Win32_PerfRawData_NETFramework_NETCLRLocksAndThreads struct {
Name string
Name string `mi:"Name"`
ContentionRatePersec uint32
CurrentQueueLength uint32
NumberofcurrentlogicalThreads uint32
NumberofcurrentphysicalThreads uint32
Numberofcurrentrecognizedthreads uint32
Numberoftotalrecognizedthreads uint32
QueueLengthPeak uint32
QueueLengthPersec uint32
RateOfRecognizedThreadsPersec uint32
TotalNumberofContentions uint32
ContentionRatePersec uint32 `mi:"ContentionRatePersec"`
CurrentQueueLength uint32 `mi:"CurrentQueueLength"`
NumberofcurrentlogicalThreads uint32 `mi:"NumberofcurrentlogicalThreads"`
NumberofcurrentphysicalThreads uint32 `mi:"NumberofcurrentphysicalThreads"`
Numberofcurrentrecognizedthreads uint32 `mi:"Numberofcurrentrecognizedthreads"`
Numberoftotalrecognizedthreads uint32 `mi:"Numberoftotalrecognizedthreads"`
QueueLengthPeak uint32 `mi:"QueueLengthPeak"`
QueueLengthPersec uint32 `mi:"QueueLengthPersec"`
RateOfRecognizedThreadsPersec uint32 `mi:"RateOfRecognizedThreadsPersec"`
TotalNumberofContentions uint32 `mi:"TotalNumberofContentions"`
}
func (c *Collector) collectClrLocksAndThreads(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRLocksAndThreads
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRLocksAndThreads", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRLocksAndThreads"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, process := range dst {

View File

@ -3,7 +3,11 @@
package netframework
import (
"fmt"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
)
@ -83,43 +87,43 @@ func (c *Collector) buildClrMemory() {
}
type Win32_PerfRawData_NETFramework_NETCLRMemory struct {
Name string
Name string `mi:"Name"`
AllocatedBytesPersec uint64
FinalizationSurvivors uint64
Frequency_PerfTime uint64
Gen0heapsize uint64
Gen0PromotedBytesPerSec uint64
Gen1heapsize uint64
Gen1PromotedBytesPerSec uint64
Gen2heapsize uint64
LargeObjectHeapsize uint64
NumberBytesinallHeaps uint64
NumberGCHandles uint64
NumberGen0Collections uint64
NumberGen1Collections uint64
NumberGen2Collections uint64
NumberInducedGC uint64
NumberofPinnedObjects uint64
NumberofSinkBlocksinuse uint64
NumberTotalcommittedBytes uint64
NumberTotalreservedBytes uint64
AllocatedBytesPersec uint64 `mi:"AllocatedBytesPersec"`
FinalizationSurvivors uint64 `mi:"FinalizationSurvivors"`
Frequency_PerfTime uint64 `mi:"Frequency_PerfTime"`
Gen0heapsize uint64 `mi:"Gen0heapsize"`
Gen0PromotedBytesPerSec uint64 `mi:"Gen0PromotedBytesPersec"`
Gen1heapsize uint64 `mi:"Gen1heapsize"`
Gen1PromotedBytesPerSec uint64 `mi:"Gen1PromotedBytesPersec"`
Gen2heapsize uint64 `mi:"Gen2heapsize"`
LargeObjectHeapsize uint64 `mi:"LargeObjectHeapsize"`
NumberBytesinallHeaps uint64 `mi:"NumberBytesinallHeaps"`
NumberGCHandles uint64 `mi:"NumberGCHandles"`
NumberGen0Collections uint64 `mi:"NumberGen0Collections"`
NumberGen1Collections uint64 `mi:"NumberGen1Collections"`
NumberGen2Collections uint64 `mi:"NumberGen2Collections"`
NumberInducedGC uint64 `mi:"NumberInducedGC"`
NumberofPinnedObjects uint64 `mi:"NumberofPinnedObjects"`
NumberofSinkBlocksinuse uint64 `mi:"NumberofSinkBlocksinuse"`
NumberTotalcommittedBytes uint64 `mi:"NumberTotalcommittedBytes"`
NumberTotalreservedBytes uint64 `mi:"NumberTotalreservedBytes"`
// PercentTimeinGC has countertype=PERF_RAW_FRACTION.
// Formula: (100 * CounterValue) / BaseValue
// By docs https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/scripting-articles/ms974615(v=msdn.10)#perf_raw_fraction
PercentTimeinGC uint32
PercentTimeinGC uint32 `mi:"PercentTimeinGC"`
// BaseValue is just a "magic" number used to make the calculation come out right.
PercentTimeinGC_base uint32
ProcessID uint64
PromotedFinalizationMemoryfromGen0 uint64
PromotedMemoryfromGen0 uint64
PromotedMemoryfromGen1 uint64
PercentTimeinGC_base uint32 `mi:"PercentTimeinGC_base"`
ProcessID uint64 `mi:"ProcessID"`
PromotedFinalizationMemoryfromGen0 uint64 `mi:"PromotedFinalizationMemoryfromGen0"`
PromotedMemoryfromGen0 uint64 `mi:"PromotedMemoryfromGen0"`
PromotedMemoryfromGen1 uint64 `mi:"PromotedMemoryfromGen1"`
}
func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRMemory
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRMemory", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRMemory"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, process := range dst {

View File

@ -3,7 +3,11 @@
package netframework
import (
"fmt"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
)
@ -47,21 +51,21 @@ func (c *Collector) buildClrRemoting() {
}
type Win32_PerfRawData_NETFramework_NETCLRRemoting struct {
Name string
Name string `mi:"Name"`
Channels uint32
ContextBoundClassesLoaded uint32
ContextBoundObjectsAllocPersec uint32
ContextProxies uint32
Contexts uint32
RemoteCallsPersec uint32
TotalRemoteCalls uint32
Channels uint32 `mi:"Channels"`
ContextBoundClassesLoaded uint32 `mi:"ContextBoundClassesLoaded"`
ContextBoundObjectsAllocPersec uint32 `mi:"ContextBoundObjectsAllocPersec"`
ContextProxies uint32 `mi:"ContextProxies"`
Contexts uint32 `mi:"Contexts"`
RemoteCallsPersec uint32 `mi:"RemoteCallsPersec"`
TotalRemoteCalls uint32 `mi:"TotalRemoteCalls"`
}
func (c *Collector) collectClrRemoting(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRRemoting
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRRemoting", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRRemoting"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, process := range dst {

View File

@ -3,7 +3,11 @@
package netframework
import (
"fmt"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
)
@ -35,20 +39,20 @@ func (c *Collector) buildClrSecurity() {
}
type Win32_PerfRawData_NETFramework_NETCLRSecurity struct {
Name string
Name string `mi:"Name"`
Frequency_PerfTime uint32
NumberLinkTimeChecks uint32
PercentTimeinRTchecks uint32
PercentTimeSigAuthenticating uint64
StackWalkDepth uint32
TotalRuntimeChecks uint32
Frequency_PerfTime uint32 `mi:"Frequency_PerfTime"`
NumberLinkTimeChecks uint32 `mi:"NumberLinkTimeChecks"`
PercentTimeinRTchecks uint32 `mi:"PercentTimeinRTchecks"`
PercentTimeSigAuthenticating uint64 `mi:"PercentTimeSigAuthenticating"`
StackWalkDepth uint32 `mi:"StackWalkDepth"`
TotalRuntimeChecks uint32 `mi:"TotalRuntimeChecks"`
}
func (c *Collector) collectClrSecurity(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRSecurity
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRSecurity", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRSecurity"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, process := range dst {

View File

@ -6,9 +6,9 @@ import (
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "nps"
@ -20,7 +20,10 @@ var ConfigDefaults = Config{}
// Collector is a Prometheus Collector for WMI Win32_PerfRawData_IAS_NPSAuthenticationServer and Win32_PerfRawData_IAS_NPSAccountingServer metrics.
type Collector struct {
config Config
wmiClient *wmi.Client
miSession *mi.Session
miQueryAuthenticationServer mi.Query
miQueryAccountingServer mi.Query
accessAccepts *prometheus.Desc
accessChallenges *prometheus.Desc
@ -78,12 +81,26 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error {
if miSession == nil {
return errors.New("miSession is nil")
}
c.wmiClient = wmiClient
miQuery, err := mi.NewQuery("SELECT Name, AccessAccepts, AccessChallenges, AccessRejects, AccessRequests, AccessBadAuthenticators, AccessDroppedPackets, AccessInvalidRequests, AccessMalformedPackets, AccessPacketsReceived, AccessPacketsSent, AccessServerResetTime, AccessServerUpTime, AccessUnknownType FROM Win32_PerfRawData_IAS_NPSAuthenticationServer")
if err != nil {
return fmt.Errorf("failed to create WMI query: %w", err)
}
c.miQueryAuthenticationServer = miQuery
miQuery, err = mi.NewQuery("SELECT Name, AccountingRequests, AccountingResponses, AccountingBadAuthenticators, AccountingDroppedPackets, AccountingInvalidRequests, AccountingMalformedPackets, AccountingNoRecord, AccountingPacketsReceived, AccountingPacketsSent, AccountingServerResetTime, AccountingServerUpTime, AccountingUnknownType FROM Win32_PerfRawData_IAS_NPSAccountingServer")
if err != nil {
return fmt.Errorf("failed to create WMI query: %w", err)
}
c.miQueryAccountingServer = miQuery
c.miSession = miSession
c.accessAccepts = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "access_accepts"),
"(AccessAccepts)",
@ -261,46 +278,46 @@ func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan
// Win32_PerfRawData_IAS_NPSAuthenticationServer docs:
// at the moment there is no Microsoft documentation.
type Win32_PerfRawData_IAS_NPSAuthenticationServer struct {
Name string
Name string `mi:"Name"`
AccessAccepts uint32
AccessChallenges uint32
AccessRejects uint32
AccessRequests uint32
AccessBadAuthenticators uint32
AccessDroppedPackets uint32
AccessInvalidRequests uint32
AccessMalformedPackets uint32
AccessPacketsReceived uint32
AccessPacketsSent uint32
AccessServerResetTime uint32
AccessServerUpTime uint32
AccessUnknownType uint32
AccessAccepts uint32 `mi:"AccessAccepts"`
AccessChallenges uint32 `mi:"AccessChallenges"`
AccessRejects uint32 `mi:"AccessRejects"`
AccessRequests uint32 `mi:"AccessRequests"`
AccessBadAuthenticators uint32 `mi:"AccessBadAuthenticators"`
AccessDroppedPackets uint32 `mi:"AccessDroppedPackets"`
AccessInvalidRequests uint32 `mi:"AccessInvalidRequests"`
AccessMalformedPackets uint32 `mi:"AccessMalformedPackets"`
AccessPacketsReceived uint32 `mi:"AccessPacketsReceived"`
AccessPacketsSent uint32 `mi:"AccessPacketsSent"`
AccessServerResetTime uint32 `mi:"AccessServerResetTime"`
AccessServerUpTime uint32 `mi:"AccessServerUpTime"`
AccessUnknownType uint32 `mi:"AccessUnknownType"`
}
type Win32_PerfRawData_IAS_NPSAccountingServer struct {
Name string
Name string `mi:"Name"`
AccountingRequests uint32
AccountingResponses uint32
AccountingBadAuthenticators uint32
AccountingDroppedPackets uint32
AccountingInvalidRequests uint32
AccountingMalformedPackets uint32
AccountingNoRecord uint32
AccountingPacketsReceived uint32
AccountingPacketsSent uint32
AccountingServerResetTime uint32
AccountingServerUpTime uint32
AccountingUnknownType uint32
AccountingRequests uint32 `mi:"AccountingRequests"`
AccountingResponses uint32 `mi:"AccountingResponses"`
AccountingBadAuthenticators uint32 `mi:"AccountingBadAuthenticators"`
AccountingDroppedPackets uint32 `mi:"AccountingDroppedPackets"`
AccountingInvalidRequests uint32 `mi:"AccountingInvalidRequests"`
AccountingMalformedPackets uint32 `mi:"AccountingMalformedPackets"`
AccountingNoRecord uint32 `mi:"AccountingNoRecord"`
AccountingPacketsReceived uint32 `mi:"AccountingPacketsReceived"`
AccountingPacketsSent uint32 `mi:"AccountingPacketsSent"`
AccountingServerResetTime uint32 `mi:"AccountingServerResetTime"`
AccountingServerUpTime uint32 `mi:"AccountingServerUpTime"`
AccountingUnknownType uint32 `mi:"AccountingUnknownType"`
}
// CollectAccept sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) CollectAccept(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_IAS_NPSAuthenticationServer
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_IAS_NPSAuthenticationServer", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, c.miQueryAuthenticationServer); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
ch <- prometheus.MustNewConstMetric(
@ -386,8 +403,8 @@ func (c *Collector) CollectAccept(ch chan<- prometheus.Metric) error {
func (c *Collector) CollectAccounting(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_IAS_NPSAccountingServer
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_IAS_NPSAccountingServer", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, c.miQueryAccountingServer); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
ch <- prometheus.MustNewConstMetric(

View File

@ -16,10 +16,10 @@ import (
"github.com/prometheus-community/windows_exporter/internal/headers/netapi32"
"github.com/prometheus-community/windows_exporter/internal/headers/psapi"
"github.com/prometheus-community/windows_exporter/internal/headers/sysinfoapi"
"github.com/prometheus-community/windows_exporter/internal/mi"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
)
@ -109,9 +109,11 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
logger.Warn("The os collect holds a number of deprecated metrics and will be removed mid 2025. " +
"See https://github.com/prometheus-community/windows_exporter/pull/1596 for more information.")
func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
logger.Warn("The os collect holds a number of deprecated metrics and will be removed mid 2025. "+
"See https://github.com/prometheus-community/windows_exporter/pull/1596 for more information.",
slog.String("collector", Name),
)
workstationInfo, err := netapi32.GetWorkstationInfo()
if err != nil {

View File

@ -11,11 +11,11 @@ import (
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "perfdata"
@ -92,7 +92,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
logger.Warn("The perfdata collector is in an experimental state! The configuration may change in future. Please report any issues.")
for i, object := range c.config.Objects {

View File

@ -9,11 +9,11 @@ import (
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "physical_disk"
@ -114,7 +114,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
c.requestsQueued = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "requests_queued"),
"The number of requests queued to the disk (PhysicalDisk.CurrentDiskQueueLength)",

View File

@ -10,9 +10,9 @@ import (
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "printer"
@ -40,7 +40,9 @@ var ConfigDefaults = Config{
type Collector struct {
config Config
wmiClient *wmi.Client
miSession *mi.Session
miQueryPrinterJobs mi.Query
miQueryPrinter mi.Query
printerStatus *prometheus.Desc
printerJobStatus *prometheus.Desc
@ -107,12 +109,25 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error {
if miSession == nil {
return errors.New("miSession is nil")
}
c.wmiClient = wmiClient
miQuery, err := mi.NewQuery("SELECT Name, Default, PrinterStatus, JobCountSinceLastReset FROM win32_Printer")
if err != nil {
return fmt.Errorf("failed to create WMI query: %w", err)
}
c.miQueryPrinter = miQuery
miQuery, err = mi.NewQuery("SELECT Name, Status FROM win32_PrintJob")
if err != nil {
return fmt.Errorf("failed to create WMI query: %w", err)
}
c.miQueryPrinterJobs = miQuery
c.miSession = miSession
c.printerJobStatus = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "job_status"),
@ -143,42 +158,35 @@ func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
}
type wmiPrinter struct {
Name string
Default bool
PrinterStatus uint16
JobCountSinceLastReset uint32
Name string `mi:"Name"`
Default bool `mi:"Default"`
PrinterStatus uint16 `mi:"PrinterStatus"`
JobCountSinceLastReset uint32 `mi:"JobCountSinceLastReset"`
}
type wmiPrintJob struct {
Name string
Status string
Name string `mi:"Name"`
Status string `mi:"Status"`
}
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
if err := c.collectPrinterStatus(ch); err != nil {
logger.Error("failed to collect printer status metrics",
slog.Any("err", err),
)
func (c *Collector) Collect(_ *types.ScrapeContext, _ *slog.Logger, ch chan<- prometheus.Metric) error {
var errs []error
return err
if err := c.collectPrinterStatus(ch); err != nil {
errs = append(errs, fmt.Errorf("failed to collect printer status metrics: %w", err))
}
if err := c.collectPrinterJobStatus(ch); err != nil {
logger.Error("failed to collect printer job status metrics",
slog.Any("err", err),
)
return err
errs = append(errs, fmt.Errorf("failed to collect printer job status metrics: %w", err))
}
return nil
return errors.Join(errs...)
}
func (c *Collector) collectPrinterStatus(ch chan<- prometheus.Metric) error {
var printers []wmiPrinter
if err := c.wmiClient.Query("SELECT * FROM win32_Printer", &printers); err != nil {
return err
if err := c.miSession.Query(&printers, mi.NamespaceRootCIMv2, c.miQueryPrinter); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, printer := range printers {
@ -215,8 +223,8 @@ func (c *Collector) collectPrinterStatus(ch chan<- prometheus.Metric) error {
func (c *Collector) collectPrinterJobStatus(ch chan<- prometheus.Metric) error {
var printJobs []wmiPrintJob
if err := c.wmiClient.Query("SELECT * FROM win32_PrintJob", &printJobs); err != nil {
return err
if err := c.miSession.Query(&printJobs, mi.NamespaceRootCIMv2, c.miQueryPrinterJobs); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
groupedPrintJobs := c.groupPrintJobs(printJobs)

View File

@ -12,13 +12,13 @@ import (
"unsafe"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
v2 "github.com/prometheus-community/windows_exporter/internal/perfdata/v2"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
"golang.org/x/sys/windows"
)
@ -38,7 +38,9 @@ var ConfigDefaults = Config{
type Collector struct {
config Config
wmiClient *wmi.Client
miSession *mi.Session
workerProcessMIQueryQuery mi.Query
perfDataCollector perfdata.Collector
@ -139,14 +141,20 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(logger *slog.Logger, wmiClient *wmi.Client) error {
func (c *Collector) Build(logger *slog.Logger, miSession *mi.Session) error {
logger = logger.With(slog.String("collector", Name))
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
if miSession == nil {
return errors.New("miSession is nil")
}
c.wmiClient = wmiClient
miQuery, err := mi.NewQuery("SELECT AppPoolName, ProcessId FROM WorkerProcess")
if err != nil {
return fmt.Errorf("failed to create WMI query: %w", err)
}
c.workerProcessMIQueryQuery = miQuery
c.miSession = miSession
if utils.PDHEnabled() {
counters := []string{
@ -302,8 +310,8 @@ func (c *Collector) Build(logger *slog.Logger, wmiClient *wmi.Client) error {
}
type WorkerProcess struct {
AppPoolName string
ProcessId uint64
AppPoolName string `mi:"AppPoolName"`
ProcessId uint64 `mi:"ProcessId"`
}
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
@ -333,10 +341,8 @@ func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch ch
var workerProcesses []WorkerProcess
if c.config.EnableWorkerProcess {
if err := c.wmiClient.Query("SELECT * FROM WorkerProcess", &workerProcesses, nil, "root\\WebAdministration"); err != nil {
logger.Debug("Could not query WebAdministration namespace for IIS worker processes",
slog.Any("err", err),
)
if err := c.miSession.Query(&workerProcesses, mi.NamespaceRootWebAdministration, c.workerProcessMIQueryQuery); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
}
@ -540,10 +546,8 @@ func (c *Collector) collectPDH(logger *slog.Logger, ch chan<- prometheus.Metric)
var workerProcesses []WorkerProcess
if c.config.EnableWorkerProcess {
if err := c.wmiClient.Query("SELECT * FROM WorkerProcess", &workerProcesses, nil, "root\\WebAdministration"); err != nil {
logger.Debug("Could not query WebAdministration namespace for IIS worker processes",
slog.Any("err", err),
)
if err := c.miSession.Query(&workerProcesses, mi.NamespaceRootWebAdministration, c.workerProcessMIQueryQuery); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
}

View File

@ -7,11 +7,11 @@ import (
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "remote_fx"
@ -81,7 +81,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(*slog.Logger, *wmi.Client) error {
func (c *Collector) Build(*slog.Logger, *mi.Session) error {
// net
c.baseTCPRTT = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "net_base_tcp_rtt_seconds"),

View File

@ -13,9 +13,9 @@ import (
"github.com/alecthomas/kingpin/v2"
"github.com/go-ole/go-ole"
"github.com/go-ole/go-ole/oleutil"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "scheduled_task"
@ -148,7 +148,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
initErrCh := make(chan error)
c.scheduledTasksReqCh = make(chan struct{})
c.scheduledTasksCh = make(chan *scheduledTaskResults)
@ -281,7 +281,7 @@ func (c *Collector) initializeScheduleService(initErrCh chan<- error) {
if err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED); err != nil {
var oleCode *ole.OleError
if errors.As(err, &oleCode) && oleCode.Code() != ole.S_OK && oleCode.Code() != wmi.S_FALSE {
if errors.As(err, &oleCode) && oleCode.Code() != ole.S_OK && oleCode.Code() != 0x00000001 {
initErrCh <- err
return

View File

@ -11,9 +11,9 @@ import (
"unsafe"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/svc/mgr"
)
@ -106,7 +106,7 @@ func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
return []string{}, nil
}
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
logger = logger.With(slog.String("collector", Name))
if c.config.ServiceInclude.String() == "^(?:.*)$" && c.config.ServiceExclude.String() == "^(?:)$" {

View File

@ -7,10 +7,10 @@ import (
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "smb"
@ -56,7 +56,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
// desc creates a new prometheus description
desc := func(metricName string, description string, labels ...string) *prometheus.Desc {
return prometheus.NewDesc(

View File

@ -7,11 +7,11 @@ import (
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const (
@ -79,7 +79,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
// desc creates a new prometheus description
desc := func(metricName string, description string, labels []string) *prometheus.Desc {
return prometheus.NewDesc(

View File

@ -8,10 +8,10 @@ import (
"regexp"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "smtp"
@ -141,10 +141,10 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
logger = logger.With(slog.String("collector", Name))
logger.Info("smtp collector is in an experimental state! Metrics for this collector have not been tested.")
func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
logger.Info("smtp collector is in an experimental state! Metrics for this collector have not been tested.",
slog.String("collector", Name),
)
c.badMailedMessagesBadPickupFileTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "badmailed_messages_bad_pickup_file_total"),

View File

@ -7,10 +7,10 @@ import (
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "system"
@ -61,7 +61,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
c.contextSwitchesTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "context_switches_total"),
"Total number of context switches (WMI source: PerfOS_System.ContextSwitchesPersec)",

View File

@ -10,11 +10,11 @@ import (
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/headers/iphlpapi"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
"golang.org/x/sys/windows"
)
@ -100,7 +100,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
counters := []string{
connectionFailures,
connectionsActive,

View File

@ -11,10 +11,11 @@ import (
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/headers/wtsapi32"
"github.com/prometheus-community/windows_exporter/internal/mi"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
"golang.org/x/sys/windows"
)
@ -31,9 +32,9 @@ type Win32_ServerFeature struct {
ID uint32
}
func isConnectionBrokerServer(logger *slog.Logger, wmiClient *wmi.Client) bool {
func isConnectionBrokerServer(logger *slog.Logger, miSession *mi.Session) bool {
var dst []Win32_ServerFeature
if err := wmiClient.Query("SELECT * FROM Win32_ServerFeature", &dst); err != nil {
if err := miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * FROM Win32_ServerFeature"))); err != nil {
return false
}
@ -112,10 +113,14 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(logger *slog.Logger, wmiClient *wmi.Client) error {
func (c *Collector) Build(logger *slog.Logger, miSession *mi.Session) error {
if miSession == nil {
return errors.New("miSession is nil")
}
logger = logger.With(slog.String("collector", Name))
c.connectionBrokerEnabled = isConnectionBrokerServer(logger, wmiClient)
c.connectionBrokerEnabled = isConnectionBrokerServer(logger, miSession)
c.sessionInfo = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "session_info"),

View File

@ -29,11 +29,11 @@ import (
"github.com/alecthomas/kingpin/v2"
"github.com/dimchansky/utfbom"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
"github.com/yusufpapurcu/wmi"
)
const Name = "textfile"
@ -104,7 +104,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
logger.Info("textfile Collector directories: "+strings.Join(c.config.TextFileDirectories, ","),
slog.String("collector", Name),
)

View File

@ -4,12 +4,13 @@ package thermalzone
import (
"errors"
"fmt"
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "thermalzone"
@ -21,7 +22,8 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI Win32_PerfRawData_Counters_ThermalZoneInformation metrics.
type Collector struct {
config Config
wmiClient *wmi.Client
miSession *mi.Session
miQuery mi.Query
percentPassiveLimit *prometheus.Desc
temperature *prometheus.Desc
@ -56,12 +58,19 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error {
if miSession == nil {
return errors.New("miSession is nil")
}
c.wmiClient = wmiClient
miQuery, err := mi.NewQuery("SELECT Name, HighPrecisionTemperature, PercentPassiveLimit, ThrottleReasons FROM Win32_PerfRawData_Counters_ThermalZoneInformation")
if err != nil {
return fmt.Errorf("failed to create WMI query: %w", err)
}
c.miQuery = miQuery
c.miSession = miSession
c.temperature = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "temperature_celsius"),
"(Temperature)",
@ -108,22 +117,20 @@ func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan
// Win32_PerfRawData_Counters_ThermalZoneInformation docs:
// https://wutils.com/wmi/root/cimv2/win32_perfrawdata_counters_thermalzoneinformation/
type Win32_PerfRawData_Counters_ThermalZoneInformation struct {
Name string
HighPrecisionTemperature uint32
PercentPassiveLimit uint32
ThrottleReasons uint32
Name string `mi:"Name"`
HighPrecisionTemperature uint32 `mi:"HighPrecisionTemperature"`
PercentPassiveLimit uint32 `mi:"PercentPassiveLimit"`
ThrottleReasons uint32 `mi:"ThrottleReasons"`
}
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_Counters_ThermalZoneInformation
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_Counters_ThermalZoneInformation", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, c.miQuery); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
// ThermalZone collector has been known to 'successfully' return an empty result.
if len(dst) == 0 {
return errors.New("empty results set for collector")
return errors.New("WMI query returned empty result set")
}
for _, info := range dst {

View File

@ -9,10 +9,10 @@ import (
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/headers/kernel32"
"github.com/prometheus-community/windows_exporter/internal/mi"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
"golang.org/x/sys/windows"
)
@ -64,7 +64,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
c.currentTime = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "current_timestamp_seconds"),
"OperatingSystem.LocalDateTime",

View File

@ -15,9 +15,9 @@ import (
"github.com/alecthomas/kingpin/v2"
"github.com/go-ole/go-ole"
"github.com/go-ole/go-ole/oleutil"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "update"
@ -80,7 +80,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
logger = logger.With(slog.String("collector", Name))
logger.Info("update collector is in an experimental state! The configuration and metrics may change in future. Please report any issues.")
@ -147,7 +147,7 @@ func (c *Collector) scheduleUpdateStatus(logger *slog.Logger, initErrCh chan<- e
if err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED); err != nil {
var oleCode *ole.OleError
if errors.As(err, &oleCode) && oleCode.Code() != ole.S_OK && oleCode.Code() != wmi.S_FALSE {
if errors.As(err, &oleCode) && oleCode.Code() != ole.S_OK && oleCode.Code() != 0x00000001 {
initErrCh <- fmt.Errorf("CoInitializeEx: %w", err)
return

View File

@ -4,13 +4,14 @@ package vmware
import (
"errors"
"fmt"
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "vmware"
@ -22,7 +23,9 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI Win32_PerfRawData_vmGuestLib_VMem/Win32_PerfRawData_vmGuestLib_VCPU metrics.
type Collector struct {
config Config
wmiClient *wmi.Client
miSession *mi.Session
miQueryCPU mi.Query
miQueryMem mi.Query
memActive *prometheus.Desc
memBallooned *prometheus.Desc
@ -74,12 +77,25 @@ func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error {
if miSession == nil {
return errors.New("miSession is nil")
}
c.wmiClient = wmiClient
miQuery, err := mi.NewQuery("SELECT * FROM Win32_PerfRawData_vmGuestLib_VCPU")
if err != nil {
return fmt.Errorf("failed to create WMI query: %w", err)
}
c.miQueryCPU = miQuery
miQuery, err = mi.NewQuery("SELECT * FROM Win32_PerfRawData_vmGuestLib_VMem")
if err != nil {
return fmt.Errorf("failed to create WMI query: %w", err)
}
c.miQueryMem = miQuery
c.miSession = miSession
c.memActive = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_active_bytes"),
@ -224,34 +240,34 @@ func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan
}
type Win32_PerfRawData_vmGuestLib_VMem struct {
MemActiveMB uint64
MemBalloonedMB uint64
MemLimitMB uint64
MemMappedMB uint64
MemOverheadMB uint64
MemReservationMB uint64
MemSharedMB uint64
MemSharedSavedMB uint64
MemShares uint64
MemSwappedMB uint64
MemTargetSizeMB uint64
MemUsedMB uint64
MemActiveMB uint64 `mi:"MemActiveMB"`
MemBalloonedMB uint64 `mi:"MemBalloonedMB"`
MemLimitMB uint64 `mi:"MemLimitMB"`
MemMappedMB uint64 `mi:"MemMappedMB"`
MemOverheadMB uint64 `mi:"MemOverheadMB"`
MemReservationMB uint64 `mi:"MemReservationMB"`
MemSharedMB uint64 `mi:"MemSharedMB"`
MemSharedSavedMB uint64 `mi:"MemSharedSavedMB"`
MemShares uint64 `mi:"MemShares"`
MemSwappedMB uint64 `mi:"MemSwappedMB"`
MemTargetSizeMB uint64 `mi:"MemTargetSizeMB"`
MemUsedMB uint64 `mi:"MemUsedMB"`
}
type Win32_PerfRawData_vmGuestLib_VCPU struct {
CpuLimitMHz uint64
CpuReservationMHz uint64
CpuShares uint64
CpuStolenMs uint64
CpuTimePercents uint64
EffectiveVMSpeedMHz uint64
HostProcessorSpeedMHz uint64
CpuLimitMHz uint64 `mi:"CpuLimitMHz"`
CpuReservationMHz uint64 `mi:"CpuReservationMHz"`
CpuShares uint64 `mi:"CpuShares"`
CpuStolenMs uint64 `mi:"CpuStolenMs"`
CpuTimePercents uint64 `mi:"CpuTimePercents"`
EffectiveVMSpeedMHz uint64 `mi:"EffectiveVMSpeedMHz"`
HostProcessorSpeedMHz uint64 `mi:"HostProcessorSpeedMHz"`
}
func (c *Collector) collectMem(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_vmGuestLib_VMem
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_vmGuestLib_VMem", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, c.miQueryMem); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
if len(dst) == 0 {
@ -333,14 +349,15 @@ func (c *Collector) collectMem(ch chan<- prometheus.Metric) error {
return nil
}
// mbToBytes moved to utils package
func mbToBytes(mb uint64) float64 {
return float64(mb * 1024 * 1024)
}
func (c *Collector) collectCpu(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_vmGuestLib_VCPU
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_vmGuestLib_VCPU", &dst); err != nil {
return err
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, c.miQueryCPU); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
if len(dst) == 0 {

View File

@ -129,7 +129,7 @@ func (c *MetricsHTTPHandler) handlerFactory(logger *slog.Logger, scrapeTimeout t
metricCollectors = &collector.MetricCollectors{
Collectors: filteredCollectors,
WMIClient: c.metricCollectors.WMIClient,
MISession: c.metricCollectors.MISession,
PerfCounterQuery: c.metricCollectors.PerfCounterQuery,
}
}

283
internal/mi/application.go Normal file
View File

@ -0,0 +1,283 @@
//go:build windows
package mi
import (
"errors"
"fmt"
"syscall"
"time"
"unsafe"
"golang.org/x/sys/windows"
)
const (
applicationID = "windows_exporter"
LocaleEnglish = "en-us"
)
var (
// DestinationOptionsTimeout is the key for the timeout option.
//
// https://github.com/microsoft/win32metadata/blob/527806d20d83d3abd43d16cd3fa8795d8deba343/generation/WinSDK/RecompiledIdlHeaders/um/mi.h#L7830
DestinationOptionsTimeout = UTF16PtrFromString[*uint16]("__MI_DESTINATIONOPTIONS_TIMEOUT")
// DestinationOptionsUILocale is the key for the UI locale option.
//
// https://github.com/microsoft/win32metadata/blob/527806d20d83d3abd43d16cd3fa8795d8deba343/generation/WinSDK/RecompiledIdlHeaders/um/mi.h#L8248
DestinationOptionsUILocale = UTF16PtrFromString[*uint16]("__MI_DESTINATIONOPTIONS_UI_LOCALE")
)
var (
modMi = windows.NewLazySystemDLL("mi.dll")
procMIApplicationInitialize = modMi.NewProc("MI_Application_InitializeV1")
)
// Application represents the MI application.
// https://learn.microsoft.com/de-de/windows/win32/api/mi/ns-mi-mi_application
type Application struct {
reserved1 uint64
reserved2 uintptr
ft *ApplicationFT
}
// ApplicationFT represents the function table of the MI application.
// https://learn.microsoft.com/de-de/windows/win32/api/mi/ns-mi-mi_applicationft
type ApplicationFT struct {
Close uintptr
NewSession uintptr
NewHostedProvider uintptr
NewInstance uintptr
NewDestinationOptions uintptr
NewOperationOptions uintptr
NewSubscriptionDeliveryOptions uintptr
NewSerializer uintptr
NewDeserializer uintptr
NewInstanceFromClass uintptr
NewClass uintptr
}
type DestinationOptions struct {
reserved1 uint64
reserved2 uintptr
ft *DestinationOptionsFT
}
type DestinationOptionsFT struct {
Delete uintptr
SetString uintptr
SetNumber uintptr
AddCredentials uintptr
GetString uintptr
GetNumber uintptr
GetOptionCount uintptr
GetOptionAt uintptr
GetOption uintptr
GetCredentialsCount uintptr
GetCredentialsAt uintptr
GetCredentialsPasswordAt uintptr
Clone uintptr
SetInterval uintptr
GetInterval uintptr
}
// Application_Initialize initializes the MI [Application].
// It is recommended to have only one Application per process.
//
// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_application_initializev1
func Application_Initialize() (*Application, error) {
application := &Application{}
applicationId, err := windows.UTF16PtrFromString(applicationID)
if err != nil {
return nil, err
}
r0, _, err := procMIApplicationInitialize.Call(
0,
uintptr(unsafe.Pointer(applicationId)),
0,
uintptr(unsafe.Pointer(application)),
)
if !errors.Is(err, windows.NOERROR) {
return nil, fmt.Errorf("syscall returned: %w", err)
}
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return nil, result
}
return application, nil
}
// Close deinitializes the management infrastructure client API that was initialized through a call to Application_Initialize.
//
// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_application_close
func (application *Application) Close() error {
if application == nil || application.ft == nil {
return ErrNotInitialized
}
r0, _, _ := syscall.SyscallN(application.ft.Close, uintptr(unsafe.Pointer(application)))
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return result
}
return nil
}
// NewSession creates a session used to share connections for a set of operations to a single destination.
//
// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_application_newsession
func (application *Application) NewSession(options *DestinationOptions) (*Session, error) {
if application == nil || application.ft == nil {
return nil, ErrNotInitialized
}
session := &Session{}
r0, _, _ := syscall.SyscallN(
application.ft.NewSession,
uintptr(unsafe.Pointer(application)),
0,
0,
uintptr(unsafe.Pointer(options)),
0,
0,
uintptr(unsafe.Pointer(session)),
)
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return nil, result
}
defaultOperationOptions, err := application.NewOperationOptions()
if err != nil {
return nil, fmt.Errorf("failed to create default operation options: %w", err)
}
if err = defaultOperationOptions.SetTimeout(5 * time.Second); err != nil {
return nil, fmt.Errorf("failed to set timeout: %w", err)
}
session.defaultOperationOptions = defaultOperationOptions
return session, nil
}
// NewOperationOptions creates an OperationOptions object that can be used with the operation functions on the Session object.
//
// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_application_newoperationoptions
func (application *Application) NewOperationOptions() (*OperationOptions, error) {
if application == nil || application.ft == nil {
return nil, ErrNotInitialized
}
operationOptions := &OperationOptions{}
mustUnderstand := True
r0, _, _ := syscall.SyscallN(
application.ft.NewOperationOptions,
uintptr(unsafe.Pointer(application)),
uintptr(mustUnderstand),
uintptr(unsafe.Pointer(operationOptions)),
)
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return nil, result
}
return operationOptions, nil
}
// NewDestinationOptions creates an DestinationOptions object that can be used with the Application.NewSession function.
//
// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_application_newdestinationoptions
func (application *Application) NewDestinationOptions() (*DestinationOptions, error) {
if application == nil || application.ft == nil {
return nil, ErrNotInitialized
}
operationOptions := &DestinationOptions{}
r0, _, _ := syscall.SyscallN(
application.ft.NewDestinationOptions,
uintptr(unsafe.Pointer(application)),
uintptr(unsafe.Pointer(operationOptions)),
)
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return nil, result
}
return operationOptions, nil
}
// SetTimeout sets the timeout for the destination options.
//
// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_destinationoptions_settimeout
func (do *DestinationOptions) SetTimeout(timeout time.Duration) error {
if do == nil || do.ft == nil {
return ErrNotInitialized
}
r0, _, _ := syscall.SyscallN(
do.ft.SetInterval,
uintptr(unsafe.Pointer(do)),
uintptr(unsafe.Pointer(DestinationOptionsTimeout)),
uintptr(unsafe.Pointer(NewInterval(timeout))),
0,
)
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return result
}
return nil
}
// SetLocale sets the locale for the destination options.
//
// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_destinationoptions_setuilocale
func (do *DestinationOptions) SetLocale(locale string) error {
if do == nil || do.ft == nil {
return ErrNotInitialized
}
localeUTF16, err := windows.UTF16PtrFromString(locale)
if err != nil {
return fmt.Errorf("failed to convert locale: %w", err)
}
r0, _, _ := syscall.SyscallN(
do.ft.SetString,
uintptr(unsafe.Pointer(do)),
uintptr(unsafe.Pointer(DestinationOptionsUILocale)),
uintptr(unsafe.Pointer(localeUTF16)),
0,
)
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return result
}
return nil
}
func (do *DestinationOptions) Delete() error {
r0, _, _ := syscall.SyscallN(
do.ft.Delete,
uintptr(unsafe.Pointer(do)),
)
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return result
}
return nil
}

154
internal/mi/callbacks.go Normal file
View File

@ -0,0 +1,154 @@
//go:build windows
package mi
import (
"errors"
"fmt"
"reflect"
"sync"
"unsafe"
"golang.org/x/sys/windows"
)
// We have to registry a global callback function, since the amount of callbacks is limited.
var operationUnmarshalCallbacksInstanceResult = sync.OnceValue[uintptr](func() uintptr {
return windows.NewCallback(func(
operation *Operation,
callbacks *OperationUnmarshalCallbacks,
instance *Instance,
moreResults Boolean,
instanceResult ResultError,
errorMessageUTF16 *uint16,
errorDetails *Instance,
_ uintptr,
) uintptr {
if moreResults == False {
defer operation.Close()
}
return callbacks.InstanceResult(operation, instance, moreResults, instanceResult, errorMessageUTF16, errorDetails)
})
})
type OperationUnmarshalCallbacks struct {
dst any
dv reflect.Value
errCh chan<- error
elemType reflect.Type
elemValue reflect.Value
}
func NewUnmarshalOperationsCallbacks(dst any, errCh chan<- error) (*OperationCallbacks[OperationUnmarshalCallbacks], error) {
dv := reflect.ValueOf(dst)
if dv.Kind() != reflect.Ptr || dv.IsNil() {
return nil, ErrInvalidEntityType
}
dv = dv.Elem()
elemType := dv.Type().Elem()
elemValue := reflect.ValueOf(reflect.New(elemType).Interface()).Elem()
if dv.Kind() != reflect.Slice || elemType.Kind() != reflect.Struct {
return nil, ErrInvalidEntityType
}
dv.Set(reflect.MakeSlice(dv.Type(), 0, 0))
return &OperationCallbacks[OperationUnmarshalCallbacks]{
CallbackContext: &OperationUnmarshalCallbacks{
errCh: errCh,
dst: dst,
dv: dv,
elemType: elemType,
elemValue: elemValue,
},
InstanceResult: operationUnmarshalCallbacksInstanceResult(),
}, nil
}
func (o *OperationUnmarshalCallbacks) InstanceResult(
_ *Operation,
instance *Instance,
moreResults Boolean,
instanceResult ResultError,
errorMessageUTF16 *uint16,
_ *Instance,
) uintptr {
defer func() {
if moreResults == False {
close(o.errCh)
}
}()
if !errors.Is(instanceResult, MI_RESULT_OK) {
o.errCh <- fmt.Errorf("%w: %s", instanceResult, windows.UTF16PtrToString(errorMessageUTF16))
return 0
}
if instance == nil {
return 0
}
counter, err := instance.GetElementCount()
if err != nil {
o.errCh <- fmt.Errorf("failed to get element count: %w", err)
return 0
}
if counter == 0 {
return 0
}
for i := range o.elemType.NumField() {
field := o.elemValue.Field(i)
// Check if the field has an `mi` tag
miTag := o.elemType.Field(i).Tag.Get("mi")
if miTag == "" {
continue
}
element, err := instance.GetElement(miTag)
if err != nil {
o.errCh <- fmt.Errorf("failed to get element: %w", err)
return 0
}
switch element.valueType {
case ValueTypeBOOLEAN:
field.SetBool(element.value == 1)
case ValueTypeUINT8, ValueTypeUINT16, ValueTypeUINT32, ValueTypeUINT64:
field.SetUint(uint64(element.value))
case ValueTypeSINT8, ValueTypeSINT16, ValueTypeSINT32, ValueTypeSINT64:
field.SetInt(int64(element.value))
case ValueTypeSTRING:
if element.value == 0 {
o.errCh <- fmt.Errorf("%s: invalid pointer: value is nil", miTag)
return 0
}
// Convert the UTF-16 string to a Go string
stringValue := windows.UTF16PtrToString((*uint16)(unsafe.Pointer(element.value)))
field.SetString(stringValue)
case ValueTypeREAL32, ValueTypeREAL64:
field.SetFloat(float64(element.value))
default:
o.errCh <- fmt.Errorf("unsupported value type: %d", element.valueType)
return 0
}
}
o.dv.Set(reflect.Append(o.dv, o.elemValue))
return 0
}

7
internal/mi/doc.go Normal file
View File

@ -0,0 +1,7 @@
//go:build windows
// mi is a package that provides a Go API for Windows Management Infrastructure (MI) functions.
// It requires Windows Management Framework 3.0 or later.
//
// https://learn.microsoft.com/de-de/previous-versions/windows/desktop/wmi_v2/why-use-mi-
package mi

10
internal/mi/errors.go Normal file
View File

@ -0,0 +1,10 @@
//go:build windows
package mi
import "errors"
var (
ErrNotInitialized = errors.New("not initialized")
ErrInvalidEntityType = errors.New("invalid entity type")
)

179
internal/mi/instance.go Normal file
View File

@ -0,0 +1,179 @@
//go:build windows
package mi
import (
"errors"
"fmt"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
type Instance struct {
ft *InstanceFT
classDecl *ClassDecl
serverName *uint16
nameSpace *uint16
_ [4]uintptr
}
type InstanceFT struct {
Clone uintptr
Destruct uintptr
Delete uintptr
IsA uintptr
GetClassName uintptr
SetNameSpace uintptr
GetNameSpace uintptr
GetElementCount uintptr
AddElement uintptr
SetElement uintptr
SetElementAt uintptr
GetElement uintptr
GetElementAt uintptr
ClearElement uintptr
ClearElementAt uintptr
GetServerName uintptr
SetServerName uintptr
GetClass uintptr
}
type ClassDecl struct {
Flags uint32
Code uint32
Name *uint16
Mqualifiers uintptr
NumQualifiers uint32
Mproperties uintptr
NumProperties uint32
Size uint32
SuperClass *uint16
SuperClassDecl uintptr
Methods uintptr
NumMethods uint32
Schema uintptr
ProviderFT uintptr
OwningClass uintptr
}
func (instance *Instance) Delete() error {
if instance == nil || instance.ft == nil {
return ErrNotInitialized
}
r0, _, _ := syscall.SyscallN(instance.ft.Delete, uintptr(unsafe.Pointer(instance)))
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return result
}
return nil
}
func (instance *Instance) GetElement(elementName string) (*Element, error) {
if instance == nil || instance.ft == nil {
return nil, ErrNotInitialized
}
elementNameUTF16, err := windows.UTF16PtrFromString(elementName)
if err != nil {
return nil, fmt.Errorf("failed to convert element name %s to UTF-16: %w", elementName, err)
}
var (
value uintptr
valueType ValueType
)
r0, _, _ := syscall.SyscallN(
instance.ft.GetElement,
uintptr(unsafe.Pointer(instance)),
uintptr(unsafe.Pointer(elementNameUTF16)),
uintptr(unsafe.Pointer(&value)),
uintptr(unsafe.Pointer(&valueType)),
0,
0,
)
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return nil, result
}
return &Element{
value: value,
valueType: valueType,
}, nil
}
func (instance *Instance) GetElementCount() (uint32, error) {
if instance == nil || instance.ft == nil {
return 0, ErrNotInitialized
}
var count uint32
r0, _, _ := syscall.SyscallN(
instance.ft.GetElementCount,
uintptr(unsafe.Pointer(instance)),
uintptr(unsafe.Pointer(&count)),
)
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return 0, result
}
return count, nil
}
func (instance *Instance) GetClassName() (string, error) {
if instance == nil || instance.ft == nil {
return "", ErrNotInitialized
}
var classNameUTF16 *uint16
r0, _, _ := syscall.SyscallN(
instance.ft.GetClassName,
uintptr(unsafe.Pointer(instance)),
uintptr(unsafe.Pointer(&classNameUTF16)),
)
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return "", result
}
if classNameUTF16 == nil {
return "", errors.New("class name is nil")
}
return windows.UTF16PtrToString(classNameUTF16), nil
}
func Instance_Print(instance *Instance) (string, error) {
elementMap := map[string]any{}
properties := instance.classDecl.Properties()
count, err := instance.GetElementCount()
if err != nil {
return "", err
}
if count == 0 {
return "", nil
}
for _, property := range properties {
name := windows.UTF16PtrToString(property.Name)
element, _ := instance.GetElement(name)
value, _ := element.GetValue()
elementMap[windows.UTF16PtrToString(property.Name)] = value
}
return fmt.Sprintf("%v", elementMap), nil
}

View File

@ -0,0 +1,43 @@
//go:build windows
package mi_test
import (
"testing"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/stretchr/testify/require"
)
func Benchmark_MI_Query_Unmarshal(b *testing.B) {
application, err := mi.Application_Initialize()
require.NoError(b, err)
require.NotEmpty(b, application)
session, err := application.NewSession(nil)
require.NoError(b, err)
require.NotEmpty(b, session)
b.ResetTimer()
var processes []win32Process
query, err := mi.NewQuery("SELECT Name FROM Win32_Process WHERE Handle = 0 OR Handle = 4")
require.NoError(b, err)
for i := 0; i < b.N; i++ {
err := session.QueryUnmarshal(&processes, mi.OperationFlagsStandardRTTI, nil, mi.NamespaceRootCIMv2, mi.QueryDialectWQL, query)
require.NoError(b, err)
require.Equal(b, []win32Process{{Name: "System Idle Process"}, {Name: "System"}}, processes)
}
b.StopTimer()
err = session.Close()
require.NoError(b, err)
err = application.Close()
require.NoError(b, err)
b.ReportAllocs()
}

294
internal/mi/mi_test.go Normal file
View File

@ -0,0 +1,294 @@
//go:build windows
package mi_test
import (
"testing"
"time"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/testutils"
"github.com/stretchr/testify/require"
"golang.org/x/sys/windows"
)
type win32Process struct {
Name string `mi:"Name"`
}
type wmiPrinter struct {
Name string `mi:"Name"`
Default bool `mi:"Default"`
PrinterStatus uint16 `mi:"PrinterStatus"`
JobCountSinceLastReset uint32 `mi:"JobCountSinceLastReset"`
}
type wmiPrintJob struct {
Name string `mi:"Name"`
Status string `mi:"Status"`
}
func Test_MI_Application_Initialize(t *testing.T) {
application, err := mi.Application_Initialize()
require.NoError(t, err)
require.NotEmpty(t, application)
err = application.Close()
require.NoError(t, err)
}
func Test_MI_Application_TestConnection(t *testing.T) {
application, err := mi.Application_Initialize()
require.NoError(t, err)
require.NotEmpty(t, application)
destinationOptions, err := application.NewDestinationOptions()
require.NoError(t, err)
require.NotEmpty(t, destinationOptions)
err = destinationOptions.SetTimeout(1 * time.Second)
require.NoError(t, err)
err = destinationOptions.SetLocale(mi.LocaleEnglish)
require.NoError(t, err)
session, err := application.NewSession(destinationOptions)
require.NoError(t, err)
require.NotEmpty(t, session)
err = session.TestConnection()
require.NoError(t, err)
require.NotEmpty(t, session)
err = session.Close()
require.NoError(t, err)
err = application.Close()
require.NoError(t, err)
}
func Test_MI_Query(t *testing.T) {
application, err := mi.Application_Initialize()
require.NoError(t, err)
require.NotEmpty(t, application)
destinationOptions, err := application.NewDestinationOptions()
require.NoError(t, err)
require.NotEmpty(t, destinationOptions)
err = destinationOptions.SetTimeout(1 * time.Second)
require.NoError(t, err)
err = destinationOptions.SetLocale(mi.LocaleEnglish)
require.NoError(t, err)
session, err := application.NewSession(destinationOptions)
require.NoError(t, err)
require.NotEmpty(t, session)
operation, err := session.QueryInstances(mi.OperationFlagsStandardRTTI, nil, mi.NamespaceRootCIMv2, mi.QueryDialectWQL, "select Name from win32_process where handle = 0")
require.NoError(t, err)
require.NotEmpty(t, operation)
instance, moreResults, err := operation.GetInstance()
require.NoError(t, err)
require.NotEmpty(t, instance)
count, err := instance.GetElementCount()
require.NoError(t, err)
require.NotZero(t, count)
element, err := instance.GetElement("Name")
require.NoError(t, err)
require.NotEmpty(t, element)
value, err := element.GetValue()
require.NoError(t, err)
require.Equal(t, "System Idle Process", value)
require.NotEmpty(t, value)
require.False(t, moreResults)
err = operation.Close()
require.NoError(t, err)
err = session.Close()
require.NoError(t, err)
err = application.Close()
require.NoError(t, err)
}
func Test_MI_QueryUnmarshal(t *testing.T) {
application, err := mi.Application_Initialize()
require.NoError(t, err)
require.NotEmpty(t, application)
destinationOptions, err := application.NewDestinationOptions()
require.NoError(t, err)
require.NotEmpty(t, destinationOptions)
err = destinationOptions.SetTimeout(1 * time.Second)
require.NoError(t, err)
err = destinationOptions.SetLocale(mi.LocaleEnglish)
require.NoError(t, err)
session, err := application.NewSession(destinationOptions)
require.NoError(t, err)
require.NotEmpty(t, session)
var processes []win32Process
queryProcess, err := mi.NewQuery("select Name from win32_process where handle = 0")
require.NoError(t, err)
err = session.QueryUnmarshal(&processes, mi.OperationFlagsStandardRTTI, nil, mi.NamespaceRootCIMv2, mi.QueryDialectWQL, queryProcess)
require.NoError(t, err)
require.Equal(t, []win32Process{{Name: "System Idle Process"}}, processes)
err = session.Close()
require.NoError(t, err)
err = application.Close()
require.NoError(t, err)
}
func Test_MI_EmptyQuery(t *testing.T) {
application, err := mi.Application_Initialize()
require.NoError(t, err)
require.NotEmpty(t, application)
destinationOptions, err := application.NewDestinationOptions()
require.NoError(t, err)
require.NotEmpty(t, destinationOptions)
err = destinationOptions.SetTimeout(1 * time.Second)
require.NoError(t, err)
err = destinationOptions.SetLocale(mi.LocaleEnglish)
require.NoError(t, err)
session, err := application.NewSession(destinationOptions)
require.NoError(t, err)
require.NotEmpty(t, session)
operation, err := session.QueryInstances(mi.OperationFlagsStandardRTTI, nil, mi.NamespaceRootCIMv2, mi.QueryDialectWQL, "SELECT Name, Status FROM win32_PrintJob")
require.NoError(t, err)
require.NotEmpty(t, operation)
instance, moreResults, err := operation.GetInstance()
require.NoError(t, err)
require.Empty(t, instance)
require.False(t, moreResults)
err = operation.Close()
require.NoError(t, err)
err = session.Close()
require.NoError(t, err)
err = application.Close()
require.NoError(t, err)
}
func Test_MI_Query_Unmarshal(t *testing.T) {
application, err := mi.Application_Initialize()
require.NoError(t, err)
require.NotEmpty(t, application)
destinationOptions, err := application.NewDestinationOptions()
require.NoError(t, err)
require.NotEmpty(t, destinationOptions)
err = destinationOptions.SetTimeout(1 * time.Second)
require.NoError(t, err)
err = destinationOptions.SetLocale(mi.LocaleEnglish)
require.NoError(t, err)
session, err := application.NewSession(destinationOptions)
require.NoError(t, err)
require.NotEmpty(t, session)
operation, err := session.QueryInstances(mi.OperationFlagsStandardRTTI, nil, mi.NamespaceRootCIMv2, mi.QueryDialectWQL, "SELECT Name FROM Win32_Process WHERE Handle = 0 OR Handle = 4")
require.NoError(t, err)
require.NotEmpty(t, operation)
var processes []win32Process
err = operation.Unmarshal(&processes)
require.NoError(t, err)
require.Equal(t, []win32Process{{Name: "System Idle Process"}, {Name: "System"}}, processes)
err = operation.Close()
require.NoError(t, err)
err = session.Close()
require.NoError(t, err)
err = application.Close()
require.NoError(t, err)
}
func Test_MI_FD_Leak(t *testing.T) {
t.Skip("This test is disabled because it is not deterministic and may fail on some systems.")
application, err := mi.Application_Initialize()
require.NoError(t, err)
require.NotEmpty(t, application)
session, err := application.NewSession(nil)
require.NoError(t, err)
require.NotEmpty(t, session)
currentFileHandle, err := testutils.GetProcessHandleCount(windows.CurrentProcess())
require.NoError(t, err)
t.Log("Current File Handle Count: ", currentFileHandle)
queryPrinter, err := mi.NewQuery("SELECT Name, Default, PrinterStatus, JobCountSinceLastReset FROM win32_Printer")
require.NoError(t, err)
queryPrinterJob, err := mi.NewQuery("SELECT Name, Status FROM win32_PrintJob")
require.NoError(t, err)
for range 1000 {
var wmiPrinters []wmiPrinter
err := session.Query(&wmiPrinters, mi.NamespaceRootCIMv2, queryPrinter)
require.NoError(t, err)
var wmiPrintJobs []wmiPrintJob
err = session.Query(&wmiPrintJobs, mi.NamespaceRootCIMv2, queryPrinterJob)
require.NoError(t, err)
currentFileHandle, err = testutils.GetProcessHandleCount(windows.CurrentProcess())
require.NoError(t, err)
t.Log("Current File Handle Count: ", currentFileHandle)
}
currentFileHandle, err = testutils.GetProcessHandleCount(windows.CurrentProcess())
require.NoError(t, err)
t.Log("Current File Handle Count: ", currentFileHandle)
err = session.Close()
require.NoError(t, err)
currentFileHandle, err = testutils.GetProcessHandleCount(windows.CurrentProcess())
require.NoError(t, err)
t.Log("Current File Handle Count: ", currentFileHandle)
err = application.Close()
require.NoError(t, err)
currentFileHandle, err = testutils.GetProcessHandleCount(windows.CurrentProcess())
require.NoError(t, err)
t.Log("Current File Handle Count: ", currentFileHandle)
}

272
internal/mi/operation.go Normal file
View File

@ -0,0 +1,272 @@
//go:build windows
package mi
import (
"errors"
"fmt"
"reflect"
"syscall"
"time"
"unsafe"
"golang.org/x/sys/windows"
)
// OperationOptionsTimeout is the key for the timeout option.
//
// https://github.com/microsoft/win32metadata/blob/527806d20d83d3abd43d16cd3fa8795d8deba343/generation/WinSDK/RecompiledIdlHeaders/um/mi.h#L9240
var OperationOptionsTimeout = UTF16PtrFromString[*uint16]("__MI_OPERATIONOPTIONS_TIMEOUT")
// OperationFlags represents the flags for an operation.
//
// https://learn.microsoft.com/en-us/previous-versions/windows/desktop/wmi_v2/mi-flags
type OperationFlags uint32
const (
OperationFlagsDefaultRTTI OperationFlags = 0x0000
OperationFlagsBasicRTTI OperationFlags = 0x0002
OperationFlagsNoRTTI OperationFlags = 0x0400
OperationFlagsStandardRTTI OperationFlags = 0x0800
OperationFlagsFullRTTI OperationFlags = 0x0004
)
// Operation represents an operation.
// https://learn.microsoft.com/en-us/windows/win32/api/mi/ns-mi-mi_operation
type Operation struct {
reserved1 uint64
reserved2 uintptr
ft *OperationFT
}
// OperationFT represents the function table for Operation.
// https://learn.microsoft.com/en-us/windows/win32/api/mi/ns-mi-mi_operationft
type OperationFT struct {
Close uintptr
Cancel uintptr
GetSession uintptr
GetInstance uintptr
GetIndication uintptr
GetClass uintptr
}
type OperationOptions struct {
reserved1 uint64
reserved2 uintptr
ft *OperationOptionsFT
}
type OperationOptionsFT struct {
Delete uintptr
SetString uintptr
SetNumber uintptr
SetCustomOption uintptr
GetString uintptr
GetNumber uintptr
GetOptionCount uintptr
GetOptionAt uintptr
GetOption uintptr
GetEnabledChannels uintptr
Clone uintptr
SetInterval uintptr
GetInterval uintptr
}
type OperationCallbacks[T any] struct {
CallbackContext *T
PromptUser uintptr
WriteError uintptr
WriteMessage uintptr
WriteProgress uintptr
InstanceResult uintptr
IndicationResult uintptr
ClassResult uintptr
StreamedParameterResult uintptr
}
// Close closes an operation handle.
//
// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_operation_close
func (o *Operation) Close() error {
if o == nil || o.ft == nil {
return ErrNotInitialized
}
r0, _, _ := syscall.SyscallN(o.ft.Close, uintptr(unsafe.Pointer(o)))
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return result
}
return nil
}
func (o *Operation) Cancel() error {
if o == nil || o.ft == nil {
return ErrNotInitialized
}
r0, _, _ := syscall.SyscallN(o.ft.Close, uintptr(unsafe.Pointer(o)), 0)
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return result
}
return nil
}
func (o *Operation) GetInstance() (*Instance, bool, error) {
if o == nil || o.ft == nil {
return nil, false, ErrNotInitialized
}
var (
instance *Instance
errorDetails *Instance
moreResults Boolean
instanceResult ResultError
errorMessageUTF16 *uint16
)
r0, _, _ := syscall.SyscallN(
o.ft.GetInstance,
uintptr(unsafe.Pointer(o)),
uintptr(unsafe.Pointer(&instance)),
uintptr(unsafe.Pointer(&moreResults)),
uintptr(unsafe.Pointer(&instanceResult)),
uintptr(unsafe.Pointer(&errorMessageUTF16)),
uintptr(unsafe.Pointer(&errorDetails)),
)
if !errors.Is(instanceResult, MI_RESULT_OK) {
return nil, false, fmt.Errorf("instance result: %w (%s)", instanceResult, windows.UTF16PtrToString(errorMessageUTF16))
}
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return nil, false, result
}
return instance, moreResults == True, nil
}
func (o *Operation) Unmarshal(dst any) error {
if o == nil || o.ft == nil {
return ErrNotInitialized
}
dv := reflect.ValueOf(dst)
if dv.Kind() != reflect.Ptr || dv.IsNil() {
return ErrInvalidEntityType
}
dv = dv.Elem()
elemType := dv.Type().Elem()
elemValue := reflect.ValueOf(reflect.New(elemType).Interface()).Elem()
if dv.Kind() != reflect.Slice || elemType.Kind() != reflect.Struct {
return ErrInvalidEntityType
}
dv.Set(reflect.MakeSlice(dv.Type(), 0, 0))
for {
instance, moreResults, err := o.GetInstance()
if err != nil {
return fmt.Errorf("failed to get instance: %w", err)
}
// If WMI returns nil, it means there are no more results.
if instance == nil {
break
}
counter, err := instance.GetElementCount()
if err != nil {
return fmt.Errorf("failed to get element count: %w", err)
}
if counter == 0 {
break
}
for i := range elemType.NumField() {
field := elemValue.Field(i)
// Check if the field has an `mi` tag
miTag := elemType.Field(i).Tag.Get("mi")
if miTag == "" {
continue
}
element, err := instance.GetElement(miTag)
if err != nil {
return fmt.Errorf("failed to get element: %w", err)
}
switch element.valueType {
case ValueTypeBOOLEAN:
field.SetBool(element.value == 1)
case ValueTypeUINT8, ValueTypeUINT16, ValueTypeUINT32, ValueTypeUINT64:
field.SetUint(uint64(element.value))
case ValueTypeSINT8, ValueTypeSINT16, ValueTypeSINT32, ValueTypeSINT64:
field.SetInt(int64(element.value))
case ValueTypeSTRING:
if element.value == 0 {
return fmt.Errorf("%s: invalid pointer: value is nil", miTag)
}
// Convert the UTF-16 string to a Go string
stringValue := windows.UTF16PtrToString((*uint16)(unsafe.Pointer(element.value)))
field.SetString(stringValue)
case ValueTypeREAL32, ValueTypeREAL64:
field.SetFloat(float64(element.value))
default:
return fmt.Errorf("unsupported value type: %d", element.valueType)
}
}
dv.Set(reflect.Append(dv, elemValue))
if !moreResults {
break
}
}
return nil
}
func (o *OperationOptions) SetTimeout(timeout time.Duration) error {
if o == nil || o.ft == nil {
return ErrNotInitialized
}
r0, _, _ := syscall.SyscallN(
o.ft.SetInterval,
uintptr(unsafe.Pointer(o)),
uintptr(unsafe.Pointer(OperationOptionsTimeout)),
uintptr(unsafe.Pointer(NewInterval(timeout))),
0,
)
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return result
}
return nil
}
func (o *OperationOptions) Delete() error {
if o == nil || o.ft == nil {
return ErrNotInitialized
}
r0, _, _ := syscall.SyscallN(o.ft.Delete, uintptr(unsafe.Pointer(o)))
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return result
}
return nil
}

102
internal/mi/result.go Normal file
View File

@ -0,0 +1,102 @@
//go:build windows
package mi
import "errors"
type ResultError uint32
const (
MI_RESULT_OK ResultError = iota
MI_RESULT_FAILED
MI_RESULT_ACCESS_DENIED
MI_RESULT_INVALID_NAMESPACE
MI_RESULT_INVALID_PARAMETER
MI_RESULT_INVALID_CLASS
MI_RESULT_NOT_FOUND
MI_RESULT_NOT_SUPPORTED
MI_RESULT_CLASS_HAS_CHILDREN
MI_RESULT_CLASS_HAS_INSTANCES
MI_RESULT_INVALID_SUPERCLASS
MI_RESULT_ALREADY_EXISTS
MI_RESULT_NO_SUCH_PROPERTY
MI_RESULT_TYPE_MISMATCH
MI_RESULT_QUERY_LANGUAGE_NOT_SUPPORTED
MI_RESULT_INVALID_QUERY
MI_RESULT_METHOD_NOT_AVAILABLE
MI_RESULT_METHOD_NOT_FOUND
MI_RESULT_NAMESPACE_NOT_EMPTY
MI_RESULT_INVALID_ENUMERATION_CONTEXT
MI_RESULT_INVALID_OPERATION_TIMEOUT
MI_RESULT_PULL_HAS_BEEN_ABANDONED
MI_RESULT_PULL_CANNOT_BE_ABANDONED
MI_RESULT_FILTERED_ENUMERATION_NOT_SUPPORTED
MI_RESULT_CONTINUATION_ON_ERROR_NOT_SUPPORTED
MI_RESULT_SERVER_LIMITS_EXCEEDED
MI_RESULT_SERVER_IS_SHUTTING_DOWN
)
func (r ResultError) Error() string {
return r.String()
}
func (r ResultError) String() string {
switch {
case errors.Is(r, MI_RESULT_OK):
return "MI_RESULT_OK"
case errors.Is(r, MI_RESULT_FAILED):
return "MI_RESULT_FAILED"
case errors.Is(r, MI_RESULT_ACCESS_DENIED):
return "MI_RESULT_ACCESS_DENIED"
case errors.Is(r, MI_RESULT_INVALID_NAMESPACE):
return "MI_RESULT_INVALID_NAMESPACE"
case errors.Is(r, MI_RESULT_INVALID_PARAMETER):
return "MI_RESULT_INVALID_PARAMETER"
case errors.Is(r, MI_RESULT_INVALID_CLASS):
return "MI_RESULT_INVALID_CLASS"
case errors.Is(r, MI_RESULT_NOT_FOUND):
return "MI_RESULT_NOT_FOUND"
case errors.Is(r, MI_RESULT_NOT_SUPPORTED):
return "MI_RESULT_NOT_SUPPORTED"
case errors.Is(r, MI_RESULT_CLASS_HAS_CHILDREN):
return "MI_RESULT_CLASS_HAS_CHILDREN"
case errors.Is(r, MI_RESULT_CLASS_HAS_INSTANCES):
return "MI_RESULT_CLASS_HAS_INSTANCES"
case errors.Is(r, MI_RESULT_INVALID_SUPERCLASS):
return "MI_RESULT_INVALID_SUPERCLASS"
case errors.Is(r, MI_RESULT_ALREADY_EXISTS):
return "MI_RESULT_ALREADY_EXISTS"
case errors.Is(r, MI_RESULT_NO_SUCH_PROPERTY):
return "MI_RESULT_NO_SUCH_PROPERTY"
case errors.Is(r, MI_RESULT_TYPE_MISMATCH):
return "MI_RESULT_TYPE_MISMATCH"
case errors.Is(r, MI_RESULT_QUERY_LANGUAGE_NOT_SUPPORTED):
return "MI_RESULT_QUERY_LANGUAGE_NOT_SUPPORTED"
case errors.Is(r, MI_RESULT_INVALID_QUERY):
return "MI_RESULT_INVALID_QUERY"
case errors.Is(r, MI_RESULT_METHOD_NOT_AVAILABLE):
return "MI_RESULT_METHOD_NOT_AVAILABLE"
case errors.Is(r, MI_RESULT_METHOD_NOT_FOUND):
return "MI_RESULT_METHOD_NOT_FOUND"
case errors.Is(r, MI_RESULT_NAMESPACE_NOT_EMPTY):
return "MI_RESULT_NAMESPACE_NOT_EMPTY"
case errors.Is(r, MI_RESULT_INVALID_ENUMERATION_CONTEXT):
return "MI_RESULT_INVALID_ENUMERATION_CONTEXT"
case errors.Is(r, MI_RESULT_INVALID_OPERATION_TIMEOUT):
return "MI_RESULT_INVALID_OPERATION_TIMEOUT"
case errors.Is(r, MI_RESULT_PULL_HAS_BEEN_ABANDONED):
return "MI_RESULT_PULL_HAS_BEEN_ABANDONED"
case errors.Is(r, MI_RESULT_PULL_CANNOT_BE_ABANDONED):
return "MI_RESULT_PULL_CANNOT_BE_ABANDONED"
case errors.Is(r, MI_RESULT_FILTERED_ENUMERATION_NOT_SUPPORTED):
return "MI_RESULT_FILTERED_ENUMERATION_NOT_SUPPORTED"
case errors.Is(r, MI_RESULT_CONTINUATION_ON_ERROR_NOT_SUPPORTED):
return "MI_RESULT_CONTINUATION_ON_ERROR_NOT_SUPPORTED"
case errors.Is(r, MI_RESULT_SERVER_LIMITS_EXCEEDED):
return "MI_RESULT_SERVER_LIMITS_EXCEEDED"
case errors.Is(r, MI_RESULT_SERVER_IS_SHUTTING_DOWN):
return "MI_RESULT_SERVER_IS_SHUTTING_DOWN"
default:
return "MI_RESULT_UNKNOWN"
}
}

235
internal/mi/session.go Normal file
View File

@ -0,0 +1,235 @@
//go:build windows
package mi
import (
"errors"
"fmt"
"runtime"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
// Session represents a session.
//
// https://learn.microsoft.com/en-us/windows/win32/api/mi/ns-mi-mi_session
type Session struct {
reserved1 uint64
reserved2 uintptr
ft *SessionFT
defaultOperationOptions *OperationOptions
}
// SessionFT represents the function table for Session.
//
// https://learn.microsoft.com/en-us/windows/win32/api/mi/ns-mi-mi_session
type SessionFT struct {
Close uintptr
GetApplication uintptr
GetInstance uintptr
ModifyInstance uintptr
CreateInstance uintptr
DeleteInstance uintptr
Invoke uintptr
EnumerateInstances uintptr
QueryInstances uintptr
AssociatorInstances uintptr
ReferenceInstances uintptr
Subscribe uintptr
GetClass uintptr
EnumerateClasses uintptr
TestConnection uintptr
}
// Close closes a session and releases all associated memory.
//
// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_session_close
func (s *Session) Close() error {
if s == nil || s.ft == nil {
return ErrNotInitialized
}
if s.defaultOperationOptions != nil {
_ = s.defaultOperationOptions.Delete()
}
r0, _, _ := syscall.SyscallN(s.ft.Close,
uintptr(unsafe.Pointer(s)),
0,
0,
)
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return result
}
return nil
}
// TestConnection queries instances. It is used to test the connection.
// The function returns an operation that can be used to retrieve the result with [Operation.GetInstance]. The operation must be closed with [Operation.Close].
// The instance returned by [Operation.GetInstance] is always nil.
//
// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_session_testconnection
func (s *Session) TestConnection() error {
if s == nil || s.ft == nil {
return ErrNotInitialized
}
operation := &Operation{}
// ref: https://github.com/KurtDeGreeff/omi/blob/9caa55032a1070a665e14fd282a091f6247d13c3/Unix/scriptext/py/PMI_Session.c#L92-L105
r0, _, _ := syscall.SyscallN(
s.ft.TestConnection,
uintptr(unsafe.Pointer(s)),
0,
0,
uintptr(unsafe.Pointer(operation)),
)
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return result
}
var err error
if _, _, err = operation.GetInstance(); err != nil {
return fmt.Errorf("failed to get instance: %w", err)
}
if err = operation.Close(); err != nil {
return fmt.Errorf("failed to close operation: %w", err)
}
return nil
}
// GetApplication gets the Application handle that was used to create the specified session.
//
// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_session_getapplication
func (s *Session) GetApplication() (*Application, error) {
if s == nil || s.ft == nil {
return nil, ErrNotInitialized
}
application := &Application{}
r0, _, _ := syscall.SyscallN(
s.ft.GetApplication,
uintptr(unsafe.Pointer(s)),
uintptr(unsafe.Pointer(application)),
)
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return nil, result
}
return application, nil
}
// QueryInstances queries for a set of instances based on a query expression.
//
// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_session_queryinstances
func (s *Session) QueryInstances(flags OperationFlags, operationOptions *OperationOptions, namespaceName Namespace,
queryDialect QueryDialect, queryExpression string,
) (*Operation, error) {
if s == nil || s.ft == nil {
return nil, ErrNotInitialized
}
queryExpressionUTF16, err := windows.UTF16PtrFromString(queryExpression)
if err != nil {
return nil, err
}
operation := &Operation{}
if operationOptions == nil {
operationOptions = s.defaultOperationOptions
}
r0, _, _ := syscall.SyscallN(
s.ft.QueryInstances,
uintptr(unsafe.Pointer(s)),
uintptr(flags),
uintptr(unsafe.Pointer(operationOptions)),
uintptr(unsafe.Pointer(namespaceName)),
uintptr(unsafe.Pointer(queryDialect)),
uintptr(unsafe.Pointer(queryExpressionUTF16)),
0,
uintptr(unsafe.Pointer(operation)),
)
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return nil, result
}
return operation, nil
}
// QueryUnmarshal queries for a set of instances based on a query expression.
//
// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_session_queryinstances
func (s *Session) QueryUnmarshal(dst any,
flags OperationFlags, operationOptions *OperationOptions,
namespaceName Namespace, queryDialect QueryDialect, queryExpression Query,
) error {
if s == nil || s.ft == nil {
return ErrNotInitialized
}
operation := &Operation{}
if operationOptions == nil {
operationOptions = s.defaultOperationOptions
}
errCh := make(chan error, 1)
operationCallbacks, err := NewUnmarshalOperationsCallbacks(dst, errCh)
if err != nil {
return err
}
r0, _, _ := syscall.SyscallN(
s.ft.QueryInstances,
uintptr(unsafe.Pointer(s)),
uintptr(flags),
uintptr(unsafe.Pointer(operationOptions)),
uintptr(unsafe.Pointer(namespaceName)),
uintptr(unsafe.Pointer(queryDialect)),
uintptr(unsafe.Pointer(queryExpression)),
uintptr(unsafe.Pointer(operationCallbacks)),
uintptr(unsafe.Pointer(operation)),
)
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
return result
}
errs := make([]error, 0)
for err := range errCh {
if err != nil {
errs = append(errs, err)
}
}
// KeepAlive is used to ensure that the callbacks are not garbage collected before the operation is closed.
runtime.KeepAlive(operationCallbacks.CallbackContext)
return errors.Join(errs...)
}
// Query queries for a set of instances based on a query expression.
func (s *Session) Query(dst any, namespaceName Namespace, queryExpression Query) error {
err := s.QueryUnmarshal(dst, OperationFlagsStandardRTTI, nil, namespaceName, QueryDialectWQL, queryExpression)
if err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
return nil
}

137
internal/mi/types.go Normal file
View File

@ -0,0 +1,137 @@
//go:build windows
package mi
import (
"time"
"unsafe"
"github.com/prometheus-community/windows_exporter/internal/utils"
"golang.org/x/sys/windows"
)
type Boolean uint8
const (
False Boolean = 0
True Boolean = 1
)
type QueryDialect *uint16
func NewQueryDialect(queryDialect string) (QueryDialect, error) {
return windows.UTF16PtrFromString(queryDialect)
}
var (
QueryDialectWQL = utils.Must(NewQueryDialect("WQL"))
QueryDialectCQL = utils.Must(NewQueryDialect("CQL"))
)
type Namespace *uint16
func NewNamespace(namespace string) (Namespace, error) {
return windows.UTF16PtrFromString(namespace)
}
var (
NamespaceRootCIMv2 = utils.Must(NewNamespace("root/CIMv2"))
NamespaceRootWindowsFSRM = utils.Must(NewNamespace("root/microsoft/windows/fsrm"))
NamespaceRootWebAdministration = utils.Must(NewNamespace("root/WebAdministration"))
NamespaceRootMSCluster = utils.Must(NewNamespace("root/MSCluster"))
)
type Query *uint16
func NewQuery(query string) (Query, error) {
return windows.UTF16PtrFromString(query)
}
// UTF16PtrFromString converts a string to a UTF-16 pointer at initialization time.
//
//nolint:ireturn
func UTF16PtrFromString[T *uint16](s string) T {
val, err := windows.UTF16PtrFromString(s)
if err != nil {
panic(err)
}
return val
}
type Timestamp struct {
Year uint32
Month uint32
Day uint32
Hour uint32
Minute uint32
Second uint32
Microseconds uint32
UTC int32
}
type Interval struct {
Days uint32
Hours uint32
Minutes uint32
Seconds uint32
Microseconds uint32
Padding1 uint32
Padding2 uint32
Padding3 uint32
}
func NewInterval(interval time.Duration) *Interval {
// Convert the duration to a number of microseconds
microseconds := interval.Microseconds()
// Create a new interval with the microseconds
return &Interval{
Days: uint32(microseconds / (24 * 60 * 60 * 1000000)),
Hours: uint32(microseconds / (60 * 60 * 1000000)),
Minutes: uint32(microseconds / (60 * 1000000)),
Seconds: uint32(microseconds / 1000000),
Microseconds: uint32(microseconds % 1000000),
}
}
type Datetime struct {
IsTimestamp bool
Timestamp *Timestamp // Used when IsTimestamp is true
Interval *Interval // Used when IsTimestamp is false
}
type PropertyDecl struct {
Flags uint32
Code uint32
Name *uint16
Mqualifiers uintptr
NumQualifiers uint32
PropertyType ValueType
ClassName *uint16
Subscript uint32
Offset uint32
Origin *uint16
Propagator *uint16
Value uintptr
}
func (c *ClassDecl) Properties() []*PropertyDecl {
// Create a slice to hold the properties
properties := make([]*PropertyDecl, c.NumProperties)
// Mproperties is a pointer to an array of pointers to PropertyDecl
propertiesArray := (**PropertyDecl)(unsafe.Pointer(c.Mproperties))
// Iterate over the number of properties and fetch each property
for i := range c.NumProperties {
// Get the property pointer at index i
propertyPtr := *(**PropertyDecl)(unsafe.Pointer(uintptr(unsafe.Pointer(propertiesArray)) + uintptr(i)*unsafe.Sizeof(uintptr(0))))
// Append the property to the slice
properties[i] = propertyPtr
}
// Return the slice of properties
return properties
}

112
internal/mi/value.go Normal file
View File

@ -0,0 +1,112 @@
//go:build windows
package mi
import (
"errors"
"fmt"
"unsafe"
"golang.org/x/sys/windows"
)
type ValueType int
const (
ValueTypeBOOLEAN ValueType = iota
ValueTypeUINT8
ValueTypeSINT8
ValueTypeUINT16
ValueTypeSINT16
ValueTypeUINT32
ValueTypeSINT32
ValueTypeUINT64
ValueTypeSINT64
ValueTypeREAL32
ValueTypeREAL64
ValueTypeCHAR16
ValueTypeDATETIME
ValueTypeSTRING
ValueTypeREFERENCE
ValueTypeINSTANCE
ValueTypeBOOLEANA
ValueTypeUINT8A
ValueTypeSINT8A
ValueTypeUINT16A
ValueTypeSINT16A
ValueTypeUINT32A
ValueTypeSINT32A
ValueTypeUINT64A
ValueTypeSINT64A
ValueTypeREAL32A
ValueTypeREAL64A
ValueTypeCHAR16A
ValueTypeDATETIMEA
ValueTypeSTRINGA
ValueTypeREFERENCEA
ValueTypeINSTANCEA
ValueTypeARRAY ValueType = 16
)
type Element struct {
value uintptr
valueType ValueType
}
func (e *Element) GetValue() (any, error) {
switch e.valueType {
case ValueTypeBOOLEAN:
return e.value == 1, nil
case ValueTypeUINT8:
return uint8(e.value), nil
case ValueTypeSINT8:
return int8(e.value), nil
case ValueTypeUINT16:
return uint16(e.value), nil
case ValueTypeSINT16:
return int16(e.value), nil
case ValueTypeUINT32:
return uint32(e.value), nil
case ValueTypeSINT32:
return int32(e.value), nil
case ValueTypeUINT64:
return uint64(e.value), nil
case ValueTypeSINT64:
return int64(e.value), nil
case ValueTypeREAL32:
return float32(e.value), nil
case ValueTypeREAL64:
return float64(e.value), nil
case ValueTypeCHAR16:
return uint16(e.value), nil
case ValueTypeDATETIME:
if e.value == 0 {
return nil, errors.New("invalid pointer: value is nil")
}
return *(*Datetime)(unsafe.Pointer(e.value)), nil
case ValueTypeSTRING:
if e.value == 0 {
return nil, errors.New("invalid pointer: value is nil")
}
// Convert the UTF-16 string to a Go string
return windows.UTF16PtrToString((*uint16)(unsafe.Pointer(e.value))), nil
case ValueTypeSTRINGA:
if e.value == 0 {
return nil, errors.New("invalid pointer: value is nil")
}
// Assuming array of pointers to UTF-16 strings
ptrArray := *(*[]*uint16)(unsafe.Pointer(e.value))
strArray := make([]string, len(ptrArray))
for i, ptr := range ptrArray {
strArray[i] = windows.UTF16PtrToString(ptr)
}
return strArray, nil
default:
return nil, fmt.Errorf("unsupported value type: %d", e.valueType)
}
}

View File

@ -0,0 +1,28 @@
package testutils
import (
"unsafe"
"golang.org/x/sys/windows"
)
var (
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
procGetProcessHandleCount = modkernel32.NewProc("GetProcessHandleCount")
)
func GetProcessHandleCount(handle windows.Handle) (uint32, error) {
var count uint32
r1, _, err := procGetProcessHandleCount.Call(
uintptr(handle),
uintptr(unsafe.Pointer(&count)),
)
if r1 != 1 {
return 0, err
}
return count, nil
}

View File

@ -10,10 +10,10 @@ import (
"time"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/pkg/collector"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require"
"github.com/yusufpapurcu/wmi"
)
func FuncBenchmarkCollector[C collector.Collector](b *testing.B, name string, collectFunc collector.BuilderWithFlags[C]) {
@ -57,14 +57,16 @@ func TestCollector[C collector.Collector, V interface{}](t *testing.T, fn func(*
c := fn(conf)
ch := make(chan prometheus.Metric, 10000)
wmiClient := &wmi.Client{
AllowMissingFields: true,
}
wmiClient.SWbemServicesClient, err = wmi.InitializeSWbemServices(wmiClient)
miApp, err := mi.Application_Initialize()
require.NoError(t, err)
miSession, err := miApp.NewSession(nil)
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, c.Close(logger))
require.NoError(t, miSession.Close())
require.NoError(t, miApp.Close())
})
wg := sync.WaitGroup{}
@ -78,7 +80,7 @@ func TestCollector[C collector.Collector, V interface{}](t *testing.T, fn func(*
}
}()
require.NoError(t, c.Build(logger, wmiClient))
require.NoError(t, c.Build(logger, miSession))
time.Sleep(1 * time.Second)

View File

@ -35,3 +35,11 @@ func PDHEnabled() bool {
return false
}
func MIEnabled() bool {
if v, ok := os.LookupEnv("WINDOWS_EXPORTER_WMI_ENGINE"); ok && v == "mi" {
return true
}
return false
}

View File

@ -17,3 +17,14 @@ func BoolToFloat(b bool) float64 {
func ToPTR[t any](v t) *t {
return &v
}
// Must panics if the error is not nil.
//
//nolint:ireturn
func Must[T any](v T, err error) T {
if err != nil {
panic(err)
}
return v
}

1
main.go Normal file
View File

@ -0,0 +1 @@
package main

View File

@ -57,9 +57,9 @@ import (
"github.com/prometheus-community/windows_exporter/internal/collector/time"
"github.com/prometheus-community/windows_exporter/internal/collector/update"
"github.com/prometheus-community/windows_exporter/internal/collector/vmware"
"github.com/prometheus-community/windows_exporter/internal/mi"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/yusufpapurcu/wmi"
)
// NewWithFlags To be called by the exporter for collector initialization before running kingpin.Parse.
@ -132,9 +132,6 @@ func NewWithConfig(config Config) *MetricCollectors {
func New(collectors Map) *MetricCollectors {
return &MetricCollectors{
Collectors: collectors,
WMIClient: &wmi.Client{
AllowMissingFields: true,
},
}
}
@ -186,11 +183,9 @@ func (c *MetricCollectors) Enable(enabledCollectors []string) error {
// Build To be called by the exporter for collector initialization.
func (c *MetricCollectors) Build(logger *slog.Logger) error {
var err error
c.WMIClient.SWbemServicesClient, err = wmi.InitializeSWbemServices(c.WMIClient)
err := c.initMI()
if err != nil {
return fmt.Errorf("initialize SWbemServices: %w", err)
return fmt.Errorf("error from initialize MI: %w", err)
}
wg := sync.WaitGroup{}
@ -203,7 +198,7 @@ func (c *MetricCollectors) Build(logger *slog.Logger) error {
go func() {
defer wg.Done()
if err = collector.Build(logger, c.WMIClient); err != nil {
if err = collector.Build(logger, c.MISession); err != nil {
errCh <- fmt.Errorf("error build collector %s: %w", collector.GetName(), err)
}
}()
@ -245,11 +240,42 @@ func (c *MetricCollectors) Close(logger *slog.Logger) error {
}
}
if c.WMIClient != nil && c.WMIClient.SWbemServicesClient != nil {
if err := c.WMIClient.SWbemServicesClient.Close(); err != nil {
app, err := c.MISession.GetApplication()
if err != nil && !errors.Is(err, mi.ErrNotInitialized) {
errs = append(errs, err)
}
if err := c.MISession.Close(); err != nil && !errors.Is(err, mi.ErrNotInitialized) {
errs = append(errs, err)
}
if err := app.Close(); err != nil && !errors.Is(err, mi.ErrNotInitialized) {
errs = append(errs, err)
}
return errors.Join(errs...)
}
// Close To be called by the exporter for collector cleanup.
func (c *MetricCollectors) initMI() error {
app, err := mi.Application_Initialize()
if err != nil {
return fmt.Errorf("error from initialize MI application: %w", err)
}
destinationOptions, err := app.NewDestinationOptions()
if err != nil {
return fmt.Errorf("error from create NewDestinationOptions: %w", err)
}
if err = destinationOptions.SetLocale(mi.LocaleEnglish); err != nil {
return fmt.Errorf("error from set locale: %w", err)
}
c.MISession, err = app.NewSession(destinationOptions)
if err != nil {
return fmt.Errorf("error from create NewSession: %w", err)
}
return nil
}

View File

@ -4,14 +4,14 @@ import (
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
type MetricCollectors struct {
Collectors Map
WMIClient *wmi.Client
MISession *mi.Session
PerfCounterQuery string
}
@ -22,7 +22,7 @@ type (
// Collector interface that a collector has to implement.
type Collector interface {
Build(logger *slog.Logger, wmiClient *wmi.Client) error
Build(logger *slog.Logger, miSession *mi.Session) error
// Close closes the collector
Close(logger *slog.Logger) error
// GetName get the name of the collector