Skip to content

Commit

Permalink
logon: BREAKING: replace wmi query by Win32 API calls and expose deta…
Browse files Browse the repository at this point in the history
…iled logon. (click PR for more information)

Signed-off-by: Jan-Otto Kröpke <[email protected]>
  • Loading branch information
jkroepke committed Oct 12, 2024
1 parent 22fdb33 commit d10074a
Show file tree
Hide file tree
Showing 6 changed files with 304 additions and 181 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Name | Description | Enabled by default
[netframework](docs/collector.netframework.md) | .NET Framework metrics |
[net](docs/collector.net.md) | Network interface I/O | &#10003;
[os](docs/collector.os.md) | OS metrics (memory, processes, users) | &#10003;
[perfdata](docs/collector.perfdata.md) | Custom perfdata metrics |
[physical_disk](docs/collector.physical_disk.md) | physical disk metrics | &#10003;
[printer](docs/collector.printer.md) | Printer metrics |
[process](docs/collector.process.md) | Per-process metrics |
Expand Down
51 changes: 38 additions & 13 deletions docs/collector.logon.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,60 @@

The logon collector exposes metrics detailing the active user logon sessions.

|||
-|-
Metric name prefix | `logon`
Classes | [`Win32_LogonSession`](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-logonsession)
Enabled by default? | No

> :warning: **On some deployments, this collector seems to have some memory/timeout issues**: See [#583](https://github.com/prometheus-community/windows_exporter/issues/583)
| | |
|---------------------|-----------|
| Metric name prefix | `logon` |
| Source | Win32 API |
| Enabled by default? | No |

## Flags

None

## Metrics

Name | Description | Type | Labels
-----|-------------|------|-------
`windows_logon_logon_type` | Number of active user logon sessions | gauge | status
| Name | Description | Type | Labels |
|-------------------------------------------|--------------------------------------------|-------|------------------------------------|
| `windows_logon_session_timestamp_seconds` | timestamp of the logon session in seconds. | gauge | `domain`, `id`, `type`, `username` |

### Example metric
Query the total number of interactive logon sessions
```
windows_logon_logon_type{status="interactive"}
# HELP windows_logon_session_timestamp_seconds timestamp of the logon session in seconds
# TYPE windows_logon_session_timestamp_seconds gauge
windows_logon_session_timestamp_seconds{domain="",id="103590",type="System",username=""} 1.728759837e+09
windows_logon_session_timestamp_seconds{domain="Font Driver Host",id="104592",type="Interactive",username="UMFD-0"} 1.728759837e+09
windows_logon_session_timestamp_seconds{domain="Font Driver Host",id="124850",type="Interactive",username="UMFD-1"} 1.728759838e+09
windows_logon_session_timestamp_seconds{domain="JOK-PC",id="521539",type="Interactive",username="Jan"} 1.728759839e+09
windows_logon_session_timestamp_seconds{domain="JOK-PC",id="521983",type="Interactive",username="Jan"} 1.728759839e+09
windows_logon_session_timestamp_seconds{domain="NT-AUTORITÄT",id="997",type="Service",username="Lokaler Dienst"} 1.728759838e+09
windows_logon_session_timestamp_seconds{domain="WORKGROUP",id="996",type="Service",username="JOK-PC$"} 1.728759838e+09
windows_logon_session_timestamp_seconds{domain="WORKGROUP",id="999",type="System",username="JOK-PC$"} 1.728759837e+09
windows_logon_session_timestamp_seconds{domain="Window Manager",id="148473",type="Interactive",username="DWM-1"} 1.728759838e+09
windows_logon_session_timestamp_seconds{domain="Window Manager",id="148582",type="Interactive",username="DWM-1"} 1.728759838e+09
```

### Possible values for `type`

- System
- Interactive
- Network
- Batch
- Service
- Proxy
- Unlock
- NetworkCleartext
- NewCredentials
- RemoteInteractive
- CachedInteractive
- CachedRemoteInteractive
- CachedUnlock

## Useful queries
Query the total number of local and remote (I.E. Terminal Services) interactive sessions.
```
windows_logon_logon_type{status=~"interactive|remote_interactive"}
count(windows_logon_logon_type{type=~"Interactive|RemoteInteractive"}) by (type)
```

## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_
_This collector doesn’t yet have alerting examples, we would appreciate your help adding them!_
188 changes: 20 additions & 168 deletions internal/collector/logon/logon.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
package logon

import (
"errors"
"fmt"
"log/slog"

"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/headers/secur32"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
Expand All @@ -20,10 +21,9 @@ var ConfigDefaults = Config{}

// A Collector is a Prometheus Collector for WMI metrics.
type Collector struct {
config Config
wmiClient *wmi.Client
config Config

logonType *prometheus.Desc
sessionInfo *prometheus.Desc
}

func New(config *Config) *Collector {
Expand Down Expand Up @@ -54,16 +54,11 @@ 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")
}

c.wmiClient = wmiClient
c.logonType = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "logon_type"),
"Number of active logon sessions (LogonSession.LogonType)",
[]string{"status"},
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
c.sessionInfo = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "session_timestamp_seconds"),
"timestamp of the logon session in seconds.",
[]string{"id", "username", "domain", "type"},
nil,
)

