🚨 Powershell : Monitor a Log file for event

This is a simple script to monitor a log file in a specified file path and then send out a email notification when that string is found when it is seen more that four (4) time in the log file, that then need to fire off a remediation script to fix the issue and restore service.

Script : JavaRemediate.ps1

# Define parameters
$logFilePath = "C:\Quarantine\JavaHighCPU\Test.log" # Path to the log file
$errorText = "Caused by: java.io.IOException: HTTP/1.1 header parser received no bytes"
$smtpServer = "smtp.bear.local"
$alertRecipient = "lee@croucher.cloud"
$alertSender = "java.process@croucher.cloud"
$emailCooldownPeriod = 30 # Cooldown period in minutes
$htmlTemplatePath = "logmessage.html" # Path to the HTML template
$restartScriptPath = "RestartProcessandEmail.ps1" # Path to the restart script

# Get the server name
$serverName = [Environment]::MachineName

# Initialize last email sent time
$global:lastEmailSent = $null

# Initialize error counter
$global:errorCounter = 0

# Function to send an email alert
function Send-Alert {
    param (
        [string]$subject,
        [string]$body
    )
    $currentTime = Get-Date
    if ($global:lastEmailSent -eq $null -or ($currentTime - $global:lastEmailSent).TotalMinutes -ge $emailCooldownPeriod) {
        $htmlContent = Get-Content -Path $htmlTemplatePath -Raw
        $htmlContent = $htmlContent -replace "{{SUBJECT}}", $subject
        $htmlContent = $htmlContent -replace "{{BODY}}", $body
        $emailParams = @{
            From       = $alertSender
            To         = $alertRecipient
            Subject    = $subject
            Body       = $htmlContent
            BodyAsHtml = $true
            SmtpServer = $smtpServer
            Encoding   = [System.Text.Encoding]::UTF8
        }
        Send-MailMessage @emailParams     

        # Update the last email sent time
        $global:lastEmailSent = $currentTime

        # Run the restart script
        Run-RestartScript

        # Reset the error counter after sending the alert and running the script
        $global:errorCounter = 0
    }
}

# Function to run the restart script and wait for successful completion
function Run-RestartScript {
    Write-Output "Running restart script: $restartScriptPath" # Debug output
    $process = Start-Process -FilePath "powershell.exe" -ArgumentList "-ExecutionPolicy Bypass -File `"$restartScriptPath`"" -PassThru -Wait

    # Check the exit code
    if ($process.ExitCode -eq 0) {
        Write-Output "Restart script completed successfully." # Debug output
    } else {
        Write-Output "Restart script failed with exit code $($process.ExitCode)." 
    }
}

# Monitor log file for specific text
Get-Content -Path $logFilePath -Wait -Tail 0 | ForEach-Object {
    Write-Output "Reading log entry: $_" # Debug output
    if ($_ -like "*$errorText*") {
        # Increment the error counter
        $global:errorCounter++
        Write-Output "Trigger $global:errorCounter/4 found" # Output the trigger count

        # Check if the counter has reached 4
        if ($global:errorCounter -ge 4) {
            Write-Output "Trigger threshold reached. Sending alert and running external script." # Debug output
            $subject = "⚠️ Alert: Log file error detected on $serverName"
            $body = "Error detected in log file: $logFilePath<br><br>Error Details:<br>$_"
            Send-Alert -subject $subject -body $body
        }
    }
}

Provide the Log file path

This the the absolute path the log file you wish to monitor, that in this example is this variable:

$logFilePath = "C:\Quarantine\JavaHighCPU\Test.log" # Path to the log file

If you are not sure about gettting the full path then you can right click on the file  (while holding down shift) and choose the "Copy as path option"


If you do not see the option for "Copy as Path" you are not holding down the sift key before you right click the file.

Format the logmessage.html

This is the content that will be send when the string is detected, this is what I have used, however you requirements may be different, this is simply HTML design in its most basic form.

If you wish to test this mechanism then you can do, and the script is pointing at a log file in this blog entry and its looking for this:

$errorText = "Error on http communication.java.io.IOException: HTTP/1.1 header parser

This means if you edit that file that "may" be empty and enter that into the file, you should get an email shortfall thereafter, that proves the logic works.

Multiple Triggers Required

I have required multiple triggers of the keyword phase to fire off the email and the remediation script, I have chosen four (4) as the trigger but that can be changed in the script, here you can see it waiting for 4 triggers before sending the email and then running the remediation script:


Script : RestartProcessandEmail.ps1

This will restart the process and then confirm the service is back online before returning the "success" command back the previous script, I have built in a timeout section as well as many services take time to restart.

$serviceName = "ServiceCode" # Service Name of the service you wish to restart
$timeout = 180 # Timeout in seconds
$stopTime = Get-Date
$stopTime = $stopTime.AddSeconds($timeout)

# Email settings
$smtpServer = "smtp.bear.local"
$smtpFrom = "java.process@croucher.cloud"
$smtpTo = "lee@croucher.cloud"
$subject = "$serviceName Service Restarted"
$htmlFilePath = "restartedservice.html"

# Stop the service
Write-Output "Attempting to stop the service $serviceName..."
Stop-Service -Name $serviceName -Force -ErrorAction Stop

# Wait for the service to stop
do {
    Start-Sleep -Seconds 2
    $service = Get-Service -Name $serviceName
    if ($service.Status -eq 'Stopped') {
        Write-Output "$serviceName service has stopped."
        break
    }
    Write-Output "Waiting for $serviceName to stop..."
} while (($service.Status -ne 'Stopped') -and (Get-Date -lt $stopTime))

if ($service.Status -ne 'Stopped') {
    Write-Output "Timeout: $serviceName service did not stop within $timeout seconds."
    exit 1
}

# Restart the service
Write-Output "Starting the service $serviceName..."
Start-Service -Name $serviceName

# Confirm the service is running
$service = Get-Service -Name $serviceName
if ($service.Status -eq 'Running') {
    Write-Output "$serviceName service is now running."

    # Send confirmation email
    if (Test-Path $htmlFilePath) {
        $htmlBody = Get-Content -Path $htmlFilePath -Raw

        Send-MailMessage -From $smtpFrom -To $smtpTo -Subject $subject `
            -BodyAsHtml -Body $htmlBody -SmtpServer $smtpServer

        Write-Output "Confirmation email sent to $smtpTo."
    } else {
        Write-Output "Error: HTML file not found at $htmlFilePath."
    }
} else {
    Write-Output "Failed to start the $serviceName service."
    exit 1
}

