How to Monitor Windows Log Files with PowerShell on Monitis

Lately, Monitis has been blogging a series of ‘how-to’ posts on everything from working with VBScripts to 101 tips to MySQL tuning and optimization. Continuing that tradition, here’s a short post on monitoring the content of log files with PowerShell on Monitis.

Using custom scripts, you can monitor not only numeric metrics but other kind of outcomes as well.

For example you can parse a log file and check if it contains (or doesn’t contain) a certain text. Many applications and services are using text log files, which share common characteristics:

– all log files are in the same folder

– all log files begin with the same name and have the same extension

– usually the date/time that the line was logged is at the beginning of the line itself

If your logs have the above features, you can easily parse them and report the outcome to your Monitis console.  In this article we will create a page with Custom Monitors and then describe a script to parse the logs and upload the result to those Custom Monitors.

About PowerShell

PowerShell is a powerful command line shell and a scripting language.  It allows a very easy interaction with several components of the operating system such as a registry, WMI, .NET Framework, COM objects and Performance Counters.

PowerShell was first released as version 1.0 in 2006, the current 2.0 version is already integrated in Windows 2008 R2 and Windows 7.

For previous operating systems, PowerShell 2.0 comes as an installation package integrated with Windows Management Framework Core. You can download the version supported by your OS from here: Once installed, look in your Start menu and you will find a blue icon named “Windows PowerShell;” Click it and you will get the PowerShell command line prompt.  You can edit PowerShell scripts with any text editor, the most popular editor specific for PowerShell is PowerGUI:

PowerShell and ExecutionPolicy

By default, PowerShell requires that all script are signed to be run, this means that your script won’t run unless you sign them. In order to run your custom script you will have to change the execution policy.

You can check the current execution policy settings with the command:


Inside a PowerShell window, possible values are Restricted (the default), RemoteSigned and Unrestricted.

Using RemoteSigned, you will be able to run custom scripts saved on your local drive. If you plan to save your scripts on network drives, you will have to use Unrestricted. In order to configure the appropriate execution policy you will have to run the following command:

Set-ExecutionPolicy RemoteSigned

Running PowerShell scripts

Once you have saved your PowerShell script, you can run them from a PowerShell window.
This differs slightly from running batch files from the usual Command Prompt because in PowerShell you will always have to specify the path. For example, in order to run a script in the current directory you will use the command:


Creating a page with the Custom Monitors

In order to interact with Monitis API, you have to first request a Token to be used in the following calls. As you can see below, this is easily done:


$apiKey = "Your API key here"
$secretKey = "Your secret key here"

write-host "Requesting token"
$url = "" + $apiKey + "&secretkey=" + $secretKey
$wc = new-object net.webclient
$resp = $wc.DownloadString($url).ToString()
$pos = $resp.IndexOf(":") + 2
$token = $resp.Substring($pos, $resp.Length - $pos - 2)
write-host "Token: " $token

Then we will add a page that will contain all our Custom Monitors:

write-host "Adding a page"
$nvc = new-object System.Collections.Specialized.NameValueCollection
$nvc.Add('apikey', $apikey)
$nvc.Add('validation', 'token')
$nvc.Add('authToken', $token)
$nvc.Add('timestamp', (get-date).touniversaltime().ToString("yyyy-MM-dd HH:mm:ss"))
$nvc.Add('action', 'addPage')
$nvc.Add('title', 'ParseLog')
$nvc.Add('columnCount', '2')

$wc = new-object net.webclient
$wc.Headers.Add("Content-Type", "application/x-www-form-urlencoded")
$resp = $wc.UploadValues('', $nvc)
$resp = [text.encoding]::ascii.getstring($resp)
$pos = $resp.IndexOf("pageId") + 8
$pageID = $resp.Substring($pos, $resp.Length - $pos - 2)

Since you may want to add several Custom Monitors, the code can be optimized using a function that creates the Custom Monitor and adds it to the page.

