Search code examples
windowspowershellloggingfilesystemwatcher

Unable to get the contents of modified file using FileSystemWatcher in PowerShell


I have an active production log file which deletes the contents of the logs after the file hits the size limit of 1 GB and also zips the file at every midnight thus in every day's zipped file, we do not have the complete log traffic for whole day. What we have is just the logs from the time it last hit 1 GB, till midnight of that day.

I am trying to watch that file and copy the contents of that file to some other file real-time. Upon some research, I came to know about FileSystemWatcher and tried several variations however could not append the contents of the file to another file.

With the following script, I am able to get the message displayed on the console. However, I am not able to append the contents to another file.

So, what I am trying to achieve is, whenever the XML file is changed, a real-time sync happens between the log file and some other file (Which I will create with this script) so that at the end of the day, I have a complete log file. Since this is a production system (about to be retired), it's quite busy so I am trying to implement a real-time sync solution.

$folder = "C:\Users\hslasw\docs\Log_Truncation_Issue"
$filter = "*.xml"
$Watcher = New-Object IO.FileSystemWatcher $folder, $filter -Property @{ 
    IncludeSubdirectories = $false
    NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'
}
$onChanged = Register-ObjectEvent $Watcher -EventName Changed -SourceIdentifier FileChanged -Action {
    $path = $Event.SourceEventArgs.FullPath
    $name = $Event.SourceEventArgs.Name
    $changeType = $Event.SourceEventArgs.ChangeType
    $timeStamp = $Event.TimeGenerated
    $filename = $path

    Write-Host (Get-Content -Path $path) |
        Out-File -Encoding Ascii -FilePath -Append C:\Users\xxxx\docs\Log_Truncation_Issue_Destination\testfile.xml
}
# Unregister-Event FileChanged

This is where the issue is:

Write-Host (Get-Content -Path $path) |
    Out-File -Encoding Ascii -FilePath -Append C:\Users\xxxx\docs\Log_Truncation_Issue_Destination\testfile.xml

Is there a better approach? Splitting the log file so it never reaches 1 GB is not possible in my situation. Also to note, the APP cannot be modified to change the logging behavior. I also considered rsync since it's an inbuilt tool. However, I need to pass the file name as a variable which doesn't seem to be supported by rsync.


Solution

  • Write-Host output goes directly to the host console, so the output does not go into the pipeline. Remove the Write-Host, and also remove the parentheses (as for what you want to do you don't need to read the file into memory first). The parameter order of your Out-File statement is incorrect too. The path argument belongs to the -FilePath parameter.

    $onChanged = Register-ObjectEvent $Watcher -EventName Changed -SourceIdentifier FileChanged -Action {
       $path       = $Event.SourceEventArgs.FullPath
       $name       = $Event.SourceEventArgs.Name
       $changeType = $Event.SourceEventArgs.ChangeType
       $timeStamp  = $Event.TimeGenerated
    
       Get-Content -Path $path |
           Out-File -FilePath 'C:\path\to\output.xml' -Encoding Ascii -Append
    }
    

    Also, beware that you cannot simply append to an XML file, as that would produce invalid XML. If you need to add data from one XML file to another XML file you should use PowerShell's XML parser. However, that would be a different question and should be asked as such.