This will also send another email this will confirm the script has restarted the service and the service is now back online, then once the e-mail is sent the script will default back to monitoring mode again.

Service Name Advisory

The service name you give this script is the "Service Name" not the "Display Name" of the service, a good example of this is the Windows Update service as you can see below these names differ massively.



$serviceName = "ServiceCode" #Service Name of the service you wish to restart

Ensure that the $serviceName value you give in the script (shown above) is the Service Name value from the correct value.

What if the service fails to restart in the timeout period?

Well if the service does not restart in the timeout period then increase the value, this is that value:

$timeout = 180 # Timeout in seconds

If you increase this to more than 5 minutes and the service is not stopping then you either have something wrong with the service stop process or a hanging process.

Why is my service is not stopping!

However some services will get into "Stopping" and never recover depending on what they do and this will call for a process termination or killing the process "tree" and that can be accommodated with the script below.

Powershell : Restart-Timeout-ForceRestart,ps1

$serviceName = "TAGETIK_TDL"
$processName = "TAGETIK_TDL-wrapper"
$timeout = 180 # Timeout in seconds
$stopTime = Get-Date
$stopTime = $stopTime.AddSeconds($timeout)
$htmlFilePath = "forcedrestart.html" # Update with the correct path to your HTML file
$smtpServer = "smtp.bear.local" # Update with your SMTP server
$smtpFrom = "java.process@croucher.cloud"
$smtpTo = "lee@croucher.cloud"
$subject = "$serviceName Service Restarted"

# Step 1: Attempt to stop the service
Write-Output "Attempting to stop the service $serviceName..."
Stop-Service -Name $serviceName -Force -ErrorAction Stop

# Step 2: Wait for the service to stop within the timeout
do {
    Start-Sleep -Seconds 2
    $service = Get-Service -Name $serviceName
    if ($service.Status -eq 'Stopped') {
        Write-Output "$serviceName service has stopped."
        break
    }
    Write-Output "Waiting for $serviceName to stop..."
} while (($service.Status -ne 'Stopped') -and (Get-Date -lt $stopTime))

# Step 3: Check if the service did not stop within the timeout
if ($service.Status -ne 'Stopped') {
    Write-Output "Timeout: $serviceName service did not stop within $timeout seconds."
    
    # Step 4: Find and kill the associated process
    $processes = Get-Process -Name $processName -ErrorAction SilentlyContinue
    if ($processes) {
        Write-Output "Forcing the termination of the process $processName..."
        Stop-Process -Name $processName -Force
        Write-Output "$processName process has been terminated."
    } else {
        Write-Output "No process named $processName found."
    }
    
    # Step 5: Exit with a failure code since the service didn't stop gracefully
    exit 1
}

# Step 6: Restart the service
Write-Output "Starting the service $serviceName..."
Start-Service -Name $serviceName

# Step 7: Confirm the service is running
$service = Get-Service -Name $serviceName
if ($service.Status -eq 'Running') {
    Write-Output "$serviceName service is now running."
    
    # Step 8: Send the HTML file via email
    if (Test-Path $htmlFilePath) {
        $htmlContent = Get-Content -Path $htmlFilePath -Raw
        Write-Output "Sending restart notification email..."
        Send-MailMessage -To $toAddress -From $fromAddress -Subject $subject -Body $htmlContent -BodyAsHtml -SmtpServer $smtpServer
        Write-Output "Restart notification email sent."
    } else {
        Write-Output "HTML file not found at $htmlFilePath. Email not sent."
    }
} else {
    Write-Output "Failed to start the $serviceName service."
    exit 1
}

Then with the new HTML template in the script you can then send a more information email, keeping people in the loop of the status and outcome.

Previous Post Next Post

نموذج الاتصال