Expand All @@ -72,171 +67,28 @@ func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {

// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, _ *slog.Logger, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
logger.Error("failed collecting user metrics",
slog.Any("err", err),
)

return err
}

return nil
}

// Win32_LogonSession docs:
// - https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-logonsession
type Win32_LogonSession struct {
LogonType uint32
}

func (c *Collector) collect(ch chan<- prometheus.Metric) error {
var dst []Win32_LogonSession
if err := c.wmiClient.Query("SELECT * FROM Win32_LogonSession", &dst); err != nil {
return err
}

if len(dst) == 0 {
return errors.New("WMI query returned empty result set")
logonSessions, err := secur32.GetLogonSessions()
if err != nil {
return fmt.Errorf("failed to get logon sessions: %w", err)
}

// Init counters
system := 0
interactive := 0
network := 0
batch := 0
service := 0
proxy := 0
unlock := 0
networkcleartext := 0
newcredentials := 0
remoteinteractive := 0
cachedinteractive := 0
cachedremoteinteractive := 0
cachedunlock := 0

for _, entry := range dst {
switch entry.LogonType {
case 0:
system++
case 2:
interactive++
case 3:
network++
case 4:
batch++
case 5:
service++
case 6:
proxy++
case 7:
unlock++
case 8:
networkcleartext++
case 9:
newcredentials++
case 10:
remoteinteractive++
case 11:
cachedinteractive++
case 12:
cachedremoteinteractive++
case 13:
cachedunlock++
}
for _, session := range logonSessions {
ch <- prometheus.MustNewConstMetric(
c.sessionInfo,
prometheus.GaugeValue,
float64(session.LogonTime.Unix()),
session.LogonId.String(), session.UserName, session.LogonDomain, session.LogonType.String(),
)
}

ch <- prometheus.MustNewConstMetric(
c.logonType,
prometheus.GaugeValue,
float64(system),
"system",
)

ch <- prometheus.MustNewConstMetric(
c.logonType,
prometheus.GaugeValue,
float64(interactive),
"interactive",
)

ch <- prometheus.MustNewConstMetric(
c.logonType,
prometheus.GaugeValue,
float64(network),
"network",
)

ch <- prometheus.MustNewConstMetric(
c.logonType,
prometheus.GaugeValue,
float64(batch),
"batch",
)

ch <- prometheus.MustNewConstMetric(
c.logonType,
prometheus.GaugeValue,
float64(service),
"service",
)

ch <- prometheus.MustNewConstMetric(
c.logonType,
prometheus.GaugeValue,
float64(proxy),
"proxy",
)

ch <- prometheus.MustNewConstMetric(
c.logonType,
prometheus.GaugeValue,
float64(unlock),
"unlock",
)

ch <- prometheus.MustNewConstMetric(
c.logonType,
prometheus.GaugeValue,
float64(networkcleartext),
"network_clear_text",
)

ch <- prometheus.MustNewConstMetric(
c.logonType,
prometheus.GaugeValue,
float64(newcredentials),
"new_credentials",
)

ch <- prometheus.MustNewConstMetric(
c.logonType,
prometheus.GaugeValue,
float64(remoteinteractive),
"remote_interactive",
)

ch <- prometheus.MustNewConstMetric(
c.logonType,
prometheus.GaugeValue,
float64(cachedinteractive),
"cached_interactive",
)

ch <- prometheus.MustNewConstMetric(
c.logonType,
prometheus.GaugeValue,
float64(remoteinteractive),
"cached_remote_interactive",
)

ch <- prometheus.MustNewConstMetric(
c.logonType,
prometheus.GaugeValue,
float64(cachedunlock),
"cached_unlock",
)

return nil
}
Loading

0 comments on commit d10074a

Please sign in to comment.