1. 为什么我劝你先学会看关机记录,而不是急着装监控软件
很多人遇到电脑半夜自动重启、下班关机第二天发现机器是开着的、或者服务器莫名其妙断过电,第一反应是去装个什么监控工具、上个带告警的软件。我早年也这么干过,后来发现纯属绕远路——Windows自己就一直在记这些事,只是大多数人从来没打开过那个界面。
这个能力就是事件查看器(Event Viewer)。它不是什么高级工具,就是系统自带的一个日志浏览器,从Windows NT时代活到现在,Win7、Win10、Win11、Server全系都有。它记录的东西细到什么程度?你几点几分关机、是正常关机还是异常断电、谁触发的重启、重启前系统有没有报错,全都在里面躺着。关键词就三个:Windows、事件查看器、关机/重启记录。
这篇文章适合谁看?三类人。第一类是普通用户,电脑老是自己重启,想搞清楚到底是谁干的;第二类是运维和IT支持,需要排查服务器非计划停机;第三类是做自动化、脚本、虚拟机的朋友,机器状态变化直接影响任务执行,必须有个可追溯的依据。不管你是哪类,看完这篇你都能自己动手把记录翻出来,不用求人。
我先说个反直觉的结论:关机记录比开机记录更有价值。开机记录只能告诉你"它起来了",关机记录能告诉你"它是怎么倒下的"。正常关机、强制断电、蓝屏崩溃、系统更新重启,这四种情况在日志里的表现完全不同,而区分它们,恰恰是定位问题的关键。下面我就按我实际排查的顺序,一层层拆给你看。
2. 事件查看器里到底该盯哪几个日志通道
打开事件查看器很简单,Win + R输入eventvwr.msc回车,或者在开始菜单搜"事件查看器"。但打开之后很多人就懵了——左边树状目录里几十个日志分类,Windows日志下面还有应用程序、安全、系统、Setup等等,到底看哪个?
我直接给结论:关机重启相关的记录,99%集中在"Windows 日志 → 系统"这一个通道里。别去应用程序日志里翻,那里是软件自己写的;也别一上来就看安全日志,那是登录审计用的。系统日志才是内核和系统服务汇报状态的地方。
2.1 系统日志里的事件来源怎么认
系统日志里每一行都有"来源"(Source)这一列,关机重启相关的来源主要有这么几个,我列个表你对照着记:
| 事件来源 | 典型事件ID | 含义 |
|---|---|---|
| Kernel-General | 12、13 | 系统启动(12)、系统关闭(13) |
| Kernel-Power | 41 | 非正常关机,通常是断电或强制关机 |
| EventLog | 6005、6006、6008 | 日志服务启动、停止、意外关闭 |
| User32 | 1074 | 有程序或用户主动发起的关机/重启 |
| Winlogon | 6000、6003 | 登录相关,辅助判断 |
| Kernel-Boot | 20、27 | 启动类型、引导信息 |
这张表是我自己排查时反复用的,建议你截图存下来。重点记三个:6006是正常关机,6008是意外关机,41是断电级别的异常。这三个ID基本能覆盖你80%的排查场景。
2.2 6006和6008的区别,是判断"正常"与"异常"的分水岭
很多人分不清6006和6008,我用大白话解释。6006的意思是"事件日志服务已停止",它出现的前提是系统走完了正常的关机流程,各个服务按顺序退出,最后日志服务自己关掉自己。所以你看到6006,基本可以认定这是一次体面的关机。
6008就难看了,它的原文是"上一次系统关机是意外的"。注意这个"上一次"——6008是在下次开机时才写进去的,因为系统当时已经来不及写日志了。它出现说明系统在关机时没有走完流程,可能是直接断电、长按电源键、蓝屏死机,或者虚拟机被宿主机强行杀掉。
提示:6008的时间戳是"意外关机发生的时间",不是它被记录的时间。这个细节很多人搞错,导致时间线对不上。
2.3 Kernel-Power 41:最需要警惕的那一条
如果说6008是"意外",那Kernel-Power 41就是"严重意外"。它的完整描述是"系统已重新启动,但未正常关闭"。触发41的原因通常是:供电中断、硬件故障、过热保护、驱动崩溃导致死机。它和6008经常成对出现,但41的指向性更强——它明确告诉你系统是"非正常掉电"。
我处理过一台工作站,每天凌晨三点左右重启,6008天天有,41隔三差五出现。最后查出来是电源老化,负载一高就保护性断电。所以看到41,别只盯着软件,先怀疑供电和散热,这是经验。
3. 手把手把关机记录筛出来:从图形界面到命令行
知道了看哪个通道、认哪些ID,接下来就是实操。我给你两条路:图形界面适合偶尔查一次,命令行适合批量导出和写脚本。两条都学会,你才算真正掌握。
3.1 图形界面筛选:三步锁定目标事件
第一步,打开事件查看器,左侧展开"Windows 日志",右键"系统",选"筛选当前日志"。
第二步,在弹出的窗口里找到"事件 ID"输入框,把你要查的ID用英文逗号隔开填进去。比如查关机,就填6006,6008,41,1074。注意是英文逗号,中文逗号不认。
第三步,如果只想看某个时间段,切到"筛选器"选项卡里的"记录时间",选"指定日期范围"。查"昨天为什么重启"就选昨天到今天的区间。
筛完之后,中间列表会只剩这几类事件。双击任意一条,下面"常规"标签里有人话描述,"详细信息"标签里有XML格式的原始数据。我一般直接看XML,因为里面字段更全,比如BugcheckCode、PowerButtonTimestamp这些,图形界面不显示。
3.2 用PowerShell一条命令导出关机历史
图形界面查一次两次还行,要长期跟踪就得靠命令。下面这条是我常用的,直接复制到管理员权限的PowerShell里跑:
Get-WinEvent -FilterHashtable @{ LogName='System' ID=6006,6008,41,1074 } | Select-Object TimeCreated, Id, ProviderName, Message | Sort-Object TimeCreated -Descending | Format-Table -AutoSize -Wrap这条命令的逻辑是:从System日志里捞出这四个ID,按时间倒序排列,输出时间、ID、来源和描述。-Wrap是为了让长描述换行显示,不然会被截断。
如果你想导出成CSV存档,把最后两行换成:
Export-Csv -Path "C:\shutdown_log.csv" -NoTypeInformation -Encoding UTF8这样每次排查完存一份,时间长了就能看出规律。我之前帮一个客户查"每周一早上机器状态不对",就是靠连续四周的CSV对比,发现是周末的备份任务把机器拖垮导致异常重启。
3.3 老系统用wevtutil,兼容性更好
有些环境还在跑Windows Server 2008或者Win7,PowerShell版本低,Get-WinEvent可能不好使。这时候用wevtutil这个老工具:
wevtutil qe System /q:"*[System[(EventID=6006 or EventID=6008 or EventID=41)]]" /f:text /rd:true /c:50参数解释一下:qe是查询事件,/q:后面是XPath查询语句,/f:text指定文本输出,/rd:true表示倒序(最新在前),/c:50限制返回50条。这条命令在几乎所有Windows版本上都能跑,是保底方案。
注意:
wevtutil查询语法是XPath,和PowerShell的哈希表写法不一样,别混用。我第一次用的时候把EventID=6006写成ID=6006,查出来空的,折腾了十分钟才发现。
4. 1074事件:揪出"是谁按下了重启键"
前面讲的6006、6008、41都是"结果",告诉你机器怎么关的。但很多时候你真正想知道的是"谁让它关的"——是Windows更新?是某个软件?还是有人远程操作?这个答案在事件ID 1074里。
1074的来源是User32,描述格式大致是:"进程 xxx 已代表用户 xxx 启动计算机 xxx 的关机,原因如下:xxx"。这句话信息量极大,它把发起进程、发起用户、关机类型、关机原因全交代了。
4.1 从1074里读出四个关键字段
我拿一条真实记录举例(脱敏处理):
进程 C:\Windows\System32\svchost.exe 已代表用户 NT AUTHORITY\SYSTEM 启动计算机 的 重启,原因如下: 操作系统: 恢复 (计划内) 原因代码: 0x80020002 关机类型: 重启拆开看:进程是svchost,用户是SYSTEM,说明是系统服务发起的;原因是"恢复(计划内)",原因代码0x80020002,关机类型是重启。这一条基本可以判定是Windows更新或系统维护任务触发的重启。
再看另一条:
进程 C:\Program Files\SomeApp\app.exe 已代表用户 DESKTOP-XXX\admin 启动计算机 的 关机,原因如下: 其他 (计划外) 原因代码: 0x500ff 关机类型: 关机这条就明确了,是某个应用以admin身份发起的关机,属于计划外。如果你不认识这个app,那它就是嫌疑对象。
4.2 原因代码对照:0x80020002和0x500ff差在哪
原因代码是个32位数,高16位表示"原因类型",低16位表示"具体原因"。常用的几个我整理如下:
| 原因代码 | 含义 |
|---|---|
| 0x80020002 | 系统更新/维护,计划内 |
| 0x80020003 | 系统更新,计划内,其他 |
| 0x500ff | 其他,计划外,用户发起 |
| 0x40010004 | 电源按钮,计划外 |
| 0x84020004 | 应用程序发起的更新重启 |
看到0x8002xxxx基本就是系统自己干的,看到0x500ff或0x4001xxxx就要警惕,前者是软件,后者是物理按键。这个对照表网上不常见,是我从微软文档和实际记录里一点点攒出来的,你存着有用。
4.3 没有1074怎么办:反推法
有时候你只看到6006正常关机,但没看到对应的1074。这种情况通常是关机流程走得太快,或者日志被覆盖了。这时候用反推法:先找到6006的时间点,然后往前推30秒到2分钟,看这个窗口内有没有1074、有没有应用程序日志里的异常、有没有系统服务的停止记录。
我遇到过一次,6006有,1074没有,往前翻发现是某个备份软件在关机前调用了系统API直接关机,没走User32的路径。这种就得结合应用程序日志一起看,单看系统日志会漏。
5. 虚拟机、蓝屏、更新重启:三种特殊场景的日志特征
前面讲的是通用方法,但实际排查中,有三类场景特别容易让人迷惑,我单独拎出来说。
5.1 虚拟机里的"意外关机"往往是宿主机干的
在VMware、Hyper-V、VirtualBox里跑系统,你经常会在客户机日志里看到6008和41。别急着怀疑客户机自己有问题,大概率是宿主机把它杀了。比如宿主机重启、宿主机资源不足触发回收、或者你直接点了"关闭电源"而不是"关机"。
判断方法:看客户机41事件的时间戳,和宿主机的操作时间对不对得上。如果对得上,那就是宿主机的锅。另外,Hyper-V的客户机如果装了集成服务,正常关机时会走一套"心跳"流程,日志里会有对应的关机请求记录;如果没装或者被强制关闭,就只有41。
提示:虚拟机排查时,客户机日志和宿主机日志要对着看,只看一边永远找不到真相。
5.2 蓝屏后的重启:41里藏着BugcheckCode
蓝屏(BSOD)导致的重启,在系统日志里也是41,但它的XML里会多一个字段叫BugcheckCode。这个代码是定位蓝屏原因的关键。比如BugcheckCode=0x0000007E通常指向驱动问题,0x00000050指向内存问题。
提取方法:
Get-WinEvent -FilterHashtable @{LogName='System'; ID=41} | ForEach-Object { $xml = [xml]$_.ToXml() [PSCustomObject]@{ Time = $_.TimeCreated BugcheckCode = $xml.Event.EventData.Data | Where-Object {$_.Name -eq 'BugcheckCode'} | Select-Object -ExpandProperty '#text' } }这段脚本把41事件里的BugcheckCode单独抽出来,配合蓝屏代码表一查,方向就清楚了。我一般还会同时看C:\Windows\Minidump目录下有没有dump文件,有的话用分析工具进一步定位。
5.3 Windows更新重启:1074 + 19 + 43的组合拳
系统更新导致的重启,日志特征很典型:先有一条1074(原因代码0x80020002),然后关机时6006,开机后可能还有事件ID 19(更新安装成功)和43(更新开始安装)。这一串连起来,就是一次完整的更新重启。
如果你发现机器在凌晨自动重启,先别慌,按这个组合去对。对上了就是更新,对不上再往硬件和软件方向查。我见过有人因为更新重启把重要任务跑挂了,后来在任务计划里把"活动时间"设成工作时间,就避开了。
6. 日志被覆盖、时间对不上、权限不够:三个高频坑的解法
方法讲完了,但实操中总有意外。下面这三个坑我几乎每次带新人都要讲一遍。
6.1 日志被覆盖:默认只有20MB,几天就滚没了
系统日志默认最大20MB,机器一忙,几天就写满,老的记录被循环覆盖。等你出问题去查,发现记录没了,那叫一个绝望。
解决办法:提前把日志调大。图形界面在"系统"日志右键→属性,把"日志最大大小"改成100MB甚至更大,选"按需覆盖"或"存档"。命令行更利索:
wevtutil sl System /ms:104857600/ms:后面是字节数,104857600就是100MB。这个操作建议所有做运维的都提前做,别等出事。
6.2 时间对不上:时区和UTC的坑
事件查看器默认显示本地时间,但XML里存的是UTC。你用脚本导出的时候,如果不做转换,时间会差8小时(东八区)。我第一次导出CSV给客户,客户说"你这时间怎么是凌晨",就是这个问题。
PowerShell里TimeCreated默认已经是本地时间,但如果你直接读XML的SystemTime字段,那是UTC。转换方法:
[System.TimeZoneInfo]::ConvertTimeToUtc($localTime)或者反过来。总之导出前先确认时区,不然时间线全乱。
6.3 权限不够:普通用户看不到安全日志
系统日志普通用户能看,但安全日志(Security)需要管理员权限,而且默认审计策略下,关机重启不一定记在安全日志里。如果你查的是"谁登录后关的机",那得先开审计策略,再结合安全日志的4634(注销)和1074一起看。
开启方法:secpol.msc→ 本地策略 → 审核策略 → 审核登录事件,勾选成功和失败。这个改动会影响日志量,生产环境要评估。
7. 把排查流程固化成脚本:我的日常做法
查得多了,我就把常用操作写成一个脚本,放在U盘里,到哪台机器插上就能跑。核心逻辑是:先导出最近N天的关机相关事件,再按ID分类统计,最后输出一个摘要。
$days = 7 $start = (Get-Date).AddDays(-$days) $events = Get-WinEvent -FilterHashtable @{ LogName='System'; ID=6006,6008,41,1074; StartTime=$start } -ErrorAction SilentlyContinue $summary = $events | Group-Object Id | Select-Object Name, Count $summary | Format-Table -AutoSize $events | Sort-Object TimeCreated -Descending | Select-Object TimeCreated, Id, @{N='Brief';E={$_.Message.Split("`n")[0]}} | Export-Csv "C:\shutdown_report.csv" -NoTypeInformation -Encoding UTF8跑完你会得到一张统计表(每个ID出现几次)和一份明细CSV。统计表用来看趋势,明细用来看具体。我一般每周跑一次,机器状态心里有数。
提示:
-ErrorAction SilentlyContinue是为了防止某个ID不存在时报错中断,加上更稳。
8. 几个我踩过的坑和私藏技巧
最后分享几个文档里不会写、但实际特别有用的点。
第一,6008的时间戳是"上次关机时间",不是"本次开机时间"。这个我强调过,但还是有人搞错。你看到6008,它的时间往前推,才是问题发生的时刻。
第二,关机记录和开机记录要配对看。一次完整的循环是:6006(关机)→ 12(启动)→ 6005(日志服务启动)。如果中间缺了6006,直接跳到12,那这次关机就是异常的。这个配对法比单看一个ID准得多。
第三,Kernel-Boot的20和27能告诉你启动类型。20是正常启动,27是引导相关。如果机器频繁出现27,可能是引导配置有问题,值得深挖。
第四,日志里的"任务类别"字段别忽略。比如1074的任务类别会写"关机",41会写"无",这些细节能帮你快速分类。
第五,长期跟踪建议用"自定义视图"。事件查看器支持把筛选条件存成自定义视图,下次直接点开就行,不用重新填ID。路径是左侧"自定义视图"右键→创建自定义视图,把筛选条件配好保存。我给自己建了三个:日常关机、异常关机、更新重启,切换起来一秒的事。
这套东西我从Win7时代用到现在,Win11和Server 2022上依然好使。核心就一句话:系统一直在记,你只要知道去哪看、看哪个ID、怎么把时间线串起来。剩下的,就是熟练度的问题了。