Merge branch 'master' into release-please--branches--master--components--butler-sos

This commit is contained in:
Göran Sander
2025-09-29 13:51:45 +02:00
committed by GitHub
9 changed files with 831 additions and 489 deletions

View File

@@ -25,12 +25,14 @@ jobs:
shell: powershell
continue-on-error: false
run: |
Write-Host "🔍 Starting Butler SOS log monitoring..."
Write-Host "Monitor run time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss UTC')"
Write-Host "Starting Butler SOS log monitoring..."
$currentTime = Get-Date -Format 'yyyy-MM-dd HH:mm:ss UTC'
Write-Host "Monitor run time: $currentTime"
Write-Host "Server: $env:SERVER_NAME"
Write-Host "Service: $env:BUTLER_SOS_INSIDER_SERVICE_NAME"
Write-Host "Deploy path: $env:BUTLER_SOS_INSIDER_DEPLOY_PATH"
# Define paths and variables
$deployPath = $env:BUTLER_SOS_INSIDER_DEPLOY_PATH
$serviceName = $env:BUTLER_SOS_INSIDER_SERVICE_NAME
$currentDate = Get-Date -Format "yyyy-MM-dd"
@@ -46,150 +48,136 @@ jobs:
Write-Host " Current day log: $currentDayLogPath"
Write-Host " Previous day log: $previousDayLogPath"
# Initialize variables
$errorFound = $false
$allErrorEntries = @()
$currentTotalLines = 0
$currentInfoCount = 0
$currentWarnCount = 0
$currentErrorCount = 0
$currentFatalCount = 0
$previousTotalLines = 0
$previousInfoCount = 0
$previousWarnCount = 0
$previousErrorCount = 0
$previousFatalCount = 0
try {
# Check service-error.log
Write-Host "📋 Checking service-error.log..."
if (Test-Path $serviceErrorLogPath) {
Write-Host "✅ Found service-error.log"
$serviceErrorContent = Get-Content -Path $serviceErrorLogPath -ErrorAction SilentlyContinue
if ($serviceErrorContent -and $serviceErrorContent.Count -gt 0) {
Write-Host "❌ Errors found in service-error.log:" -ForegroundColor Red
$serviceErrorContent | ForEach-Object {
Write-Host " $_" -ForegroundColor Red
$allErrorEntries += "SERVICE-ERROR: $_"
# Check service-error.log
Write-Host "Checking service-error.log..."
if (Test-Path $serviceErrorLogPath) {
Write-Host "Found service-error.log"
$serviceErrorContent = Get-Content -Path $serviceErrorLogPath -ErrorAction SilentlyContinue
if ($serviceErrorContent -and $serviceErrorContent.Count -gt 0) {
Write-Host "Errors found in service-error.log:" -ForegroundColor Red
foreach ($line in $serviceErrorContent) {
Write-Host " $line" -ForegroundColor Red
$allErrorEntries += "SERVICE-ERROR: $line"
}
$errorFound = $true
} else {
Write-Host "service-error.log is empty"
}
} else {
Write-Host "service-error.log does not exist"
}
# Check current day log file
Write-Host "Checking current day log file..."
if (Test-Path $currentDayLogPath) {
Write-Host "Found current day log file"
$currentDayContent = Get-Content -Path $currentDayLogPath -ErrorAction SilentlyContinue
if ($currentDayContent) {
$totalLines = $currentDayContent.Count
Write-Host "Current day log has $totalLines lines"
# Check for ERROR or FATAL entries (case-insensitive, simple approach)
$errorLines = @()
foreach ($line in $currentDayContent) {
$upperLine = $line.ToUpper()
if ($upperLine.Contains("ERROR") -or $upperLine.Contains("FATAL")) {
$errorLines += $line
}
}
if ($errorLines.Count -gt 0) {
Write-Host "Found $($errorLines.Count) error/fatal entries in current day log:" -ForegroundColor Red
foreach ($errorLine in $errorLines) {
Write-Host " $errorLine" -ForegroundColor Red
$allErrorEntries += "CURRENT-DAY: $errorLine"
}
$errorFound = $true
} else {
Write-Host "✅ service-error.log is empty"
Write-Host "No error/fatal entries in current day log"
}
} else {
Write-Host " service-error.log does not exist"
Write-Host "Current day log file is empty"
}
} else {
Write-Host "Current day log file does not exist"
}
# Check previous day log file
Write-Host "Checking previous day log file..."
if (Test-Path $previousDayLogPath) {
Write-Host "Found previous day log file"
$previousDayContent = Get-Content -Path $previousDayLogPath -ErrorAction SilentlyContinue
if ($previousDayContent) {
$totalLines = $previousDayContent.Count
Write-Host "Previous day log has $totalLines lines"
# Check for ERROR or FATAL entries (case-insensitive, simple approach)
$errorLines = @()
foreach ($line in $previousDayContent) {
$upperLine = $line.ToUpper()
if ($upperLine.Contains("ERROR") -or $upperLine.Contains("FATAL")) {
$errorLines += $line
}
}
if ($errorLines.Count -gt 0) {
Write-Host "Found $($errorLines.Count) error/fatal entries in previous day log:" -ForegroundColor Red
foreach ($errorLine in $errorLines) {
Write-Host " $errorLine" -ForegroundColor Red
$allErrorEntries += "PREVIOUS-DAY: $errorLine"
}
$errorFound = $true
} else {
Write-Host "No error/fatal entries in previous day log"
}
} else {
Write-Host "Previous day log file is empty"
}
} else {
Write-Host "Previous day log file does not exist"
}
# Set outputs based on results
if ($errorFound) {
Write-Host "ERRORS DETECTED!" -ForegroundColor Red
$errorCount = $allErrorEntries.Count
Write-Host "Total errors: $errorCount"
# Prepare error data for email step
$errorData = @{
ErrorFound = $true
ErrorEntries = $allErrorEntries
CurrentLogPath = $currentDayLogPath
PreviousLogPath = $previousDayLogPath
ServiceErrorLogPath = $serviceErrorLogPath
CurrentTotalLines = 0
CurrentInfoCount = 0
CurrentWarnCount = 0
CurrentErrorCount = 0
CurrentFatalCount = 0
PreviousTotalLines = 0
PreviousInfoCount = 0
PreviousWarnCount = 0
PreviousErrorCount = 0
PreviousFatalCount = 0
}
# Check current day log file
Write-Host "📋 Checking current day log file..."
if (Test-Path $currentDayLogPath) {
Write-Host "✅ Found current day log file"
$currentDayContent = Get-Content -Path $currentDayLogPath -ErrorAction SilentlyContinue
if ($currentDayContent) {
$currentTotalLines = $currentDayContent.Count
$currentInfoCount = ($currentDayContent | Where-Object { $_ -match "(?i)\binfo[\s:]" }).Count
$currentWarnCount = ($currentDayContent | Where-Object { $_ -match "(?i)\bwarn[\s:]" }).Count
$currentErrorCount = ($currentDayContent | Where-Object { $_ -match "(?i)\berror[\s:]" }).Count
$currentFatalCount = ($currentDayContent | Where-Object { $_ -match "(?i)\bfatal[\s:]" }).Count
# Check for error/fatal entries
$errorEntries = $currentDayContent | Where-Object { $_ -match "(?i)\b(error|fatal)[\s:]" }
if ($errorEntries -and $errorEntries.Count -gt 0) {
Write-Host "❌ Error/Fatal entries found in current day log:" -ForegroundColor Red
$errorEntries | ForEach-Object {
Write-Host " $_" -ForegroundColor Red
$allErrorEntries += "CURRENT-DAY: $_"
}
$errorFound = $true
} else {
Write-Host "✅ No error/fatal entries in current day log"
}
Write-Host "Current day log stats: Total=$currentTotalLines, Info=$currentInfoCount, Warn=$currentWarnCount, Error=$currentErrorCount, Fatal=$currentFatalCount"
} else {
Write-Host " Current day log file is empty"
}
} else {
Write-Host " Current day log file does not exist"
}
# Check previous day log file
Write-Host "📋 Checking previous day log file..."
if (Test-Path $previousDayLogPath) {
Write-Host "✅ Found previous day log file"
$previousDayContent = Get-Content -Path $previousDayLogPath -ErrorAction SilentlyContinue
if ($previousDayContent) {
$previousTotalLines = $previousDayContent.Count
$previousInfoCount = ($previousDayContent | Where-Object { $_ -match "(?i)\binfo[\s:]" }).Count
$previousWarnCount = ($previousDayContent | Where-Object { $_ -match "(?i)\bwarn[\s:]" }).Count
$previousErrorCount = ($previousDayContent | Where-Object { $_ -match "(?i)\berror[\s:]" }).Count
$previousFatalCount = ($previousDayContent | Where-Object { $_ -match "(?i)\bfatal[\s:]" }).Count
# Check for error/fatal entries
$errorEntries = $previousDayContent | Where-Object { $_ -match "(?i)\b(error|fatal)[\s:]" }
if ($errorEntries -and $errorEntries.Count -gt 0) {
Write-Host "❌ Error/Fatal entries found in previous day log:" -ForegroundColor Red
$errorEntries | ForEach-Object {
Write-Host " $_" -ForegroundColor Red
$allErrorEntries += "PREVIOUS-DAY: $_"
}
$errorFound = $true
} else {
Write-Host "✅ No error/fatal entries in previous day log"
}
Write-Host "Previous day log stats: Total=$previousTotalLines, Info=$previousInfoCount, Warn=$previousWarnCount, Error=$previousErrorCount, Fatal=$previousFatalCount"
} else {
Write-Host " Previous day log file is empty"
}
} else {
Write-Host " Previous day log file does not exist"
}
# Summary
if ($errorFound) {
Write-Host "❌ ERRORS DETECTED - Taking action!" -ForegroundColor Red
Write-Host "Total error entries found: $($allErrorEntries.Count)" -ForegroundColor Red
# Export error data for email step
$errorData = @{
ErrorFound = $true
ErrorEntries = $allErrorEntries
CurrentLogPath = $currentDayLogPath
PreviousLogPath = $previousDayLogPath
ServiceErrorLogPath = $serviceErrorLogPath
CurrentTotalLines = $currentTotalLines
CurrentInfoCount = $currentInfoCount
CurrentWarnCount = $currentWarnCount
CurrentErrorCount = $currentErrorCount
CurrentFatalCount = $currentFatalCount
PreviousTotalLines = $previousTotalLines
PreviousInfoCount = $previousInfoCount
PreviousWarnCount = $previousWarnCount
PreviousErrorCount = $previousErrorCount
PreviousFatalCount = $previousFatalCount
}
# Save error data to file for next step
$errorDataJson = $errorData | ConvertTo-Json -Depth 10
$errorDataPath = Join-Path $env:TEMP "butler-sos-error-data.json"
$errorDataJson | Out-File -FilePath $errorDataPath -Encoding UTF8
Write-Host "Error data saved to: $errorDataPath"
# Set GitHub Actions output
"errors_found=true" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
"error_count=$($allErrorEntries.Count)" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
} else {
Write-Host "✅ No errors found in any log files - system appears healthy" -ForegroundColor Green
"errors_found=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
"error_count=0" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
}
} catch {
Write-Host "❌ Failed to check log files: $($_.Exception.Message)" -ForegroundColor Red
Write-Host "Stack trace: $($_.Exception.StackTrace)" -ForegroundColor Red
Write-Host "::error::Log monitoring failed - $($_.Exception.Message)"
exit 1
# Save error data to file for email step
$errorDataJson = $errorData | ConvertTo-Json -Depth 10
$errorDataPath = Join-Path $env:TEMP "butler-sos-error-data.json"
$errorDataJson | Out-File -FilePath $errorDataPath -Encoding UTF8
Write-Host "Error data saved to: $errorDataPath"
"errors_found=true" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
"error_count=$errorCount" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
} else {
Write-Host "No errors found - system appears healthy" -ForegroundColor Green
"errors_found=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
"error_count=0" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
}
- name: Stop Butler SOS service if errors found
@@ -198,47 +186,43 @@ jobs:
shell: powershell
continue-on-error: true
run: |
Write-Host "🛑 Stopping Butler SOS service due to detected errors..."
Write-Host "Stopping Butler SOS service due to detected errors..."
$serviceName = $env:BUTLER_SOS_INSIDER_SERVICE_NAME
Write-Host "Service name: $serviceName"
try {
$service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
if ($service) {
if ($service.Status -eq 'Running') {
Write-Host "Stopping service '$serviceName'..."
$service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
if ($service) {
Write-Host "Service found"
if ($service.Status -eq 'Running') {
Write-Host "Service is running, stopping it..."
try {
Stop-Service -Name $serviceName -Force -ErrorAction Stop
Write-Host "Service stop command issued"
# Wait for service to stop
$timer = 0
$timeout = 30
do {
Start-Sleep -Seconds 1
$timer++
$service = Get-Service -Name $serviceName
} while ($service.Status -ne 'Stopped' -and $timer -lt $timeout)
# Simple wait
Start-Sleep -Seconds 5
$service = Get-Service -Name $serviceName
if ($service.Status -eq 'Stopped') {
Write-Host "Service stopped successfully"
Write-Host "Service stopped successfully"
"service_stopped=true" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
} else {
Write-Host "⚠️ Service did not stop within $timeout seconds. Status: $($service.Status)" -ForegroundColor Yellow
"service_stopped=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
Write-Host "Service may not have stopped completely"
"service_stopped=partial" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
}
} else {
Write-Host " Service was not running. Current status: $($service.Status)"
"service_stopped=not_running" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
} catch {
Write-Host "Failed to stop service"
"service_stopped=failed" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
}
} else {
Write-Host "⚠️ Service '$serviceName' not found" -ForegroundColor Yellow
"service_stopped=not_found" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
Write-Host "Service was not running"
"service_stopped=not_running" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
}
} catch {
Write-Host "❌ Failed to stop service: $($_.Exception.Message)" -ForegroundColor Red
Write-Host "::warning::Failed to stop Butler SOS service"
"service_stopped=failed" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
} else {
Write-Host "Service not found"
"service_stopped=not_found" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
}
- name: Send error alert email
@@ -247,7 +231,7 @@ jobs:
shell: powershell
continue-on-error: true
run: |
Write-Host "📧 Sending error alert email..."
Write-Host "Sending error alert email..."
try {
# Load error data from previous step
@@ -262,7 +246,7 @@ jobs:
# Check if email script exists on server
$emailScript = "D:\tools\scripts\insiders-build-monitor\Send-ErrorAlert.ps1"
if (-not (Test-Path $emailScript)) {
Write-Host "⚠️ Email script not found at: $emailScript" -ForegroundColor Yellow
Write-Host "WARNING: Email script not found at: $emailScript" -ForegroundColor Yellow
Write-Host "Cannot send email alert - script deployment needed"
Write-Host "::warning::Email script not found on server"
exit 0
@@ -297,31 +281,76 @@ jobs:
# Add credentials if available (PowerShell 5.1 compatible)
if ($env:SMTP_USERNAME) {
$emailParams.Username = $env:SMTP_USERNAME
Write-Host "SMTP Username configured: $($env:SMTP_USERNAME)"
if ($env:SMTP_PASSWORD) {
# Use plain text password for PS 5.1 compatibility
$emailParams.Password = $env:SMTP_PASSWORD
Write-Host "SMTP Password configured: [HIDDEN]"
} else {
Write-Host "WARNING: SMTP_PASSWORD not set"
}
} else {
Write-Host "WARNING: SMTP_USERNAME not set"
}
Write-Host "Calling Send-ErrorAlert.ps1..."
$result = & $emailScript @emailParams
# Debug email parameters
Write-Host "Email Parameters:"
Write-Host " SMTP Server: $($emailParams.SmtpServer)"
Write-Host " SMTP Port: $($emailParams.SmtpPort)"
Write-Host " From: $($emailParams.From)"
Write-Host " To: $($emailParams.To)"
Write-Host " Server Name: $($emailParams.ServerName)"
Write-Host " Service Name: $($emailParams.ServiceName)"
Write-Host " Use SSL: $($emailParams.UseSSL)"
Write-Host " Error Entries Count: $($emailParams.ErrorEntries.Count)"
if ($result) {
Write-Host "✅ Error alert email sent successfully!"
} else {
Write-Host "❌ Failed to send error alert email"
Write-Host "::warning::Email sending failed"
Write-Host "Calling Send-ErrorAlert.ps1..."
Write-Host "Email script path: $emailScript"
Write-Host "Template path: $($emailParams.TemplatePath)"
# Create log files for debugging
$logDir = "D:\tools\scripts\insiders-build-monitor\logs"
if (-not (Test-Path $logDir)) {
New-Item -Path $logDir -ItemType Directory -Force | Out-Null
}
$outputLogPath = Join-Path $logDir "email-output.log"
$errorLogPath = Join-Path $logDir "email-error.log"
# Execute script and capture all output
Write-Host "Executing email script with output capture..."
try {
$output = & $emailScript @emailParams 2>&1
$output | Out-File -FilePath $outputLogPath -Encoding UTF8
Write-Host "Email script completed. Output captured to: $outputLogPath"
Write-Host "--- Script Output ---"
Write-Host $output
Write-Host "--- End Script Output ---"
if ($LASTEXITCODE -eq 0) {
Write-Host "SUCCESS: Error alert email sent successfully!"
"email_sent=true" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
} else {
Write-Host "ERROR: Email script returned exit code: $LASTEXITCODE"
"email_sent=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
}
} catch {
$_.Exception.Message | Out-File -FilePath $errorLogPath -Encoding UTF8
Write-Host "ERROR: Exception while running email script: $($_.Exception.Message)"
Write-Host "Error details saved to: $errorLogPath"
"email_sent=error" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
}
# Clean up
Remove-Item -Path $errorDataPath -Force -ErrorAction SilentlyContinue
} catch {
Write-Host "❌ Failed to send error alert email: $($_.Exception.Message)" -ForegroundColor Red
Write-Host "::warning::Email alert failed - $($_.Exception.Message)"
$errorMsg = $_.Exception.Message
Write-Host ("ERROR: Failed to send error alert email: " + $errorMsg) -ForegroundColor Red
Write-Host ("::warning::Email alert failed - " + $errorMsg)
}
Write-Host "📊 Monitoring workflow completed"
Write-Host "Monitoring workflow completed"
Write-Host "Summary:"
Write-Host " - Errors found: ${{ steps.monitor-logs.outputs.errors_found }}"
Write-Host " - Error count: ${{ steps.monitor-logs.outputs.error_count }}"

View File

@@ -57,6 +57,20 @@ Generic script for sending service error alerts using customizable HTML template
- `TemplatePath` (required): Full path to the HTML email template file
- Various log statistics parameters for the email template
#### `Test-Email.ps1`
Convenient test script for quickly testing the email functionality. Automatically detects PowerShell version and uses the appropriate email script.
**Parameters:**
- `From` (required): Sender email address
- `To` (required): Recipient email address
- `Username` (required): SMTP authentication username
- `Password` (required): SMTP authentication password
- `SmtpServer` (optional): SMTP server (default: smtp.gmail.com)
- `SmtpPort` (optional): SMTP port (default: 587)
- `TestHtml` (optional): Send HTML formatted test email instead of plain text
### Email Template
#### Template Structure
@@ -198,16 +212,30 @@ Run the workflow manually using GitHub Actions:
Test the PowerShell scripts locally:
```powershell
# Test email sending (PowerShell 5.1)
.\Send-Email-PS51.ps1 -SmtpServer "smtp.example.com" -SmtpPort 587 -From "test@example.com" -To "admin@example.com" -Subject "Test" -Body "Test message" -UseSSL
# Quick test using the Test-Email.ps1 script (Plain Text)
.\Test-Email.ps1 -From "your-email@gmail.com" -To "recipient@example.com" -Username "your-email@gmail.com" -Password "your-app-password"
# Quick test using the Test-Email.ps1 script (HTML)
.\Test-Email.ps1 -From "your-email@gmail.com" -To "recipient@example.com" -Username "your-email@gmail.com" -Password "your-app-password" -TestHtml
# Test basic email sending (PowerShell 5.1)
.\Send-Email-PS51.ps1 -SmtpServer "smtp.gmail.com" -SmtpPort 587 -From "your-email@gmail.com" -To "recipient@example.com" -Subject "Test Email" -Body "This is a test message" -Username "your-email@gmail.com" -Password "your-app-password" -UseSSL
# Test HTML email sending (PowerShell 5.1)
.\Send-Email-PS51.ps1 -SmtpServer "smtp.gmail.com" -SmtpPort 587 -From "your-email@gmail.com" -To "recipient@example.com" -Subject "HTML Test Email" -Body "<h1>Test HTML Email</h1><p>This is a <strong>test HTML message</strong> with <em>formatting</em>.</p>" -Username "your-email@gmail.com" -Password "your-app-password" -UseSSL -IsBodyHtml
# Test email sending (PowerShell 7+)
.\Send-Email-Modern.ps1 -SmtpServer "smtp.example.com" -SmtpPort 587 -From "test@example.com" -To "admin@example.com" -Subject "Test" -Body "Test message" -UseSSL
.\Send-Email-Modern.ps1 -SmtpServer "smtp.gmail.com" -SmtpPort 587 -From "your-email@gmail.com" -To "recipient@example.com" -Subject "Test Email" -Body "This is a test message" -Username "your-email@gmail.com" -Password (ConvertTo-SecureString "your-app-password" -AsPlainText -Force) -UseSSL
# Test error alert (requires error data and template path)
.\Send-ErrorAlert.ps1 -SmtpServer "smtp.example.com" -SmtpPort 587 -From "test@example.com" -To "admin@example.com" -ServerName "Test Server" -ServiceName "Test Service" -ErrorEntries @("Test error 1", "Test error 2") -TemplatePath ".\butler-sos-email-template-error-alert.html" -UseSSL
# Test complete error alert system (requires error data and template path)
.\Send-ErrorAlert.ps1 -SmtpServer "smtp.gmail.com" -SmtpPort 587 -From "your-email@gmail.com" -To "recipient@example.com" -ServerName "Test Server" -ServiceName "Test Service" -ErrorEntries @("Test error 1", "Test error 2") -TemplatePath ".\butler-sos-email-template-error-alert.html" -Username "your-email@gmail.com" -Password "your-app-password" -UseSSL
# Test encoding fix with special template
.\Send-ErrorAlert.ps1 -SmtpServer "smtp.gmail.com" -SmtpPort 587 -From "your-email@gmail.com" -To "recipient@example.com" -ServerName "Test Server" -ServiceName "Test Service" -ErrorEntries @("ERROR: Connection failed", "ERROR: Invalid configuration") -TemplatePath ".\test-encoding-template.html" -Username "your-email@gmail.com" -Password "your-app-password" -UseSSL
```
**Note**: Replace `your-email@gmail.com` with your actual Gmail address and `your-app-password` with your Gmail App Password. For Gmail, always use an App Password, not your regular Gmail password.
## Troubleshooting
### Common Issues
@@ -216,6 +244,7 @@ Test the PowerShell scripts locally:
2. **SMTP authentication fails**: Check SMTP credentials in GitHub secrets
3. **Service stop fails**: Verify service name in repository variables
4. **Log files not found**: Check deployment path and log directory structure
5. **Mangled characters in emails**: HTML template encoding issues - see Email Encoding section below
### Workflow Logs
@@ -226,9 +255,33 @@ All actions are logged in GitHub Actions workflow logs with detailed output incl
- Service stop attempts
- Email sending results
**Email Script Debugging Logs**: When email sending fails, detailed logs are saved to `D:\tools\scripts\insiders-build-monitor\logs\`:
- `email-output.log` - Complete script output and parameters
- `email-error.log` - Exception details and stack traces
These log files are overwritten on each run to help with debugging email delivery issues.
### Email Delivery Issues
- Check SMTP server settings and credentials
- Verify firewall allows outbound SMTP connections
- Check email server logs for delivery status
- Test SMTP settings with a simple email first
### Email Encoding Issues
If emails display garbled characters or corrupted emojis (like "âš¡" instead of "⚡"):
1. **Use HTML entities instead of Unicode emojis** in templates:
- Instead of `⚡` use `&#9889;`
- Instead of `🚨` use `&#128680;`
- Instead of `⚠️` use `&#9888;&#65039;`
2. **Test with the encoding test template**:
```powershell
.\Send-ErrorAlert.ps1 -SmtpServer "smtp.gmail.com" -SmtpPort 587 -From "your@gmail.com" -To "recipient@example.com" -ServerName "Test" -ServiceName "Test" -ErrorEntries @("Test error") -TemplatePath ".\test-encoding-template.html" -Username "your@gmail.com" -Password "app-password" -UseSSL
```
3. **Ensure UTF-8 encoding**: The scripts now include explicit UTF-8 encoding for both templates and email content

View File

@@ -47,6 +47,22 @@ param(
[int]$TimeoutSeconds = 30
)
Write-Host "=== Send-Email-Modern.ps1 Starting ==="
Write-Host "PowerShell Version: $($PSVersionTable.PSVersion)"
Write-Host "SMTP Server: $SmtpServer"
Write-Host "SMTP Port: $SmtpPort"
Write-Host "From: $From"
Write-Host "To: $To"
Write-Host "Subject: $Subject"
Write-Host "Body Length: $($Body.Length)"
Write-Host "Has Username: $([bool]$Username)"
Write-Host "Has Password: $([bool]$Password)"
Write-Host "Has Credential: $([bool]$Credential)"
Write-Host "Use SSL: $UseSSL"
Write-Host "Is Body HTML: $IsBodyHtml"
Write-Host "Priority: $Priority"
Write-Host "Timeout: $TimeoutSeconds seconds"
# Modern PowerShell function using Send-MailMessage with enhanced error handling
function Send-EmailModern {
param(
@@ -165,6 +181,10 @@ try {
$mailMessage.Body = $Body
$mailMessage.IsBodyHtml = $IsBodyHtml.IsPresent
# Set UTF-8 encoding for proper character support
$mailMessage.BodyEncoding = [System.Text.Encoding]::UTF8
$mailMessage.SubjectEncoding = [System.Text.Encoding]::UTF8
# Set priority
$mailMessage.Priority = switch ($Priority.ToLower()) {
"high" { [System.Net.Mail.MailPriority]::High }

View File

@@ -38,6 +38,20 @@ param(
[string]$Priority = "Normal"
)
Write-Host "=== Send-Email-PS51.ps1 Starting ==="
Write-Host "PowerShell Version: $($PSVersionTable.PSVersion)"
Write-Host "SMTP Server: $SmtpServer"
Write-Host "SMTP Port: $SmtpPort"
Write-Host "From: $From"
Write-Host "To: $To"
Write-Host "Subject: $Subject"
Write-Host "Body Length: $($Body.Length)"
Write-Host "Has Username: $([bool]$Username)"
Write-Host "Has Password: $([bool]$Password)"
Write-Host "Use SSL: $UseSSL"
Write-Host "Is Body HTML: $IsBodyHtml"
Write-Host "Priority: $Priority"
# PowerShell 5.1 compatible function to convert plain text password to SecureString
function ConvertTo-SecureStringPS51 {
param([string]$PlainTextPassword)
@@ -83,6 +97,10 @@ try {
$mailMessage.Body = $Body
$mailMessage.IsBodyHtml = $IsBodyHtml.IsPresent
# Set UTF-8 encoding for proper character support
$mailMessage.BodyEncoding = [System.Text.Encoding]::UTF8
$mailMessage.SubjectEncoding = [System.Text.Encoding]::UTF8
# Set priority
switch ($Priority.ToLower()) {
"high" { $mailMessage.Priority = [System.Net.Mail.MailPriority]::High }

View File

@@ -77,6 +77,15 @@ param(
)
try {
Write-Host "=== Send-ErrorAlert.ps1 Starting ==="
Write-Host "PowerShell Version: $($PSVersionTable.PSVersion)"
Write-Host "SMTP Server: $SmtpServer"
Write-Host "SMTP Port: $SmtpPort"
Write-Host "From: $From"
Write-Host "To: $To"
Write-Host "Template Path: $TemplatePath"
Write-Host "Error Entries Count: $($ErrorEntries.Count)"
Write-Host "Preparing service error alert email..."
# Read email template
@@ -85,7 +94,7 @@ try {
}
Write-Host "Loading email template from: $TemplatePath"
$template = Get-Content -Path $TemplatePath -Raw
$template = Get-Content -Path $TemplatePath -Raw -Encoding UTF8
# Prepare error entries HTML
$errorEntriesHtml = ""
@@ -96,8 +105,11 @@ try {
$errorEntriesHtml = "<h4>Found $errorCount error(s):</h4>`n"
foreach ($entry in $ErrorEntries) {
$errorEntriesHtml += "<div class='log-entry error-entry'>$([System.Web.HttpUtility]::HtmlEncode($entry))</div>`n"
# Simple HTML encoding without System.Web dependency
$encodedEntry = $entry -replace "&", "&amp;" -replace "<", "&lt;" -replace ">", "&gt;" -replace '"', "&quot;"
$errorEntriesHtml += "<div class='log-entry error-entry'>$encodedEntry</div>`n"
}
Write-Host "Processed $errorCount error entries for email"
}
else {
$errorEntriesHtml = "<p>No specific error entries to display.</p>"
@@ -129,7 +141,7 @@ try {
$emailBody = $emailBody -replace "{{GENERATION_TIME}}", $generationTime
# Create subject
$subject = "🚨 Service Error Alert - $errorCount error(s) detected on $ServerName"
$subject = "ALERT: Service Error Alert - $errorCount error(s) detected on $ServerName"
# Call the appropriate Send-Email script based on PowerShell version
$scriptDir = Split-Path $TemplatePath -Parent
@@ -150,6 +162,15 @@ try {
}
Write-Host "Calling email script: $sendEmailScript"
Write-Host "Email parameters:"
Write-Host " Subject: $subject"
Write-Host " Body length: $($emailBody.Length) characters"
Write-Host " IsBodyHtml: True"
Write-Host " Priority: High"
if ($Username) { Write-Host " Using authentication with username: $Username" }
if ($UseSSL) { Write-Host " Using SSL" }
Write-Host "Executing email script..."
$params = @{
SmtpServer = $SmtpServer
@@ -167,6 +188,7 @@ try {
if ($UseSSL) { $params.UseSSL = $true }
$result = & $sendEmailScript @params
Write-Host "Email script returned: $result"
if ($result) {
Write-Host "✅ Service error alert sent successfully!"

View File

@@ -0,0 +1,143 @@
# Test-Email.ps1
# Quick test script for the email sending functionality
# Automatically detects PowerShell version and uses appropriate email script
param(
[Parameter(Mandatory = $true)]
[string]$From,
[Parameter(Mandatory = $true)]
[string]$To,
[Parameter(Mandatory = $true)]
[string]$Username,
[Parameter(Mandatory = $true)]
[string]$Password,
[Parameter(Mandatory = $false)]
[string]$SmtpServer = "smtp.gmail.com",
[Parameter(Mandatory = $false)]
[int]$SmtpPort = 587,
[Parameter(Mandatory = $false)]
[switch]$TestHtml
)
Write-Host "=== Email Test Script ==="
Write-Host "PowerShell Version: $($PSVersionTable.PSVersion)"
Write-Host "Testing email functionality..."
$scriptDir = Split-Path $MyInvocation.MyCommand.Path -Parent
# Determine which email script to use
$psVersion = $PSVersionTable.PSVersion.Major
if ($psVersion -ge 7) {
$emailScript = Join-Path $scriptDir "Send-Email-Modern.ps1"
Write-Host "Using modern PowerShell email script for PS $psVersion"
}
else {
$emailScript = Join-Path $scriptDir "Send-Email-PS51.ps1"
Write-Host "Using PowerShell 5.1 compatible email script for PS $psVersion"
}
if (-not (Test-Path $emailScript)) {
Write-Host "❌ Email script not found: $emailScript" -ForegroundColor Red
exit 1
}
# Prepare test content
if ($TestHtml) {
$subject = "HTML Email Test from Butler SOS Monitor"
$body = @"
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
.header { color: #2c3e50; }
.success { color: #27ae60; background: #d5f4e6; padding: 10px; border-radius: 5px; }
.info { color: #3498db; }
</style>
</head>
<body>
<h1 class="header">🧪 HTML Email Test</h1>
<div class="success">
<h2> Success!</h2>
<p>If you're seeing this formatted HTML email, the email system is working correctly!</p>
</div>
<h3 class="info">Test Details:</h3>
<ul>
<li><strong>PowerShell Version:</strong> $($PSVersionTable.PSVersion)</li>
<li><strong>Script Used:</strong> $(Split-Path $emailScript -Leaf)</li>
<li><strong>SMTP Server:</strong> $SmtpServer</li>
<li><strong>SMTP Port:</strong> $SmtpPort</li>
<li><strong>Test Time:</strong> $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss UTC')</li>
</ul>
<p><em>This is a test email from the Butler SOS monitoring system.</em></p>
</body>
</html>
"@
$isBodyHtml = $true
}
else {
$subject = "Plain Text Email Test from Butler SOS Monitor"
$body = @"
Plain Text Email Test
Success!
If you're receiving this email, the email system is working correctly!
Test Details:
- PowerShell Version: $($PSVersionTable.PSVersion)
- Script Used: $(Split-Path $emailScript -Leaf)
- SMTP Server: $SmtpServer
- SMTP Port: $SmtpPort
- Test Time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss UTC')
This is a test email from the Butler SOS monitoring system.
"@
$isBodyHtml = $false
}
# Prepare parameters
$params = @{
SmtpServer = $SmtpServer
SmtpPort = $SmtpPort
From = $From
To = $To
Subject = $subject
Body = $body
Username = $Username
Password = $Password
UseSSL = $true
Priority = "High"
}
if ($isBodyHtml) {
$params.IsBodyHtml = $true
}
Write-Host "Sending test email..."
Write-Host " From: $From"
Write-Host " To: $To"
Write-Host " Type: $(if ($TestHtml) { 'HTML' } else { 'Plain Text' })"
try {
$result = & $emailScript @params
if ($result) {
Write-Host "✅ Test email sent successfully!" -ForegroundColor Green
Write-Host "Check your inbox at: $To"
}
else {
Write-Host "❌ Test email failed to send" -ForegroundColor Red
}
}
catch {
Write-Host "❌ Exception occurred: $($_.Exception.Message)" -ForegroundColor Red
}
Write-Host "=== Test Complete ==="

View File

@@ -116,13 +116,13 @@
<body>
<div class="container">
<div class="header">
<div class="alert-icon">⚠️</div>
<div class="alert-icon">&#9888;&#65039;</div>
<h1>Butler SOS Error Alert</h1>
<p>Errors detected in log files</p>
</div>
<div class="summary">
<h3>🔍 Summary</h3>
<h3>&#128269; Summary</h3>
<p><strong>{{ERROR_COUNT}}</strong> error(s) found in Butler SOS log files</p>
<p><strong>Detection Time:</strong> {{DETECTION_TIME}}</p>
<p><strong>Server:</strong> {{SERVER_NAME}}</p>
@@ -179,12 +179,12 @@
</div>
<div class="details">
<h3>🚨 Error Details</h3>
<h3>&#128680; Error Details</h3>
{{ERROR_ENTRIES}}
</div>
<div class="info-box">
<h4> Next Steps</h4>
<h4>&#8505;&#65039; Next Steps</h4>
<ol>
<li>Review the error messages above to identify the root cause</li>
<li>Check system connectivity (InfluxDB, network, etc.)</li>

View File

@@ -0,0 +1,59 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Encoding Test</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
background-color: #dc3545;
color: white;
padding: 20px;
text-align: center;
border-radius: 8px;
}
.content {
padding: 20px;
background-color: #f8f9fa;
margin: 10px 0;
border-radius: 4px;
}
</style>
</head>
<body>
<div class="header">
<h1>&#9888;&#65039; Encoding Test</h1>
<p>Testing HTML entity encoding</p>
</div>
<div class="content">
<h3>&#128680; Error Test</h3>
<p>This email tests proper HTML entity encoding instead of Unicode emojis.</p>
<p><strong>HTML Entities Used:</strong></p>
<ul>
<li>&#9888;&#65039; Warning Sign (&#38;&#35;9888;&#38;&#35;65039;)</li>
<li>&#128680; Siren (&#38;&#35;128680;)</li>
<li>&#128269; Magnifying Glass (&#38;&#35;128269;)</li>
<li>&#8505;&#65039; Information (&#38;&#35;8505;&#38;&#35;65039;)</li>
</ul>
<p><strong>Server:</strong> {{SERVER_NAME}}</p>
<p><strong>Service:</strong> {{SERVICE_NAME}}</p>
<p><strong>Error Count:</strong> {{ERROR_COUNT}}</p>
<div style="background-color: #fff3cd; border: 1px solid #ffeaa7; padding: 10px; border-radius: 4px;">
{{ERROR_ENTRIES}}
</div>
</div>
<p><em>Generated at: {{GENERATION_TIME}}</em></p>
</body>
</html>

600
package-lock.json generated

File diff suppressed because it is too large Load Diff