2023-11-04 19:51:35 +00:00
//go:build windows
package time
import (
"errors"
2024-09-10 22:34:10 +00:00
"log/slog"
"time"
2023-11-04 19:51:35 +00:00
"github.com/alecthomas/kingpin/v2"
2024-09-10 22:34:10 +00:00
"github.com/prometheus-community/windows_exporter/pkg/headers/kernel32"
2023-11-04 19:51:35 +00:00
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/winversion"
"github.com/prometheus/client_golang/prometheus"
2024-08-29 22:26:15 +00:00
"github.com/yusufpapurcu/wmi"
2024-09-10 22:34:10 +00:00
"golang.org/x/sys/windows"
2023-11-04 19:51:35 +00:00
)
const Name = "time"
type Config struct { }
var ConfigDefaults = Config { }
2024-08-10 20:05:33 +00:00
// Collector is a Prometheus Collector for Perflib counter metrics.
2024-08-05 13:50:41 +00:00
type Collector struct {
2024-08-11 11:28:39 +00:00
config Config
2023-11-04 19:51:35 +00:00
2024-09-10 22:34:10 +00:00
currentTime * prometheus . Desc
timezone * prometheus . Desc
2024-08-10 18:02:07 +00:00
clockFrequencyAdjustmentPPBTotal * prometheus . Desc
computedTimeOffset * prometheus . Desc
ntpClientTimeSourceCount * prometheus . Desc
ntpRoundTripDelay * prometheus . Desc
ntpServerIncomingRequestsTotal * prometheus . Desc
ntpServerOutgoingResponsesTotal * prometheus . Desc
2023-11-04 19:51:35 +00:00
}
2024-08-24 17:14:38 +00:00
func New ( config * Config ) * Collector {
2024-08-11 11:28:39 +00:00
if config == nil {
config = & ConfigDefaults
}
c := & Collector {
config : * config ,
}
2023-11-04 19:51:35 +00:00
return c
}
2024-08-05 13:50:41 +00:00
func NewWithFlags ( _ * kingpin . Application ) * Collector {
return & Collector { }
2023-11-04 19:51:35 +00:00
}
2024-08-05 13:50:41 +00:00
func ( c * Collector ) GetName ( ) string {
2023-11-04 19:51:35 +00:00
return Name
}
2024-09-10 22:34:10 +00:00
func ( c * Collector ) GetPerfCounter ( _ * slog . Logger ) ( [ ] string , error ) {
2023-11-04 19:51:35 +00:00
return [ ] string { "Windows Time Service" } , nil
}
2024-09-10 22:34:10 +00:00
func ( c * Collector ) Close ( _ * slog . Logger ) error {
2024-08-05 13:50:41 +00:00
return nil
}
2024-09-10 22:34:10 +00:00
func ( c * Collector ) Build ( _ * slog . Logger , _ * wmi . Client ) error {
2024-08-29 22:26:15 +00:00
if winversion . WindowsVersionFloat ( ) <= 6.1 {
return errors . New ( "windows version older than Server 2016 detected. The time collector will not run and should be disabled via CLI flags or configuration file" )
2023-11-04 19:51:35 +00:00
}
2024-09-10 22:34:10 +00:00
c . currentTime = prometheus . NewDesc (
prometheus . BuildFQName ( types . Namespace , Name , "current_timestamp_seconds" ) ,
"OperatingSystem.LocalDateTime" ,
nil ,
nil ,
)
c . timezone = prometheus . NewDesc (
prometheus . BuildFQName ( types . Namespace , Name , "timezone" ) ,
"OperatingSystem.LocalDateTime" ,
[ ] string { "timezone" } ,
nil ,
)
2024-08-10 18:02:07 +00:00
c . clockFrequencyAdjustmentPPBTotal = prometheus . NewDesc (
2023-11-04 19:51:35 +00:00
prometheus . BuildFQName ( types . Namespace , Name , "clock_frequency_adjustment_ppb_total" ) ,
"Total adjustment made to the local system clock frequency by W32Time in Parts Per Billion (PPB) units." ,
nil ,
nil ,
)
2024-08-10 18:02:07 +00:00
c . computedTimeOffset = prometheus . NewDesc (
2023-11-04 19:51:35 +00:00
prometheus . BuildFQName ( types . Namespace , Name , "computed_time_offset_seconds" ) ,
"Absolute time offset between the system clock and the chosen time source, in seconds" ,
nil ,
nil ,
)
2024-08-10 18:02:07 +00:00
c . ntpClientTimeSourceCount = prometheus . NewDesc (
2023-11-04 19:51:35 +00:00
prometheus . BuildFQName ( types . Namespace , Name , "ntp_client_time_sources" ) ,
"Active number of NTP Time sources being used by the client" ,
nil ,
nil ,
)
2024-08-10 18:02:07 +00:00
c . ntpRoundTripDelay = prometheus . NewDesc (
2023-11-04 19:51:35 +00:00
prometheus . BuildFQName ( types . Namespace , Name , "ntp_round_trip_delay_seconds" ) ,
"Roundtrip delay experienced by the NTP client in receiving a response from the server for the most recent request, in seconds" ,
nil ,
nil ,
)
2024-08-10 18:02:07 +00:00
c . ntpServerOutgoingResponsesTotal = prometheus . NewDesc (
2023-11-04 19:51:35 +00:00
prometheus . BuildFQName ( types . Namespace , Name , "ntp_server_outgoing_responses_total" ) ,
"Total number of requests responded to by NTP server" ,
nil ,
nil ,
)
2024-08-10 18:02:07 +00:00
c . ntpServerIncomingRequestsTotal = prometheus . NewDesc (
2023-11-04 19:51:35 +00:00
prometheus . BuildFQName ( types . Namespace , Name , "ntp_server_incoming_requests_total" ) ,
"Total number of requests received by NTP server" ,
nil ,
nil ,
)
2024-09-10 22:34:10 +00:00
2023-11-04 19:51:35 +00:00
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
2024-09-10 22:34:10 +00:00
func ( c * Collector ) Collect ( ctx * types . ScrapeContext , logger * slog . Logger , ch chan <- prometheus . Metric ) error {
logger = logger . With ( slog . String ( "collector" , Name ) )
errs := make ( [ ] error , 0 , 2 )
if err := c . collectTime ( ch ) ; err != nil {
logger . Error ( "failed collecting time metrics" ,
slog . Any ( "err" , err ) ,
)
errs = append ( errs , err )
2023-11-04 19:51:35 +00:00
}
2024-09-10 22:34:10 +00:00
if err := c . collectNTP ( ctx , logger , ch ) ; err != nil {
logger . Error ( "failed collecting time ntp metrics" ,
slog . Any ( "err" , err ) ,
)
errs = append ( errs , err )
}
return errors . Join ( errs ... )
2023-11-04 19:51:35 +00:00
}
2024-08-10 20:05:33 +00:00
// Perflib "Windows Time Service".
2023-11-04 19:51:35 +00:00
type windowsTime struct {
ClockFrequencyAdjustmentPPBTotal float64 ` perflib:"Clock Frequency Adjustment (ppb)" `
ComputedTimeOffset float64 ` perflib:"Computed Time Offset" `
NTPClientTimeSourceCount float64 ` perflib:"NTP Client Time Source Count" `
2024-09-10 22:34:10 +00:00
NTPRoundTripDelay float64 ` perflib:"NTP Roundtrip Delay" `
2023-11-04 19:51:35 +00:00
NTPServerIncomingRequestsTotal float64 ` perflib:"NTP Server Incoming Requests" `
NTPServerOutgoingResponsesTotal float64 ` perflib:"NTP Server Outgoing Responses" `
}
2024-09-10 22:34:10 +00:00
func ( c * Collector ) collectTime ( ch chan <- prometheus . Metric ) error {
ch <- prometheus . MustNewConstMetric (
c . currentTime ,
prometheus . GaugeValue ,
float64 ( time . Now ( ) . Unix ( ) ) ,
)
timeZoneInfo , err := kernel32 . GetDynamicTimeZoneInformation ( )
if err != nil {
return err
}
// timeZoneKeyName contains the english name of the timezone.
timezoneName := windows . UTF16ToString ( timeZoneInfo . TimeZoneKeyName [ : ] )
ch <- prometheus . MustNewConstMetric (
c . timezone ,
prometheus . GaugeValue ,
1.0 ,
timezoneName ,
)
return nil
}
func ( c * Collector ) collectNTP ( ctx * types . ScrapeContext , logger * slog . Logger , ch chan <- prometheus . Metric ) error {
logger = logger . With ( slog . String ( "collector" , Name ) )
2023-11-04 19:51:35 +00:00
var dst [ ] windowsTime // Single-instance class, array is required but will have single entry.
2024-09-10 22:34:10 +00:00
2024-08-24 17:14:38 +00:00
if err := perflib . UnmarshalObject ( ctx . PerfObjects [ "Windows Time Service" ] , & dst , logger ) ; err != nil {
2024-05-11 10:05:45 +00:00
return err
2023-11-04 19:51:35 +00:00
}
2024-09-10 22:34:10 +00:00
if len ( dst ) == 0 {
return errors . New ( "no data returned for Windows Time Service" )
}
2023-11-04 19:51:35 +00:00
ch <- prometheus . MustNewConstMetric (
2024-08-10 18:02:07 +00:00
c . clockFrequencyAdjustmentPPBTotal ,
2023-11-04 19:51:35 +00:00
prometheus . CounterValue ,
dst [ 0 ] . ClockFrequencyAdjustmentPPBTotal ,
)
ch <- prometheus . MustNewConstMetric (
2024-08-10 18:02:07 +00:00
c . computedTimeOffset ,
2023-11-04 19:51:35 +00:00
prometheus . GaugeValue ,
dst [ 0 ] . ComputedTimeOffset / 1000000 , // microseconds -> seconds
)
ch <- prometheus . MustNewConstMetric (
2024-08-10 18:02:07 +00:00
c . ntpClientTimeSourceCount ,
2023-11-04 19:51:35 +00:00
prometheus . GaugeValue ,
dst [ 0 ] . NTPClientTimeSourceCount ,
)
ch <- prometheus . MustNewConstMetric (
2024-08-10 18:02:07 +00:00
c . ntpRoundTripDelay ,
2023-11-04 19:51:35 +00:00
prometheus . GaugeValue ,
2024-09-10 22:34:10 +00:00
dst [ 0 ] . NTPRoundTripDelay / 1000000 , // microseconds -> seconds
2023-11-04 19:51:35 +00:00
)
ch <- prometheus . MustNewConstMetric (
2024-08-10 18:02:07 +00:00
c . ntpServerIncomingRequestsTotal ,
2023-11-04 19:51:35 +00:00
prometheus . CounterValue ,
dst [ 0 ] . NTPServerIncomingRequestsTotal ,
)
ch <- prometheus . MustNewConstMetric (
2024-08-10 18:02:07 +00:00
c . ntpServerOutgoingResponsesTotal ,
2023-11-04 19:51:35 +00:00
prometheus . CounterValue ,
dst [ 0 ] . NTPServerOutgoingResponsesTotal ,
)
2024-09-10 22:34:10 +00:00
2024-05-11 10:05:45 +00:00
return nil
2023-11-04 19:51:35 +00:00
}