mirror of
https://github.com/ptarmiganlabs/butler-sos.git
synced 2025-12-19 17:58:18 -05:00
Merge branch 'master' into release-please--branches--master--components--butler-sos
This commit is contained in:
381
.github/workflows/butler-sos-log-monitor.yaml
vendored
381
.github/workflows/butler-sos-log-monitor.yaml
vendored
@@ -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 }}"
|
||||
|
||||
@@ -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 `⚡`
|
||||
- Instead of `🚨` use `🚨`
|
||||
- Instead of `⚠️` use `⚠️`
|
||||
|
||||
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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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 "&", "&" -replace "<", "<" -replace ">", ">" -replace '"', """
|
||||
$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!"
|
||||
|
||||
143
docs/scripts/insiders-build-monitor/Test-Email.ps1
Normal file
143
docs/scripts/insiders-build-monitor/Test-Email.ps1
Normal 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 ==="
|
||||
@@ -116,13 +116,13 @@
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<div class="alert-icon">⚠️</div>
|
||||
<div class="alert-icon">⚠️</div>
|
||||
<h1>Butler SOS Error Alert</h1>
|
||||
<p>Errors detected in log files</p>
|
||||
</div>
|
||||
|
||||
<div class="summary">
|
||||
<h3>🔍 Summary</h3>
|
||||
<h3>🔍 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>🚨 Error Details</h3>
|
||||
{{ERROR_ENTRIES}}
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<h4>ℹ️ Next Steps</h4>
|
||||
<h4>ℹ️ Next Steps</h4>
|
||||
<ol>
|
||||
<li>Review the error messages above to identify the root cause</li>
|
||||
<li>Check system connectivity (InfluxDB, network, etc.)</li>
|
||||
|
||||
@@ -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>⚠️ Encoding Test</h1>
|
||||
<p>Testing HTML entity encoding</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<h3>🚨 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>⚠️ Warning Sign (&#9888;&#65039;)</li>
|
||||
<li>🚨 Siren (&#128680;)</li>
|
||||
<li>🔍 Magnifying Glass (&#128269;)</li>
|
||||
<li>ℹ️ Information (&#8505;&#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
600
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user