FileSystemWatcher写入日志后停止触发事件的原因与解决方法
问题原因及修复方案
核心原因
- 未处理异常终止事件线程:
Add-Content执行时可能遭遇未捕获的异常(比如日志文件被其他进程占用、写入权限不足、路径无效),PowerShell的FileSystemWatcher事件处理线程默认不会捕获异常,一旦抛出未处理异常,线程直接终止,导致Watcher无法再响应后续事件。 - 同步IO阻塞事件处理线程:FileSystemWatcher的事件处理是单线程执行的,
Add-Content同步写入日志如果耗时较长(比如大文件写入、磁盘IO延迟),会阻塞该线程,导致Watcher无法处理后续事件,甚至被系统判定为无响应而停止。
修复方案
方案1:添加异常捕获逻辑
在事件处理脚本块中包裹try/catch,确保即使日志写入失败,也不会终止事件线程:
$watcher = New-Object System.IO.FileSystemWatcher $watcher.Path = "." $watcher.Filter = "test.xlsx" $watcher.NotifyFilter = [System.IO.NotifyFilters]::FileName Register-ObjectEvent $watcher "Renamed" -Action { try { # 执行Python脚本 & python "your_script.py" # 写入日志 Add-Content -Path ".\watcher_log.txt" -Value "$(Get-Date): 文件重命名事件触发,原文件名: $($EventArgs.OldName),新文件名: $($EventArgs.Name)" } catch { # 捕获异常并记录错误日志 Add-Content -Path ".\watcher_error_log.txt" -Value "$(Get-Date): 处理事件时出错: $_" } } | Out-Null $watcher.EnableRaisingEvents = $true
方案2:异步写入日志避免阻塞
将日志写入逻辑放到后台线程执行,不阻塞事件处理线程:
$watcher = New-Object System.IO.FileSystemWatcher $watcher.Path = "." $watcher.Filter = "test.xlsx" $watcher.NotifyFilter = [System.IO.NotifyFilters]::FileName Register-ObjectEvent $watcher "Renamed" -Action { # 执行Python脚本 & python "your_script.py" # 异步写入日志 $logContent = "$(Get-Date): 文件重命名事件触发,原文件名: $($EventArgs.OldName),新文件名: $($EventArgs.Name)" Start-Job -ScriptBlock { param($content, $path) Add-Content -Path $path -Value $content } -ArgumentList $logContent, ".\watcher_log.txt" | Out-Null } | Out-Null $watcher.EnableRaisingEvents = $true
额外优化建议
- 使用绝对路径指定日志文件,避免PowerShell上下文变化导致路径无效;
- 若日志写入频繁,改用线程安全的
[System.IO.File]::AppendAllText替代Add-Content,它内部会处理文件锁定问题:[System.IO.File]::AppendAllText(".\watcher_log.txt", "$(Get-Date): 事件日志内容`n") - 定期清理日志文件,避免文件过大导致写入耗时增加。
内容的提问来源于stack exchange,提问作者Orange Pukeko
相关产品推荐
相关产品推荐

