I've got a SharePoint farm where I'm trying to ship the log files in "real time" to a server that is available for the monitoring team using PowerShell.
I first had it going pretty well using Get-SPLogEvent
, until i noticed that using the cmdlet in it self produced log entries. And I was going about it in 1 second intervalls :O
So back to my original idea with using Get-Content -Wait
then.
I put the log shipping in a job which is aborted when a newer log file is created.
This works reasonably well except when the logfile I'm trying to ship is to big to start with.
Most often, that is the case. The first log file I try to ship is empty, and the shipping is started only with the second log file.
Is there a way to have Get-Content -Wait
work correctly in a pipe with files as large as 75 - 100 MB?
$SourceLogFolder = <somewhere local>
$LogShipFolder = <somewhere remote>
while ($true) {
$NewLog = Get-ChildItem $SourceLogFolder | Select -Last 1 #Find the latest log file
if ($NewLog.Name -ne $CurrentLog.Name) {#the current log file has been closed or is new
$CurrentLog = $NewLog
Get-Job | Remove-Job -Force #clear any previous log shippings
Start-Job {#Tail CurrentLog
Get-Content $Using:CurrentLog.FullName -Wait |
Out-File "$($Using:LogShipFolder)\$($Using:CurrentLog.Name)" -Encoding utf8
}#end job
}#end if
sleep -seconds 30
}#end while
Ok, for some reason Out-File
sometimes creates the new file once before using it when piping large files.
So using Out-File -Force
will have it remove the 0 byte large shipped log file that was created.
[CmdletBinding()]
param (
[Parameter(Mandatory)][ValidateNotNullOrEmpty()]
[string]$SourceLogFolder,
[Parameter(Mandatory)][ValidateNotNullOrEmpty()]
[string]$LogShipFolder,
[int]$CheckInterval = 5, #seconds
[int]$CleanupTimeSpan = 3 #hours
)
$SourceLogFolder = $SourceLogFolder.TrimEnd('\')
$LogShipFolder = $LogShipFolder.TrimEnd('\')
$ShippedLogName = 'null'
while ($true) {
$NewLog = Get-ChildItem $SourceLogFolder | Select -Last 1
$ShippedLog = Get-ChildItem $ShippedLogName -ErrorAction SilentlyContinue
if (#the current log was closed or is new
$NewLog.Name -ne $CurrentLog.Name -and
$ShippedLog.Length -eq $ShippedLogJustNow.Length
) {
$CurrentLog = $NewLog
$ShippedLogName = "$LogShipFolder\$($CurrentLog.Name)"
Get-Job | Remove-Job -Force #kill previous log shipping
Start-ThreadJob {#Tail Current log
Get-Content ($Using:CurrentLog).FullName -Wait |
Out-File $Using:ShippedLogName -Encoding utf8 -Force
}#end job
Start-ThreadJob {#Cleanup shipped logs
$YoungestFileFound = 1
$OldLogFiles = Get-ChildItem $Using:LogShipFolder -Recurse |
where LastWriteTime -le (get-date).AddHours(-$Using:CleanupTimeSpan) |
Select -SkipLast $YoungestFileFound
$OldLogFiles | Remove-Item -Recurse -Force -Confirm:$false
}#end job
}#end if
$ShippedLogJustNow = Get-ChildItem $ShippedLogName -ErrorAction SilentlyContinue
sleep -Seconds $CheckInterval
[system.gc]::Collect() #Garbage collect to minimize memory leaks
}#end while