PowerShell日志旧条目自动消失?如何让.log文件永久增长
日志文件旧条目丢失问题排查与解决
问题描述
通过GPO部署PowerShell脚本,用户登录时会在网络共享创建并写入日志(同时映射驱动器)。使用以下两个日志函数,创建时未启用OverWrite和UseDate开关,但日志写入新条目后旧条目会被删除。现有3000+用户,需要日志文件永久保留所有条目。
原函数代码:
# Generates the log Function New-Log{ Param( [Parameter(Mandatory=$true,Position=0)] [string]$Path, [Parameter(Position=1)] [string]$Folder=$null, [Parameter(Mandatory=$true,Position=2)] [string]$Name, [Parameter(Position=3)] [switch]$OverWrite, [Parameter(Position=4)] [switch]$UseDate, [Parameter(Position=5)] [int]$MaxDays = 0 ) # New folder (Yes/No) if($Folder){ $logPath = Join-Path -ChildPath $Folder -Path $Path } else{ $logPath = $Path } # Add date to log name (Yes/No) if($UseDate.IsPresent){ $logFile = "$(Get-Date -Format 'dd-MM_HH.mm.ss')_$($Name).log" } else{ $logFile = "$Name.log" } # Ovewrite existing log if($OverWrite.IsPresent){ $logFinal = (New-Item -Path $logPath -ItemType File -Name $logFile -Force).FullName } else{ if(Test-Path -Path "$logPath\$logFile"){ $logFinal = "$logPath\$logFile" } else{ $logFinal = (New-Item -Path $logPath -ItemType File -Name $logFile -Force).FullName } } # Deletion after a quantity of days if($MaxDays -gt 0){ if((Get-ChildItem -Path $logPath | Measure-Object).Count -gt $MaxDays){ (Get-ChildItem -Path $logPath | Sort-Object LastWriteTime -Descending | Select-Object -Last 1).FullName | Remove-Item -Force } } return $logFinal } # Add log events Function New-LogEvent{ Param( [Parameter(Mandatory=$true,Position=0)] [string]$File, [Parameter(Mandatory=$true,Position=1)] [string]$Event, [switch]$UseSemicolon ) $FLG = Get-Date -Format 'dd/MM/yyyy-HH:mm:ss' if($UseSemicolon.IsPresent){ Add-Content $File -Value "$FLG; $Event" } else{ Add-Content $File -Value "$FLG - $Event" } }
问题根源分析
- 并发竞态条件:多用户同时执行脚本时,
Test-Path判断文件不存在后,到New-Item创建文件的间隙,其他用户可能也完成了同样的判断并创建文件,此时New-Item -Force会直接覆盖已存在的文件,导致旧条目丢失。 - MaxDays逻辑错误:原代码中
MaxDays是按文件数量而非时间删除文件,即使你未主动设置,若后续误设会导致不符合预期的文件删除。 - 权限潜在问题:网络共享若未配置正确的读写权限,可能导致写入时文件被意外覆盖或截断。
解决方案
1. 修复日志创建逻辑,避免并发覆盖
将New-Log中创建文件的逻辑替换为更安全的方式,利用Add-Content的特性(文件不存在时自动创建,存在时追加),彻底避免竞态条件。
2. 修正MaxDays逻辑(按时间清理而非数量)
如果需要保留指定天数内的日志,改为删除超过MaxDays的旧文件,而非按文件数量删除。
3. 验证网络共享权限
确保所有用户对日志路径拥有读取、写入、修改权限,无只读或强制覆盖的权限限制。
修改后的函数代码
# Generates the log Function New-Log{ Param( [Parameter(Mandatory=$true,Position=0)] [string]$Path, [Parameter(Position=1)] [string]$Folder=$null, [Parameter(Mandatory=$true,Position=2)] [string]$Name, [Parameter(Position=3)] [switch]$OverWrite, [Parameter(Position=4)] [switch]$UseDate, [Parameter(Position=5)] [int]$MaxDays = 0 ) # 构建日志目录路径 $logPath = if ($Folder) { Join-Path -Path $Path -ChildPath $Folder } else { $Path } # 确保日志目录存在 if (-not (Test-Path -Path $logPath)) { New-Item -Path $logPath -ItemType Directory -Force | Out-Null } # 构建日志文件名 $logFile = if ($UseDate.IsPresent) { "$(Get-Date -Format 'dd-MM_HH.mm.ss')_$($Name).log" } else { "$Name.log" } $logFinal = Join-Path -Path $logPath -ChildPath $logFile # 处理覆盖逻辑 if ($OverWrite.IsPresent) { # 清空文件内容而非删除重建,避免并发问题 Set-Content -Path $logFinal -Value "" -Force } else { # 如果文件不存在,创建空文件(用Add-Content更安全,自动处理并发) if (-not (Test-Path -Path $logFinal)) { Add-Content -Path $logFinal -Value "" -Force } } # 按天数清理旧日志(修正逻辑:删除超过MaxDays的文件) if ($MaxDays -gt 0) { $cutoffDate = (Get-Date).AddDays(-$MaxDays) Get-ChildItem -Path $logPath -Filter "*.log" | Where-Object { $_.LastWriteTime -lt $cutoffDate } | Remove-Item -Force } return $logFinal } # Add log events Function New-LogEvent{ Param( [Parameter(Mandatory=$true,Position=0)] [string]$File, [Parameter(Mandatory=$true,Position=1)] [string]$Event, [switch]$UseSemicolon ) $FLG = Get-Date -Format 'dd/MM/yyyy-HH:mm:ss' $logEntry = if ($UseSemicolon.IsPresent) { "$FLG; $Event" } else { "$FLG - $Event" } # 使用Add-Content追加,确保不会覆盖 Add-Content -Path $File -Value $logEntry -Force }
修改说明
- 日志目录预创建:先确保日志目录存在,避免创建文件时因目录不存在报错。
- 避免并发覆盖:用
Add-Content创建空文件(而非New-Item),即使多用户同时执行,Add-Content也只会追加空内容,不会覆盖已有文件;覆盖逻辑改用Set-Content清空,而非删除重建。 - 修正日志清理逻辑:按文件最后写入时间删除超过指定天数的日志,符合
MaxDays的语义。 - 强化追加逻辑:
New-LogEvent中明确使用Add-Content追加条目,确保不会覆盖原有内容。
额外建议
- 对于3000+用户的高并发场景,建议为每个用户创建独立日志文件(比如在文件名中加入用户名),避免单文件过大导致的性能问题和冲突。如果必须合并到同一文件,可以考虑使用文件锁定机制(如
[System.IO.File]::Open指定FileShare.Read)防止并发写入冲突。 - 定期备份日志文件,防止意外丢失。
内容的提问来源于stack exchange,提问作者Franco Canciani
相关产品推荐
相关产品推荐

