scheduled_task: expose result statuses and deprecate last_result (#2532)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
This commit is contained in:
Jan-Otto Kröpke
2026-10-07 23:26:47 +02:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 88034b818e
commit 92483242ba
4 changed files with 492 additions and 19 deletions
+85 -6
View File
@@ -12,13 +12,13 @@ Enabled by default? | No
### `--collector.scheduled_task.include`
If given, the path of the task needs to match the include regexp in order for the corresponding metrics to be reported.
If given, the path of the task needs to match the include regular expression in order for the corresponding metrics to be reported.
E.G. `--collector.scheduled_task.include="Firefox.*"`
### `--collector.scheduled_task.exclude`
If given, the path of the task needs to *not* match the exclude regexp in order for the corresponding metrics to be reported.
If given, the path of the task needs to *not* match the exclude regular expression in order for the corresponding metrics to be reported.
E.G. `--collector.scheduled_task.exclude="/Microsoft/.+"`
@@ -26,11 +26,53 @@ E.G. `--collector.scheduled_task.exclude="/Microsoft/.+"`
Name | Description | Type | Labels
-----|-------------|------|-------
`windows_scheduled_task_last_result` | The result that was returned the last time the registered task was run | gauge | task
`windows_scheduled_task_last_result` | **Deprecated:** use `windows_scheduled_task_last_result_status`. 1 if the last result code is zero, 0 otherwise; omitted for tasks that have never run | gauge | task
`windows_scheduled_task_last_result_status` | The last result status of a scheduled task, 1 if the current status, 0 otherwise | gauge | task, status
`windows_scheduled_task_missed_runs` | The number of times the registered task missed a scheduled run | gauge | task
`windows_scheduled_task_state` | The current state of a scheduled task | gauge | task, state
For the values of the `state` label, see below.
For the values of the `state` and `status` labels, see below.
### Last result
`windows_scheduled_task_last_result_status` uses the same enum convention as
`windows_service_state`: every status is exported for each task, with exactly one
status set to 1 and all others set to 0. The status metric is exported even when a
task has never run.
Status | Result code (decimal) | Result code (hexadecimal) | Meaning
-------|-----------------------|---------------------------|--------
`success` | 0 | `0x00000000` | The task completed successfully
`ready` | 267008 | `0x00041300` | The task is ready to run
`running` | 267009 | `0x00041301` | The task is currently running
`disabled` | 267010 | `0x00041302` | The task is disabled
`has_not_run` | 267011 | `0x00041303` | The task has never run
`no_more_runs` | 267012 | `0x00041304` | No more runs are scheduled
`not_scheduled` | 267013 | `0x00041305` | Required scheduling properties are missing
`terminated` | 267014 | `0x00041306` | The last run was terminated by the user
`no_valid_triggers` | 267015 | `0x00041307` | No triggers exist or all triggers are disabled
`event_trigger` | 267016 | `0x00041308` | Event triggers do not have set run times
`queued` | 267045 | `0x00041325` | The task has been queued to run
`unknown` | Any other code | Any other code | The result code has no named status mapping
Application exit codes, scheduler errors, and unrecognized results are grouped
under `unknown`. This status means the result code has no named mapping; it does
not establish whether the task succeeded or failed.
See [Microsoft's Task Scheduler result codes](https://learn.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-error-and-success-constants)
for details.
### Deprecated metric
`windows_scheduled_task_last_result` is deprecated. Use
`windows_scheduled_task_last_result_status` to distinguish success, running,
tasks that have never run, and other scheduler statuses. For example, use
`windows_scheduled_task_last_result_status{status="success"} == 1` to select
successful results.
The deprecated metric remains available with its existing labels and values for
compatibility. A running result code still produces 0, which does not necessarily
mean the task failed. `windows_scheduled_task_last_result` and
`windows_scheduled_task_missed_runs` are omitted when the result is `has_not_run`.
### State
@@ -46,6 +88,18 @@ A task can be in the following states:
```
windows_scheduled_task_last_result{task="/Microsoft/Windows/Chkdsk/SyspartRepair"} 1
windows_scheduled_task_last_result_status{status="success",task="/Microsoft/Windows/Chkdsk/SyspartRepair"} 1
windows_scheduled_task_last_result_status{status="ready",task="/Microsoft/Windows/Chkdsk/SyspartRepair"} 0
windows_scheduled_task_last_result_status{status="running",task="/Microsoft/Windows/Chkdsk/SyspartRepair"} 0
windows_scheduled_task_last_result_status{status="disabled",task="/Microsoft/Windows/Chkdsk/SyspartRepair"} 0
windows_scheduled_task_last_result_status{status="has_not_run",task="/Microsoft/Windows/Chkdsk/SyspartRepair"} 0
windows_scheduled_task_last_result_status{status="no_more_runs",task="/Microsoft/Windows/Chkdsk/SyspartRepair"} 0
windows_scheduled_task_last_result_status{status="not_scheduled",task="/Microsoft/Windows/Chkdsk/SyspartRepair"} 0
windows_scheduled_task_last_result_status{status="terminated",task="/Microsoft/Windows/Chkdsk/SyspartRepair"} 0
windows_scheduled_task_last_result_status{status="no_valid_triggers",task="/Microsoft/Windows/Chkdsk/SyspartRepair"} 0
windows_scheduled_task_last_result_status{status="event_trigger",task="/Microsoft/Windows/Chkdsk/SyspartRepair"} 0
windows_scheduled_task_last_result_status{status="queued",task="/Microsoft/Windows/Chkdsk/SyspartRepair"} 0
windows_scheduled_task_last_result_status{status="unknown",task="/Microsoft/Windows/Chkdsk/SyspartRepair"} 0
windows_scheduled_task_missed_runs{task="/Microsoft/Windows/Chkdsk/SyspartRepair"} 0
windows_scheduled_task_state{state="disabled",task="/Microsoft/Windows/Chkdsk/SyspartRepair"} 1
windows_scheduled_task_state{state="queued",task="/Microsoft/Windows/Chkdsk/SyspartRepair"} 0
@@ -55,13 +109,38 @@ windows_scheduled_task_state{state="unknown",task="/Microsoft/Windows/Chkdsk/Sys
```
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
Current result status for each task:
```promql
windows_scheduled_task_last_result_status == 1
```
Tasks that have never run:
```promql
windows_scheduled_task_last_result_status{status="has_not_run"} == 1
```
For dashboards still using the deprecated success/failure metric, exclude running tasks:
```promql
(windows_scheduled_task_last_result == 0)
unless on (job, instance, task)
(windows_scheduled_task_state{state="running"} == 1)
```
## Alerting examples
This example treats unknown or terminated results as failures. An `unknown`
status only means that the result code has no named mapping; adjust the alert
selection for your task's exit-code conventions. Running tasks report the
`running` status, so they do not match this alert.
**prometheus.rules**
```yaml
- alert: "WindowsScheduledTaskFailure"
expr: "windows_scheduled_task_last_result == 0"
expr: 'windows_scheduled_task_last_result_status{status=~"unknown|terminated"} == 1'
for: "1d"
labels:
severity: "high"
@@ -50,16 +50,19 @@ var ConfigDefaults = Config{
type Collector struct {
config Config
lastResult *prometheus.Desc
missedRuns *prometheus.Desc
state *prometheus.Desc
lastResult *prometheus.Desc
lastResultStatus *prometheus.Desc
missedRuns *prometheus.Desc
state *prometheus.Desc
}
// TaskState ...
// https://docs.microsoft.com/en-us/windows/desktop/api/taskschd/ne-taskschd-task_state
type TaskState uint
type TaskResult uint
// TaskResult preserves the unsigned 32-bit representation of a result code,
// including HRESULTs returned as signed LONG values by Task Scheduler.
type TaskResult uint32
const (
TASK_STATE_UNKNOWN TaskState = iota
@@ -70,8 +73,17 @@ const (
)
const (
SCHED_S_SUCCESS TaskResult = 0x0
SCHED_S_TASK_HAS_NOT_RUN TaskResult = 0x00041303
SCHED_S_SUCCESS TaskResult = 0x0
SCHED_S_TASK_READY TaskResult = 0x00041300
SCHED_S_TASK_RUNNING TaskResult = 0x00041301
SCHED_S_TASK_DISABLED TaskResult = 0x00041302
SCHED_S_TASK_HAS_NOT_RUN TaskResult = 0x00041303
SCHED_S_TASK_NO_MORE_RUNS TaskResult = 0x00041304
SCHED_S_TASK_NOT_SCHEDULED TaskResult = 0x00041305
SCHED_S_TASK_TERMINATED TaskResult = 0x00041306
SCHED_S_TASK_NO_VALID_TRIGGERS TaskResult = 0x00041307
SCHED_S_EVENT_TRIGGER TaskResult = 0x00041308
SCHED_S_TASK_QUEUED TaskResult = 0x00041325
)
type ScheduledTask struct {
@@ -152,11 +164,19 @@ func (c *Collector) Close() error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
c.lastResult = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "last_result"),
"The result that was returned the last time the registered task was run",
"DEPRECATED: use windows_scheduled_task_last_result_status. "+
"1 if the last result code of the registered task is zero, 0 otherwise",
[]string{"task"},
nil,
)
c.lastResultStatus = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "last_result_status"),
"The last result status of a scheduled task, 1 if the current status, 0 otherwise",
[]string{"task", "status"},
nil,
)
c.missedRuns = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "missed_runs"),
"The number of times the registered task missed a scheduled run",
@@ -181,12 +201,24 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric, _ time.Duration) error
//nolint:gochecknoglobals
var TASK_STATES = []string{"disabled", "queued", "ready", "running", "unknown"}
//nolint:gochecknoglobals
var TASK_RESULT_STATUSES = []string{
"success", "ready", "running", "disabled", "has_not_run", "no_more_runs",
"not_scheduled", "terminated", "no_valid_triggers", "event_trigger", "queued", "unknown",
}
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
scheduledTasks, err := getScheduledTasks()
if err != nil {
return fmt.Errorf("get scheduled tasks: %w", err)
}
c.collectMetrics(ch, scheduledTasks)
return nil
}
func (c *Collector) collectMetrics(ch chan<- prometheus.Metric, scheduledTasks ScheduledTasks) {
for _, task := range scheduledTasks {
if c.config.TaskExclude.MatchString(task.Path) ||
!c.config.TaskInclude.MatchString(task.Path) {
@@ -209,6 +241,24 @@ func (c *Collector) collect(ch chan<- prometheus.Metric) error {
)
}
resultStatus := task.LastTaskResult.String()
for _, status := range TASK_RESULT_STATUSES {
var statusValue float64
if resultStatus == status {
statusValue = 1
}
ch <- prometheus.MustNewConstMetric(
c.lastResultStatus,
prometheus.GaugeValue,
statusValue,
task.Path,
status,
)
}
if task.LastTaskResult == SCHED_S_TASK_HAS_NOT_RUN {
continue
}
@@ -232,8 +282,6 @@ func (c *Collector) collect(ch chan<- prometheus.Metric) error {
task.Path,
)
}
return nil
}
const SCHEDULED_TASK_PROGRAM_ID = "Schedule.Service.1"
@@ -253,8 +301,7 @@ func getScheduledTasks() (ScheduledTasks, error) {
defer runtime.UnlockOSThread()
if err := ole.CoInitializeEx(0, ole.COINIT_APARTMENTTHREADED|ole.COINIT_DISABLE_OLE1DDE); err != nil {
var oleCode *ole.OleError
if errors.As(err, &oleCode) && oleCode.Code() != ole.S_OK && oleCode.Code() != S_FALSE {
if oleCode, ok := errors.AsType[*ole.OleError](err); ok && oleCode.Code() != ole.S_OK && oleCode.Code() != S_FALSE {
return nil, err
}
}
@@ -442,3 +489,32 @@ func (t TaskState) String() string {
return ""
}
}
func (t TaskResult) String() string {
switch t {
case SCHED_S_SUCCESS:
return "success"
case SCHED_S_TASK_READY:
return "ready"
case SCHED_S_TASK_RUNNING:
return "running"
case SCHED_S_TASK_DISABLED:
return "disabled"
case SCHED_S_TASK_HAS_NOT_RUN:
return "has_not_run"
case SCHED_S_TASK_NO_MORE_RUNS:
return "no_more_runs"
case SCHED_S_TASK_NOT_SCHEDULED:
return "not_scheduled"
case SCHED_S_TASK_TERMINATED:
return "terminated"
case SCHED_S_TASK_NO_VALID_TRIGGERS:
return "no_valid_triggers"
case SCHED_S_EVENT_TRIGGER:
return "event_trigger"
case SCHED_S_TASK_QUEUED:
return "queued"
default:
return "unknown"
}
}
@@ -0,0 +1,304 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build windows
package scheduled_task
import (
"regexp"
"testing"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
"github.com/stretchr/testify/require"
)
func TestCollectMetrics(t *testing.T) {
t.Parallel()
for _, tc := range []struct {
name string
result int64
state TaskState
stateLabel string
status string
}{
{
name: "success", result: 0,
state: TASK_STATE_READY, stateLabel: "ready", status: "success",
},
{
name: "application error", result: 1,
state: TASK_STATE_READY, stateLabel: "ready", status: "unknown",
},
{
name: "ready", result: 0x41300,
state: TASK_STATE_READY, stateLabel: "ready", status: "ready",
},
{
name: "running", result: 0x41301,
state: TASK_STATE_RUNNING, stateLabel: "running", status: "running",
},
{
name: "disabled", result: 0x41302,
state: TASK_STATE_DISABLED, stateLabel: "disabled", status: "disabled",
},
{
name: "never run", result: 0x41303,
state: TASK_STATE_UNKNOWN, stateLabel: "unknown", status: "has_not_run",
},
{
name: "no more runs", result: 0x41304,
state: TASK_STATE_READY, stateLabel: "ready", status: "no_more_runs",
},
{
name: "not scheduled", result: 0x41305,
state: TASK_STATE_READY, stateLabel: "ready", status: "not_scheduled",
},
{
name: "terminated", result: 0x41306,
state: TASK_STATE_DISABLED, stateLabel: "disabled", status: "terminated",
},
{
name: "no valid triggers", result: 0x41307,
state: TASK_STATE_READY, stateLabel: "ready", status: "no_valid_triggers",
},
{
name: "event trigger", result: 0x41308,
state: TASK_STATE_READY, stateLabel: "ready", status: "event_trigger",
},
{
name: "queued", result: 0x41325,
state: TASK_STATE_QUEUED, stateLabel: "queued", status: "queued",
},
{
name: "signed HRESULT", result: -2147216629,
state: TASK_STATE_READY, stateLabel: "ready", status: "unknown",
},
{
name: "unsigned HRESULT", result: 0x8004130B,
state: TASK_STATE_READY, stateLabel: "ready", status: "unknown",
},
{
name: "unrecognized result", result: -559038737,
state: TASK_STATE_READY, stateLabel: "ready", status: "unknown",
},
{
name: "maximum code", result: -1,
state: TASK_STATE_READY, stateLabel: "ready", status: "unknown",
},
} {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
task := ScheduledTask{
Path: "/Test/Task",
State: tc.state,
MissedRunsCount: 3,
LastTaskResult: TaskResult(tc.result),
}
families := gatherTaskMetrics(t, nil, ScheduledTasks{task})
states := families["windows_scheduled_task_state"]
require.NotNil(t, states)
require.Equal(t, dto.MetricType_GAUGE, states.GetType())
require.Len(t, states.GetMetric(), 5)
expectedStates := map[string]float64{"disabled": 0, "queued": 0, "ready": 0, "running": 0, "unknown": 0}
expectedStates[tc.stateLabel] = 1
for _, metric := range states.GetMetric() {
require.Len(t, metric.GetLabel(), 2)
require.Equal(t, "state", metric.GetLabel()[0].GetName())
require.Equal(t, "task", metric.GetLabel()[1].GetName())
require.Equal(t, task.Path, metric.GetLabel()[1].GetValue())
state := metric.GetLabel()[0].GetValue()
want, ok := expectedStates[state]
require.True(t, ok, "unexpected or duplicate state %s", state)
require.InDelta(t, want, metric.GetGauge().GetValue(), 0)
delete(expectedStates, state)
}
require.Empty(t, expectedStates)
statuses := families["windows_scheduled_task_last_result_status"]
require.NotNil(t, statuses)
require.Equal(t, dto.MetricType_GAUGE, statuses.GetType())
require.Len(t, statuses.GetMetric(), 12)
expectedStatuses := map[string]float64{
"success": 0, "ready": 0, "running": 0, "disabled": 0,
"has_not_run": 0, "no_more_runs": 0, "not_scheduled": 0, "terminated": 0,
"no_valid_triggers": 0, "event_trigger": 0, "queued": 0, "unknown": 0,
}
expectedStatuses[tc.status] = 1
for _, metric := range statuses.GetMetric() {
require.Len(t, metric.GetLabel(), 2)
require.Equal(t, "status", metric.GetLabel()[0].GetName())
require.Equal(t, "task", metric.GetLabel()[1].GetName())
require.Equal(t, task.Path, metric.GetLabel()[1].GetValue())
status := metric.GetLabel()[0].GetValue()
want, ok := expectedStatuses[status]
require.True(t, ok, "unexpected or duplicate status %s", status)
require.InDelta(t, want, metric.GetGauge().GetValue(), 0)
delete(expectedStatuses, status)
}
require.Empty(t, expectedStatuses)
if tc.result == 0x41303 {
require.Len(t, families, 2)
require.NotContains(t, families, "windows_scheduled_task_last_result")
require.NotContains(t, families, "windows_scheduled_task_missed_runs")
return
}
require.Len(t, families, 4)
lastResult := families["windows_scheduled_task_last_result"]
require.NotNil(t, lastResult)
require.Len(t, lastResult.GetMetric(), 1)
wantSuccess := 0.0
if tc.result == 0 {
wantSuccess = 1
}
require.InDelta(t, wantSuccess, lastResult.GetMetric()[0].GetGauge().GetValue(), 0)
missedRuns := families["windows_scheduled_task_missed_runs"]
require.NotNil(t, missedRuns)
require.Len(t, missedRuns.GetMetric(), 1)
require.InDelta(t, task.MissedRunsCount, missedRuns.GetMetric()[0].GetGauge().GetValue(), 0)
})
}
}
func TestCollectMetricsFilters(t *testing.T) {
t.Parallel()
tasks := ScheduledTasks{
{Path: "/Included/Task", State: TASK_STATE_READY},
{Path: "/Included/Excluded", State: TASK_STATE_READY},
{Path: "/Other/Task", State: TASK_STATE_READY},
}
for _, tc := range []struct {
name string
config *Config
tasks ScheduledTasks
paths []string
}{
{name: "empty", tasks: ScheduledTasks{}, paths: []string{}},
{name: "defaults", tasks: tasks, paths: []string{"/Included/Task", "/Included/Excluded", "/Other/Task"}},
{
name: "include",
config: &Config{TaskInclude: regexp.MustCompile(`^/Included/`)},
tasks: tasks,
paths: []string{"/Included/Task", "/Included/Excluded"},
},
{
name: "exclude takes precedence",
config: &Config{
TaskInclude: regexp.MustCompile(`^/Included/`),
TaskExclude: regexp.MustCompile(`Excluded$`),
},
tasks: tasks,
paths: []string{"/Included/Task"},
},
{
name: "all excluded",
config: &Config{TaskExclude: regexp.MustCompile(`.*`)},
tasks: tasks,
paths: []string{},
},
} {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
families := gatherTaskMetrics(t, tc.config, tc.tasks)
if len(tc.paths) == 0 {
require.Empty(t, families)
return
}
require.Len(t, families, 4)
for _, family := range families {
paths := make([]string, 0, len(family.GetMetric()))
for _, metric := range family.GetMetric() {
for _, label := range metric.GetLabel() {
if label.GetName() == "task" {
paths = append(paths, label.GetValue())
}
}
}
samplesPerTask := 1
if family.GetName() == "windows_scheduled_task_state" {
samplesPerTask = 5
}
if family.GetName() == "windows_scheduled_task_last_result_status" {
samplesPerTask = 12
}
expected := make([]string, 0, samplesPerTask*len(tc.paths))
for range samplesPerTask {
expected = append(expected, tc.paths...)
}
require.ElementsMatch(t, expected, paths, "%s", family.GetName())
}
})
}
}
func gatherTaskMetrics(t *testing.T, config *Config, tasks ScheduledTasks) map[string]*dto.MetricFamily {
t.Helper()
c := New(config)
require.NoError(t, c.Build(nil, nil))
registry := prometheus.NewPedanticRegistry()
require.NoError(t, registry.Register(taskMetricCollector{collector: c, tasks: tasks}))
families, err := registry.Gather()
require.NoError(t, err)
result := make(map[string]*dto.MetricFamily, len(families))
for _, family := range families {
result[family.GetName()] = family
}
return result
}
type taskMetricCollector struct {
collector *Collector
tasks ScheduledTasks
}
func (c taskMetricCollector) Describe(ch chan<- *prometheus.Desc) {
prometheus.DescribeByCollect(c, ch)
}
func (c taskMetricCollector) Collect(ch chan<- prometheus.Metric) {
c.collector.collectMetrics(ch, c.tasks)
}
+16 -2
View File
@@ -1,4 +1,4 @@
# HELP test_alpha_total Some random metric.
# HELP test_alpha_total Some random metric.
# TYPE test_alpha_total counter
test_alpha_total 42
# HELP windows_cache_async_copy_reads_total (AsyncCopyReadsTotal)
@@ -325,9 +325,23 @@ windows_exporter_collector_timeout{collector="udp"} 0
# TYPE windows_physical_disk_write_seconds_total counter
# HELP windows_physical_disk_writes_total The number of write operations on the disk (PhysicalDisk.DiskWritesPerSec)
# TYPE windows_physical_disk_writes_total counter
# HELP windows_scheduled_task_last_result The result that was returned the last time the registered task was run
# HELP windows_scheduled_task_last_result DEPRECATED: use windows_scheduled_task_last_result_status. 1 if the last result code of the registered task is zero, 0 otherwise
# TYPE windows_scheduled_task_last_result gauge
windows_scheduled_task_last_result{task="/Microsoft/Windows/PLA/GAEvents"} 0
# HELP windows_scheduled_task_last_result_status The last result status of a scheduled task, 1 if the current status, 0 otherwise
# TYPE windows_scheduled_task_last_result_status gauge
windows_scheduled_task_last_result_status{status="disabled",task="/Microsoft/Windows/PLA/GAEvents"} 0
windows_scheduled_task_last_result_status{status="event_trigger",task="/Microsoft/Windows/PLA/GAEvents"} 0
windows_scheduled_task_last_result_status{status="has_not_run",task="/Microsoft/Windows/PLA/GAEvents"} 0
windows_scheduled_task_last_result_status{status="no_more_runs",task="/Microsoft/Windows/PLA/GAEvents"} 0
windows_scheduled_task_last_result_status{status="no_valid_triggers",task="/Microsoft/Windows/PLA/GAEvents"} 0
windows_scheduled_task_last_result_status{status="not_scheduled",task="/Microsoft/Windows/PLA/GAEvents"} 0
windows_scheduled_task_last_result_status{status="queued",task="/Microsoft/Windows/PLA/GAEvents"} 0
windows_scheduled_task_last_result_status{status="ready",task="/Microsoft/Windows/PLA/GAEvents"} 0
windows_scheduled_task_last_result_status{status="running",task="/Microsoft/Windows/PLA/GAEvents"} 1
windows_scheduled_task_last_result_status{status="success",task="/Microsoft/Windows/PLA/GAEvents"} 0
windows_scheduled_task_last_result_status{status="terminated",task="/Microsoft/Windows/PLA/GAEvents"} 0
windows_scheduled_task_last_result_status{status="unknown",task="/Microsoft/Windows/PLA/GAEvents"} 0
# HELP windows_scheduled_task_missed_runs The number of times the registered task missed a scheduled run
# TYPE windows_scheduled_task_missed_runs gauge
windows_scheduled_task_missed_runs{task="/Microsoft/Windows/PLA/GAEvents"} 0