Windows doesn't include a dedicated log rotation executable like Unix-based systems have with logrotate. However, the operating system provides several native mechanisms that can achieve similar functionality:
# PowerShell script for basic log rotation
$logPath = "C:\logs\app.log"
$archivePath = "C:\logs\archive\app_$(Get-Date -Format yyyyMMdd).log"
if (Test-Path $logPath) {
Move-Item $logPath $archivePath -Force
Start-Process -FilePath "C:\app\webserver.exe" -ArgumentList "--restart-logging"
}
The Event Viewer (eventvwr.msc) includes basic log archival settings:
- Maximum log size configuration
- Overwrite events as needed (circular logging)
- Archive when full options
For more advanced web server log rotation on Windows:
// Sample configuration for NXLog (community edition)
define ROOT C:\Program Files\nxlog
define CERTDIR %ROOT%\cert
<Input in>
Module im_file
File "C:\\logs\\access.log"
SavePos TRUE
Exec if file_size() > 10M then file_cycle("C:\\logs\\archive\\access.log.%Y-%m-%d");
</Input>
When evaluating tools, consider these technical factors:
- Compression support (gzip, zip)
- Time-based vs size-based rotation triggers
- Post-rotation command execution
- Permission handling for web server processes
Combine Task Scheduler with PowerShell for custom solutions:
# Scheduled task PowerShell script
$maxDays = 30
$logFolder = "C:\inetpub\logs"
Get-ChildItem "$logFolder\*.log" | Where-Object {
$_.LastWriteTime -lt (Get-Date).AddDays(-$maxDays)
} | Remove-Item -Force
# Rotate current logs
Get-ChildItem "$logFolder\*.log" | ForEach-Object {
$newName = "$($_.BaseName)_$(Get-Date -Format yyyyMMdd)$($_.Extension)"
Compress-Archive -Path $_.FullName -DestinationPath "$logFolder\archive\$newName.zip"
Clear-Content $_.FullName
}
Unlike Unix-based systems with built-in logrotate utilities, Windows requires third-party solutions for effective log rotation. The native Windows Event Log has limited rotation capabilities and doesn't meet most web server requirements.
Windows offers partial solutions through:
• Event Viewer's archive functionality (limited to Event Logs)
• PowerShell scripts using Limit-EventLog
• Windows Performance Monitor's circular logging
1. NXLog (Enterprise Edition)
NXLog provides robust log rotation with compression and encryption. Example configuration:
define ROOTDIR C:\Program Files\nxlog
define CERTDIR %ROOTDIR%\cert
<Input in>
Module im_file
File "C:\\logs\\webserver\\access.log"
SavePos TRUE
Exec $FileName = file_name();
$FilePath = file_path();
</Input>
<Output out>
Module om_file
File "C:\\logs\\webserver\\archive\\" +
strftime(now(), "%Y-%m-%d") +
"_" + $FileName
</Output>
2. LogRotateWin (Open Source)
A Windows port of Unix's logrotate with similar configuration syntax:
C:\logs\webserver\*.log {
daily
missingok
rotate 7
compress
delaycompress
notifempty
create 0640 www-data www-data
postrotate
net stop "MyWebService" && net start "MyWebService"
endscript
}
For custom rotation needs, this PowerShell script handles compression and archival:
# Set your log directory and retention policy
$logPath = "C:\logs\webserver"
$daysToKeep = 30
$archivePath = "C:\logs\archive"
# Create archive directory if not exists
if (-not (Test-Path $archivePath)) {
New-Item -ItemType Directory -Path $archivePath | Out-Null
}
# Rotate and compress logs older than 1 day
Get-ChildItem -Path "$logPath\*.log" | Where-Object {
$_.LastWriteTime -lt (Get-Date).AddDays(-1)
} | ForEach-Object {
$archiveName = "$archivePath\$($_.BaseName)_$(Get-Date -Format 'yyyyMMdd').zip"
Compress-Archive -Path $_.FullName -DestinationPath $archiveName -Update
Remove-Item $_.FullName
}
# Clean up old archives
Get-ChildItem -Path "$archivePath\*.zip" | Where-Object {
$_.LastWriteTime -lt (Get-Date).AddDays(-$daysToKeep)
} | Remove-Item
- Schedule rotation during low-traffic periods
- Test compression impact on CPU usage
- Consider SSD wear for high-volume logging
- Monitor disk space thresholds with alerts
For IIS, combine with the built-in Failed Request Tracing rotation:
%systemdrive%\inetpub\logs\LogFiles\W3SVC1\u_ex[YYMMDD].log
For Nginx on Windows, configure in nginx.conf:
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log logs/access.log main;
map $time_iso8601 $logdate {
'~^(?\d{4}-\d{2}-\d{2})' $ymd;
default 'nodate';
}
access_log logs/access-$logdate.log main;
}