Monitor Text File for Additional Information

I have a text log file that I need to monitor for a specific line. I know how to do this. However, when the Rule finds that line, I need information from a different line in the log file that is slightly above it. I am not sure where to begin to accomplish this. There are plenty of examples about how to parse a log file for a single line, but I am not sure how to do it so that it keeps track of all the text above the string I am looking for so I can include it an email.

Here is an example of what the log file will look like (and it will be in this format every time). I need to monitor the log file for the line “No alert email was sent.” and then also include the line “Condition: dest_path (\Server\Folder) does not exist or is not a directory” in the alert (and maybe some other lines if I can figure out how to do it).

Configuration File config\test210.get at 2013-11-17, 17:12:00

2013-11-17, 17:12:00
Condition: dest_path (\Server\Folder) does not exist or is not a directory

An error log was created and stored as:
C:/bin/…/errors/1117171200004132.log

The global configuration ‘SMTPserver’ property is not set or is set to an invalid value.
No alert email was sent.
2013-11-17, 17:12:00
Condition: ftp communication problem. Host: rpconnect.redprairie.net

An error log was created and stored as:
C:/live/bin/…/errors/1117171200004132.log

The global configuration ‘SMTPserver’ property is not set or is set to an invalid value.
No alert email was sent.

So… just as a kind of planning note, I don’t think PowerShell is exceptionally well-suited to ongoing “monitoring” activities. The file I/O routines in .NET, for example, aren’t the most robust (although they’ve improved), and PowerShell itself wasn’t designed with the idea of it being a long-running monitoring tool. So there’s definitely some performance/impact things to consider.

If you look at Select-String, it can do this kind of pattern-based search. It also has a -Context switch, which allows it to return a specified number of lines before/after the “hit.” The description for the parameter explains how the context is returned. That’s probably where you’d want to start.

Hi jacob,

You could try something like this:


$str = @"
2013-11-17, 17:12:01
Condition: dest_path (\\Server\Folder) does not exist or is not a directory

An error log was created and stored as:
C:/bin/../errors/1117171200004132.log

The global configuration ‘SMTPserver’ property is not set or is set to an invalid value.
No alert email was sent.
2013-11-17, 17:12:02
Condition: ftp communication problem. Host: rpconnect.redprairie.net

An error log was created and stored as:
C:/live/bin/../errors/1117171200004132.log

The global configuration ‘SMTPserver’ property is not set or is set to an invalid value.
No alert email was sent.
"@
$array = $str -split [System.Environment]::NewLine
$i = 0
$array | foreach{$i++;if($_ -eq "No alert email was sent."){$array[$i-6-1]}}

This implies that the 6th line above your “No alert email was sent.” is what you are looking for. Also have to substract 1 since the array is zero based. It is not pretty, however it works :slight_smile:

As a side note, this does not work if you paste the code into a console window. The here-string becomes an one item array in the console, but not in ISE. Apparently the -split is unable to split on newline in the console.

Cheers

Tore

Hi Jacob.

You could try and make the monitor remember key values from previous lines and react to the triggers you set up.

function Parse-LogFileForErrors { <#

#>
[CmdLetBinding()]
Param(
[parameter(ValueFromPipeline=$true)]
[ValidateScript({Test-Path $_})]
[String]$LogFile
)

BEGIN {
    $matchDateString   = '^\d{4}-\d{2}-\d{2}, \d{1,2}:\d{1,2}:\d{1,2}'
    $matchCondition    = '^Condition: (.*)$'
    $matchTrigger      = 'No alert email was sent.'
    $currentDateString = 'startup'
    $currentCondition  = 'startup'
} #end BEGIN
PROCESS {

    $allLines = Get-Content $LogFile
    foreach ($line in $allLines) {
        
        switch -Regex ($line) {
        
            $matchDateString { Write-verbose "Found datestring"
                               $currentDateString = [regex]::Match($line, $matchDateString) }
            $matchCondition  { Write-Verbose "Found condition" 
                               $currentCondition = [regex]::Match($line, $matchCondition) }
            $matchTrigger    { Write-Verbose "Found trigger, sending email [triggered: $currentDateString] [$currentCondition]"
                               # call Send-MailMessage using $currentContidion as body ?
            }
            default {}

        } #end switch      

    } #end foreach

} #end PROCESS

END {} #end END

}

Parse-LogFileForErrors -logFile d:\logfiles\logfile.log -Verbose

I’m shure there is a more elegant way to do this but this one gives me two “messages” and it is easy to extend.

Jacob sends his thanks for the responses - he’s having some trouble posting a reply.