@@ -60,11 +60,14 @@ type Collector struct {
60
60
mu sync.RWMutex
61
61
ctxCancelFn context.CancelFunc
62
62
63
+ logger * slog.Logger
64
+
63
65
metricsBuf []prometheus.Metric
64
66
65
- pendingUpdate * prometheus.Desc
66
- queryDurationSeconds * prometheus.Desc
67
- lastScrapeMetric * prometheus.Desc
67
+ pendingUpdate * prometheus.Desc
68
+ pendingUpdateLastPublished * prometheus.Desc
69
+ queryDurationSeconds * prometheus.Desc
70
+ lastScrapeMetric * prometheus.Desc
68
71
}
69
72
70
73
func New (config * Config ) * Collector {
@@ -146,9 +149,9 @@ func (c *Collector) Close() error {
146
149
}
147
150
148
151
func (c * Collector ) Build (logger * slog.Logger , _ * mi.Session ) error {
149
- logger = logger .With (slog .String ("collector" , Name ))
152
+ c . logger = logger .With (slog .String ("collector" , Name ))
150
153
151
- logger .Info ("update collector is in an experimental state! The configuration and metrics may change in future. Please report any issues." )
154
+ c . logger .Info ("update collector is in an experimental state! The configuration and metrics may change in future. Please report any issues." )
152
155
153
156
ctx , cancel := context .WithCancel (context .Background ())
154
157
@@ -164,7 +167,14 @@ func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
164
167
c .pendingUpdate = prometheus .NewDesc (
165
168
prometheus .BuildFQName (types .Namespace , Name , "pending_info" ),
166
169
"Expose information for a single pending update item" ,
167
- []string {"category" , "severity" , "title" },
170
+ []string {"id" , "revision" , "category" , "severity" , "title" },
171
+ nil ,
172
+ )
173
+
174
+ c .pendingUpdateLastPublished = prometheus .NewDesc (
175
+ prometheus .BuildFQName (types .Namespace , Name , "pending_published_timestamp" ),
176
+ "Expose last published timestamp for a single pending update item" ,
177
+ []string {"id" , "revision" },
168
178
nil ,
169
179
)
170
180
@@ -241,9 +251,16 @@ func (c *Collector) scheduleUpdateStatus(ctx context.Context, logger *slog.Logge
241
251
242
252
defer musQueryInterface .Release ()
243
253
254
+ _ , err = oleutil .PutProperty (musQueryInterface , "UserLocale" , 1033 )
255
+ if err != nil {
256
+ initErrCh <- fmt .Errorf ("failed to set ClientApplicationID: %w" , err )
257
+
258
+ return
259
+ }
260
+
244
261
_ , err = oleutil .PutProperty (musQueryInterface , "ClientApplicationID" , "windows_exporter" )
245
262
if err != nil {
246
- initErrCh <- fmt .Errorf ("put ClientApplicationID: %w" , err )
263
+ initErrCh <- fmt .Errorf ("failed to set ClientApplicationID: %w" , err )
247
264
248
265
return
249
266
}
@@ -320,7 +337,7 @@ func (c *Collector) scheduleUpdateStatus(ctx context.Context, logger *slog.Logge
320
337
}
321
338
322
339
func (c * Collector ) fetchUpdates (logger * slog.Logger , usd * ole.IDispatch ) ([]prometheus.Metric , error ) {
323
- metricsBuf := make ([]prometheus.Metric , 0 , len (c .metricsBuf ))
340
+ metricsBuf := make ([]prometheus.Metric , 0 , len (c .metricsBuf )* 2 + 1 )
324
341
325
342
timeStart := time .Now ()
326
343
@@ -367,10 +384,22 @@ func (c *Collector) fetchUpdates(logger *slog.Logger, usd *ole.IDispatch) ([]pro
367
384
c .pendingUpdate ,
368
385
prometheus .GaugeValue ,
369
386
1 ,
387
+ update .identity ,
388
+ update .revision ,
370
389
update .category ,
371
390
update .severity ,
372
391
update .title ,
373
392
))
393
+
394
+ if update .lastPublished != (time.Time {}) {
395
+ metricsBuf = append (metricsBuf , prometheus .MustNewConstMetric (
396
+ c .pendingUpdateLastPublished ,
397
+ prometheus .GaugeValue ,
398
+ float64 (update .lastPublished .Unix ()),
399
+ update .identity ,
400
+ update .revision ,
401
+ ))
402
+ }
374
403
}
375
404
376
405
metricsBuf = append (metricsBuf , prometheus .MustNewConstMetric (
@@ -383,9 +412,12 @@ func (c *Collector) fetchUpdates(logger *slog.Logger, usd *ole.IDispatch) ([]pro
383
412
}
384
413
385
414
type windowsUpdate struct {
386
- category string
387
- severity string
388
- title string
415
+ identity string
416
+ revision string
417
+ category string
418
+ severity string
419
+ title string
420
+ lastPublished time.Time
389
421
}
390
422
391
423
// getUpdateStatus retrieves the update status of the given item.
@@ -423,10 +455,48 @@ func (c *Collector) getUpdateStatus(updd *ole.IDispatch, item int) (windowsUpdat
423
455
return windowsUpdate {}, fmt .Errorf ("get Title: %w" , err )
424
456
}
425
457
458
+ // Get the Identity object
459
+ identityVariant , err := oleutil .GetProperty (updateItem , "Identity" )
460
+ if err != nil {
461
+ return windowsUpdate {}, fmt .Errorf ("get Identity: %w" , err )
462
+ }
463
+
464
+ identity := identityVariant .ToIDispatch ()
465
+ defer identity .Release ()
466
+
467
+ // Read the UpdateID
468
+ updateIDVariant , err := oleutil .GetProperty (identity , "UpdateID" )
469
+ if err != nil {
470
+ return windowsUpdate {}, fmt .Errorf ("get UpdateID: %w" , err )
471
+ }
472
+
473
+ revisionVariant , err := oleutil .GetProperty (identity , "RevisionNumber" )
474
+ if err != nil {
475
+ return windowsUpdate {}, fmt .Errorf ("get RevisionNumber: %w" , err )
476
+ }
477
+
478
+ lastPublished , err := oleutil .GetProperty (updateItem , "LastDeploymentChangeTime" )
479
+ if err != nil {
480
+ return windowsUpdate {}, fmt .Errorf ("get LastDeploymentChangeTime: %w" , err )
481
+ }
482
+
483
+ lastPublishedDate , err := ole .GetVariantDate (uint64 (lastPublished .Val ))
484
+ if err != nil {
485
+ c .logger .Debug ("failed to convert LastDeploymentChangeTime" ,
486
+ slog .String ("title" , title .ToString ()),
487
+ slog .Any ("err" , err ),
488
+ )
489
+
490
+ lastPublishedDate = time.Time {}
491
+ }
492
+
426
493
return windowsUpdate {
427
- category : categoryName ,
428
- severity : severity .ToString (),
429
- title : title .ToString (),
494
+ identity : updateIDVariant .ToString (),
495
+ revision : strconv .FormatInt (revisionVariant .Val , 10 ),
496
+ category : categoryName ,
497
+ severity : severity .ToString (),
498
+ title : title .ToString (),
499
+ lastPublished : lastPublishedDate ,
430
500
}, nil
431
501
}
432
502
0 commit comments