客户要求我计算机器和用户的平均登录时间。到目前为止,我发现事件记录的一些启动时间比以下键设置的阈值要长:
HKLM\Software\Microsoft\Windows\CurrentVersion\Diagnostics\Performance\Boot
但是这些键似乎被锁定了,所以我无法编辑它们以降低阈值,从而确保记录每次启动。是否有任何方法可以查找每次登录的登录时间,并且足够详细,可以告诉正在登录的用户以及可能更详细的信息,这也需要足够轻量,以便在每次登录时运行,并且不会对用户造成明显的影响。
答案1
最近有人要求我做一件非常类似的事情,但要包括启动和登录时间并允许历史参考。所以下面的 powershell 脚本执行以下操作:
- 获取一些环境变量
- 获取 4 个不同事件日志条目的时间\日期戳。其中第 2 个和第 4 个不是精确测量值,但经过相当广泛的研究、故障排除和测试后,它们非常接近,而且据我所知,它们是最佳选择。
- 计算这 4 个事件之间的差异
- 将所有数字填充到一个简单的 SQL 表中 [当然,您可以将数字输入到任何您想要的位置]
因此,如果您有 SCCM,该脚本将通过计划任务或按某些计划运行(不是在登录期间运行,以便根本不更改登录)。好处是您可以将 PCname 更改为其他任何名称,以便从您自己的 PC 运行它并从远程计算机获取数据(尽管用户名会显示为您自己的)以排除故障并验证数字。
我又迈出了一步,使用 SharePoint 创建了外部数据列表(使用 BCS),这样他们就拥有了一个不错的前端 GUI。下面的脚本,我保留了编写时使用的大部分注释行:
$USER = $env:username.ToUpper()
$COMPUTER = $env:computername.ToUpper()
$Current_Time = Get-Date
$PCname = ''
$addedtime = 0
#1. get event time of last OS load
$filterXML = @'
<QueryList>
<Query Id="0" Path="System">
<Select Path="System">*[System[Provider[@Name='Microsoft-Windows-Kernel-General'] and (Level=4 or Level=0) and (EventID=12)]]</Select>
</Query>
</QueryList>
'@
$OSLoadTime=(Get-WinEvent -ComputerName $PCname -MaxEvents 1 -FilterXml $filterXML).timecreated
#Write-Host $PCname
#Write-Host "1. Last System Boot @ " $OSLoadTime
#2. Get event time of Time-Service [pre-Ctrl+Alt-Del] after latest OS load
$filterXML = @'
<QueryList>
<Query Id="0" Path="System">
<Select Path="System">*[System[Provider[@Name='Microsoft-Windows-Time-Service'] and (Level=4 or Level=0) and (EventID=35)]]</Select>
</Query>
</QueryList>
'@
$CtrlAltDelTime=(Get-WinEvent -ComputerName $PCname -MaxEvents 1 -FilterXml $filterXML).timecreated
#Write-Host "2. Time-sync after Boot @ " $CtrlAltDelTime
#get minutes (rounded to 1 decimal) between OS load time and 1st load of GPOs
$BootDuration = "{0:N1}" -f ((($CtrlAltDelTime - $OSLoadTime).TotalSeconds + $addedtime)/60)
#3. get event time of 1st successful logon
$filterXML = @'
<QueryList>
<Query Id="0" Path="System">
<Select Path="System">*[System[Provider[@Name='Microsoft-Windows-Winlogon'] and (Level=4 or Level=0) and (EventID=7001)]]</Select>
</Query>
</QueryList>
'@
$LogonDateTime=(Get-WinEvent -ComputerName $PCname -MaxEvents 1 -FilterXml $filterXML -ErrorAction SilentlyContinue).timecreated
If ($LogonDateTime) {
#Write-Host "3. Successful Logon @ " $LogonDateTime
}
Else {
#Write-Host "Duration of Bootup = " $BootDuration "minutes" -foregroundcolor blue -BackgroundColor white
#Write-Host $PCname "has not logged back in." -foregroundcolor red -BackgroundColor white
Exit
}
#Write-Host "Duration of Bootup = " $BootDuration "minutes" -foregroundcolor blue -BackgroundColor white
#4. Get Win License validated after logon (about same time as explorer loads)
$filterXML = @'
<QueryList>
<Query Id="0" Path="Application">
<Select Path="Application">*[System[Provider[@Name='Microsoft-Windows-Winlogon'] and (Level=4 or Level=0) and (EventID=4101)]]</Select>
</Query>
</QueryList>
'@
$DesktopTime=(Get-WinEvent -ComputerName $PCname -MaxEvents 1 -FilterXml $filterXML).timecreated
$LogonDuration = "{0:N1}" -f ((($DesktopTime - $LogonDateTime).TotalSeconds + $addedtime)/60)
#Write-Host "4. WinLicVal after Logon @ " $DesktopTime
#Write-Host "Duration of Logon = " $LogonDuration "minutes" -foregroundcolor blue -BackgroundColor white
#START SQL Injection Section
[void][reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")
$sqlServer = "SQLserver01"
$dbName = "BootUpTimes"
$tbl = "tblBootUpTimes"
#$srv = New-Object Microsoft.SqlServer.Management.Smo.Server $sqlServer
#$db = $srv.databases[$dbName]
#$conn = New-Object System.Data.SqlClient.SqlConnection("Data Source=$sqlServer;Initial Catalog=$dbName; Integrated Security=SSPI")
$conn = New-Object System.Data.SqlClient.SqlConnection("server=$sqlServer;database=$dbName;Password=plaintext;User Id=BootUpTimes")
$conn.Open()
$cmd = $conn.CreateCommand()
$cmd.CommandText = "INSERT INTO $tbl VALUES ('$Current_Time','$USER','$COMPUTER','$OSLoadTime','$CtrlAltDelTime','$BootDuration','$LogonDateTime','$DesktopTime','$LogonDuration')"
Try
{
$null = $cmd.ExecuteNonQuery()
}
Catch
{
}
$conn.Close()
在最后的 SQL 部分中,有几行被注释掉,提供了另一种方法(基于安全性)来输入 SQL,而不需要以明文形式输入密码。
答案2
我不确定为什么有人会认为脚本会帮助您确定登录时间(毕竟,在有人登录之前您无法运行脚本,而拉取时间不会有帮助,因为时间漂移肯定会导致错误报告 - 这也直到启动处理才会修复。我建议您使用的工具是性能工具包中的 xperf 工具。您想查看 explorerinit 时间来了解总登录时间。请参阅Windows 开/关转换性能分析了解从启动到桌面的详细过程。请参阅Windows 性能分析工具从适当的位置获取 xperf 和 xbootmgr。
答案3
https://superuser.com/questions/250267/how-to-diagnose-slow-booting-or-logon-in-windows-7
此线程展示了使用 Windows 性能分析工具进行启动诊断的“Microsoft”方式
Microsoft 提供了有关“开/关过渡性能”(即打开或关闭 Windows)的非常好的记录程序:http://msdn.microsoft.com/en-us/windows/hardware/gg463386.aspx
使用这些官方工具,您可以为客户提供权威答案。在我看来,这比尝试使用脚本要好得多。如果您的需求很基本,那么可能有点过头了。
另外,如果您的需求非常基本,请不要错过该主题的 Soluto 网站 :)
答案4
以下作为登录脚本执行的批处理文件将在某种程度上告诉您从身份验证到 shell 准备就绪需要多长时间。
set logfile=\\server\share\%computername%-%username%.log
net user /domain %username% | find /I "Last logon" > %logfile%
net time %logonserver% >> %logfile%
我没有测试过这个,并且我做了一些假设。
- 返回的登录时间
net user
是 DC 执行身份验证的时间。我相信情况确实如此,但找不到任何具体的证据来支持这一点。 - 登录脚本在用户 shell 加载时运行。如果您使用较旧的传统 NT4 登录脚本,而不是组策略定义的登录脚本,情况确实如此,但由于 GPO 登录脚本对用户隐藏运行(默认情况下),我从未见过它们执行的时间。
- 您的用户名不包含空格,
%username%
如果是这种情况,您可能需要加上引号。 - 您有一个全球可写的共享,数据将记录在此(
\\server\share
如上例所示)。您可以在单个机器上本地记录数据,但这会使检查结果变得更加困难。
编辑:
我已经更新了脚本以处理 Jim 所担心的时间漂移问题。登录开始时间从net use
命令中获取,该时间取自身份验证域控制器的时钟。该net time
命令现在也从同一台服务器获取时间。