PowerShell不直接记录服务启动耗时,但可通过事件ID 100(Kernel-Boot日志)获取自动启动服务的完成时间戳,或用Start-Service前后计时法测算手动启动耗时,并结合事件ID 7000/7009排查失败原因。
powershell 本身不直接记录单个 windows 服务的“启动耗时”,但可通过组合事件日志、时间戳比对与服务状态变化,间接测算关键服务从触发启动到进入 running 状态所经历的时间。这不是毫秒级仪表盘,而是面向运维可观测性的实用追踪方案。
抓取服务启动完成的确切时刻(事件 ID 100)
Windows 内核在每个服务成功启动后,会向 Microsoft-Windows-Kernel-Boot 日志源写入事件 ID 100,其中包含 ServiceName 和精确到毫秒的 StartTime 字段。这是最权威的启动完成标记。
- 执行命令提取最近一次某服务(如 WinRM)的启动事件:
Get-WinEvent -FilterHashtable @{LogName='System'; ProviderName='Microsoft-Windows-Kernel-Boot'; Id=100} -MaxEvents 50 | Where-Object {$_.Properties[0].Value -eq 'WinRM'} | Select-Object TimeCreated, @{n='ServiceName';e={$_.Properties[0].Value}}, @{n='StartTimeMs';e={$_.Properties[1].Value}} | Sort-Object TimeCreated -Descending | Select-Object -First 1 - 注意:该事件仅在服务由系统自动启动(非手动 Start-Service)时稳定生成;若用于监控自定义服务,请确保其 StartType = Automatic
用时间差法估算手动/计划启动耗时
当你通过脚本或任务计划启动一个服务时,可主动打点计时:在 Start-Service 前记录当前时间,在确认状态变为 Running 后再取一次时间,二者之差即为本次启动耗时。
- 示例片段(含超时保护):
$start = Get-Date
Start-Service -Name 'Spooler' -ErrorAction Stop
$maxWait = 30
1..$maxWait | ForEach-Object {
if ((Get-Service Spooler).Status -eq 'Running') { break }
Start-Sleep -Seconds 1
}
$end = Get-Date
$duration = $end - $start
Write-Host "Spooler 启动耗时: $($duration.TotalSeconds.ToString('F2')) 秒" - 此方法适用于自动化修复脚本中增加耗时反馈,便于识别缓慢服务(如 >8 秒应告警)
批量监控多个服务并输出耗时报告
将上述逻辑封装为可复用函数,结合服务清单与阈值判断,生成结构化结果,便于后续导出 HTML 或发邮件。
- 定义目标服务列表:
$services = @('WinRM', 'Dhcp', 'BITS') - 逐个执行带计时的启动检查(跳过已运行项):
$report = foreach ($svc in $services) {
$s = Get-Service $svc -ErrorAction SilentlyContinue
if (!$s) { continue }
if ($s.Status -eq 'Running') {
[PSCustomObject]@{ Service = $svc; Status = 'Running'; DurationSec = 0; Result = 'OK' }
} else {
$t0 = Get-Date
try {
Start-Service $svc -ErrorAction Stop
Wait-Service $svc -Status Running -Timeout 15
$t1 = Get-Date
$sec = ($t1 - $t0).TotalSeconds
[PSCustomObject]@{ Service = $svc; Status = 'Running'; DurationSec = [math]::Round($sec, 2); Result = if ($sec -gt 5) { 'Slow' } else { 'OK' } }
} catch {
[PSCustomObject]@{ Service = $svc; Status = 'Failed'; DurationSec = 0; Result = $_.Exception.Message.Substring(0, [Math]::Min(50, $_.Exception.Message.Length)) }
}
}
} - 输出为表格或导出 CSV:
$report | Where-Object Result -ne 'OK' | Format-Table -AutoSize
$report | Export-Csv "$env:TEMP\ServiceStartupTimes_$(Get-Date -Format 'yyyyMMdd_HHmm').csv" -NoTypeInformation
关联启动失败原因(查事件 ID 7000 / 7009)
若某服务启动耗时异常长或最终失败,不能只看“花了多久”,更要查“为什么卡住”。系统会在服务控制管理器(Service Control Manager)日志中记录依赖超时(ID 7009)或服务未响应(ID 7000)。
- 快速定位最近一次 Spooler 启动问题:
Get-WinEvent -FilterHashtable @{LogName='System'; ProviderName='Service Control Manager'; Id=7000,7009} -MaxEvents 20 | Where-Object {$_.Message -match 'Spooler'} | Format-List TimeCreated, Id, Message - 常见根因包括:依赖服务未就绪、权限不足、二进制文件损坏、注册表配置错误——这些信息比单纯耗时更有诊断价值


