function AddCustMon([string] $name, [string] $monitorParams, [string] $resultParams, [string] $row, [string] $column) {
  Write-Host "Adding Custom Monitor " $name
  $nvc = new-object System.Collections.Specialized.NameValueCollection
  $nvc.Add('apikey', $apikey)
  $nvc.Add('validation', 'token')
  $nvc.Add('authToken', $token)
  $nvc.Add('timestamp', (get-date).touniversaltime().ToString("yyyy-MM-dd HH:mm:ss"))
  $nvc.Add('action', 'addMonitor')
  $nvc.Add('monitorParams', $monitorParams)
  $nvc.Add('resultParams', $resultParams)
  $nvc.Add('name', $name )
  $nvc.Add('tag', 'ParseLog' )

  $wc = new-object net.webclient
  $wc.Headers.Add("Content-Type", "application/x-www-form-urlencoded")
  $resp = $wc.UploadValues('', $nvc)
  $resp = [text.encoding]::ascii.getstring($resp)
  $pos = $resp.IndexOf("data") + 6
  $testID = $resp.Substring($pos, $resp.Length - $pos - 1)

  write-host "Adding test " $name " to the page"
  $nvc = new-object System.Collections.Specialized.NameValueCollection
  $nvc.Add('apikey', $apikey)
  $nvc.Add('validation', 'token')
  $nvc.Add('authToken', $token)
  $nvc.Add('timestamp', (get-date).touniversaltime().ToString("yyyy-MM-dd HH:mm:ss"))
  $nvc.Add('action', 'addPageModule')
  $nvc.Add('moduleName', 'CustomMonitor')
  $nvc.Add('pageId', $pageID)
  $nvc.Add('column', $column )
  $nvc.Add('row', $row )
  $nvc.Add('dataModuleId', $testID)

  $wc = new-object net.webclient
  $wc.Headers.Add("Content-Type", "application/x-www-form-urlencoded")
  $resp = $wc.UploadValues('', $nvc)

Then, call the function, once per Custom Monitor:

AddCustMon 'Log1' 'Log1:First Log file:TextMatch:3:false;;' 'match:Match:N%2FA:2;' '1' '1'

Parsing logs

In order to parse the logs you should pass some parameters to your script:


Enter the full path name of the log you want to parse. Wildcards are allowed, and the most recent file will be parsed.Example:


The text to search inside the log can be a simple string or a regular expression. You can find some examples on using regular expressions here:


Here you will specify if the normal condition is to find the MatchText or not, it can be $true or $false.  When $true, the result will be 1 if the MatchText IS found in at least ONE line, or else 0.
When $false, the result will be 1 if the MatchText IS NOT found in ANY of the lines, or else 0.


This parameter controls how the script behaves at startup: when $true, the script skips all existing lines in the log file; when $false, all existing lines are parsed.

The default is $true.


This is the number of minutes to wait between each time the script parses the log and uploads the result to the Custom Monitor. By default it s 5 minutes.


This is the tag assigned to the Custom Monitor to upload values to.


This is the name assigned to the Custom Monitor to upload values to.

A sample command line could be:

 .\ParseLog.ps1 -LogName C:\myappp\logs\AppLog*.log -MatchText "connection error" -ShouldMatch $true -OnlyNewLines $true -SleepMinutes 3 -Tag ParseLog -Name Log1

Now let’s explain the code.

Given a full path name with wildcards it is very easy to find the most recent file:

$files = @(Get-ChildItem $LogName | Sort-Object LastWriteTime -descending)
$LogName = $files[0].FullName

Once identified the file we will parse the lines and check for the text to match:

$dfre = [regex]("(?<date>" + ($DateFormat -replace "\w", "\d") +")")
$matchRes = 0;
foreach ($line in Get-Content $LogName) {
  try {
    $dtLine = [datetime]::ParseExact($dfre.matches($line)[0].Value, $DateFormat, $null)
    $diff = New-TimeSpan -Start $dtLine -End (Get-Date)

    if ($diff.TotalMinutes -le $MinutesBack) {
      if ($line -match $MatchText) {
        $matchRes = 1;
        Write-Host $line
        if ($ShouldMatch -eq $true) {
  catch {
if ($ShouldMatch -eq $false) {
  if ($matchRes -eq 0) {
    $matchRes = 1;
  } else {
    $matchRes = 0;

Once you have the result, you can upload it to the appropriate Custom Monitor as we have presented in previous articles.

You can find the full source code of the script here!

Stay tuned for our next blog posts with more advice on how to make your life easier — when it comes to IT, that is.