powershellsharepointmonitoringlog-shipping

How do I ship logs from SharePoint in almost real time to a fileshare using PowerShell?


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

Solution

  • 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