Wykrywanie błędów w logach

W przypadku gdy program nie ma funkcjonalności zgłaszania błędów lub gdy chcemy wykonać jakąś czynność po wystąpieniu błędu zapisanego w plik log możemy wykorzystać powershella.
Ogólny niniejszego poradnika jest tak aby analizować plik log w poszukiwaniu konkretnej wartości np. „error”.
Założenia: poszukujemy danego ciągu w pliku log co kilka/kilkanaście minut i w przypadku jego wystąpienia wykonujemy jakąś czynność. Jednocześnie zliczamy ilość wykrytych ciągów znaków i tą ilość zapisujemy w pliku zewnętrznym. Każdego kolejnego dnia zerujemy ilość wystąpienia błędów z uwagi na analizowanie nowego pliku log.
W pierwszej kolejności musimy ustalić jaką nazwę ma plik logów. Jeżeli każdego dnia bieżący plik log ma taką samą nazwę a do nazwy archiwalnych plików logów dodawana jest np. data ustawiamy zmienną na sztywno np.

$filepathdata = "C:\program\Logs\logs.log.txt"

W przypadku gdy bieżący plik log w swojej nazwie zawiera datę np. w postaci yyyy-MM-dd należy tak przygotować zmienną by zawsze zawierała bieżącą datę. Do tego celu możemy wykorzystać polecenie get-date wraz z odpowiednim formatowaniem daty np.

$filepathdata = "C:\program\Logs\logs_"+(get-date -format yyyy-MM-dd)+".txt"

w tym przypadku docelowa nazwa pliku przykładowo będzie wyglądać tak: log_2024-10-20.txt.

Na potrzeby naszego skryptu tworzymy plik licznik.txt. Do pliku będziemy zapisywać ilość wystąpienia błędów w każdym kolejnym dniu. Wartość tą będziemy resetować każdego dnia. W tym celu posłużymy się datą modyfikacji pliku którą porównamy z bieżącą datą. Wartość modyfikacji pliku oraz funkcja get-date zwraca nam datę i godzinę. Do porównania musimy wykorzystać tylko datę. Datę modyfikacji pliku odczytamy wykorzystując funkcję Get-Item wraz z właściwością LastWriteTime

$modyfikacjapliku = ((Get-Item „C:\Scripts\licznik.txt”).LastWriteTime).date

Cały fragment skryptu może wyglądać tak:

$dzis=(get-date).date
#jeżeli data modyfikacji pliku licznika jest z wczoraj to wyzeruj licznik w pliku
if ($dzis -gt $modyfikacjapliku) {
#Write-Host "modyfikacja pliku jest wczoraj"
$licznik=0
$licznik | Set-Content -Path "C:\Scripts\licznik.txt"
}
else { #Write-Host "Data taka sama"
}

Na potrzeby kolejnych wykonań skryptu w danym dniu odczytujemy wartość wystąpień zapisanych w pliku licznik.txt. Czynność tą wykonujemy po to aby po wystąpieniu błędy wykonać jakąś czynność i przy następnym sprawdzaniu nie wykonywać czynności dopóki błąd nie pojawi się ponownie (ilość błędów zwiększy się)

#odczyt zmiennej z pliku
$licznik = [int](Get-Content -Path "C:\Scripts\licznik.txt" )

Poszukiwany ciąg znaków zapiszemy sobie w zmiennej

#wyszukiwany ciąg znaków błędu
$phrase = "error"

W zmiennej $phrase możemy zapisać dowolny ciąg znaków. Jaki ciąg znaków należy wpisać musimy ustalić po analizie plików log – poszukujemy ciąg znaków wskazujących błąd po wystąpieniu którego którym chcemy wykonać jakieś polecenia.
Wyszukujemy poszukiwany ciąg znaków (zmienna $phrase) w pliku określonym zmienną $filepathdata, zliczamy ilość wystąpień i tą liczbę zapisujemy w zmiennej $count

# Poszukaj wartości w pliku i policz wystąpienia
$count = (Get-Content $filepathdata | Select-String -Pattern $phrase).Count

W kolejnym kroku porównujemy ilość dotychczasowych wystąpień błędu z liczbą odczytaną z pliku licznik.txt. W przypadku gdy błędów jest więcej (pojawiły się nowe błędy) wykonujemy jakieś polecenia i nową wartość wystąpienia błędów zapisujemy do pliku, a w przypadku gdy ilość błędów jest taka sam nie robimy nic.

#sprawdź czy pojawił się nowy error
If ($count -gt $licznik) {
#wykonaj polecenie
write-host -f red 'Jest więcej'

#było więcej więc zapisz ilość do pliku
$licznik = $count
$licznik | Set-Content -Path "C:\Scripts\licznik.txt"
}
else {
#jest tyle samo nie rób nic
write-host -f green "jest tyle samo"}

W moim przypadku po wystąpieniu błędu wymagany był restart aplikacji i strony na serwerze IIS. Najprościej byłoby zresetować cały serwer IIS ale jeśli na danym serwerze są także inne strony nie ma potrzeby restartowania całego serwera IIS.
Aby zrestartować dana aplikację możemy skorzystać z polecenia:

& C:\Windows\System32\inetsrv\appcmd stop apppool /apppool.name:Nasza_app
Start-Sleep -Seconds 5
& C:\Windows\System32\inetsrv\appcmd start apppool /apppool.name:Nasza_app

Znak & na początku linii jest potrzeby aby polecenie zostało wykonane ze skryptu powershell. Po zatrzymaniu app wstrzymujemy wykonywanie skryptu na 5s i po tym czasie uruchamimy app
Do zrestartowania danej strony na serwerze IIS należy wydać polecenie:

& C:\Windows\System32\inetsrv>appcmd stop site /site.name:"NazwaNaszejStrony"
Start-Sleep -Seconds 5
& C:\Windows\System32\inetsrv>appcmd stop site /site.name:"NazwaNaszejStrony"

Po wystąpieniu błędu możemy z poziomu powershell wysłać email

Send-MailMessage -From [email protected] -To [email protected] -Subject 'Wykonano restart App' -Body 'Był błąd i został uruchomiony restart ze skryptu' -SmtpServer 'IP_serwera_SMTP' -port 587 -Encoding UTF8

Całość skryptu będzie wyglądać tak:

# wybierz plik którą chcesz przeszukiwać
$filepathdata = "C:\program\Logs\logs_"+(get-date -format yyyy-MM-dd)+".txt"
#pobierz datę modyfikacji pliku w ktorym zapisywany jest licznik wystąpień błędów w danym dniu
$modyfikacjapliku = ((Get-Item "C:\Scripts\licznik.txt").LastWriteTime).date
#$dzis=(get-date).date
#jeżeli data modyfikacji pliku licznika jest z wczoraj to wyzeruj licznik w pliku
if ($dzis -gt $modyfikacjapliku) {
#Write-Host "modyfikacja pliku jest wczoraj"
$licznik=0
$licznik | Set-Content -Path "C:\Scripts\licznik.txt"
}
else { #Write-Host "Data taka sama"
}
#odczyt zmiennej z pliku
$licznik = [int](Get-Content -Path "C:\Scripts\licznik.txt" )
#wyszukiwany ciąg znaków błędu
$phrase = "error"
# Poszukaj wartości w pliku i policz wystąpienia
$count = (Get-Content $filepathdata | Select-String -Pattern $phrase).Count
#sprawdź czy pojawił się nowy error
If ($count -gt $licznik) {
#wykonaj polecenie
Send-MailMessage -From [email protected] -To [email protected] -Subject 'Wykonano restart App' -Body 'Był błąd i został uruchomiony restart ze skryptu' -SmtpServer 'IP_serwera_SMTP' -port 587 -Encoding UTF8
write-host -f red 'Jest więcej'
#było więcej więc zapisz ilość do pliku
$licznik = $count
$licznik | Set-Content -Path "C:\Scripts\licznik.txt"
}
else {
#jest tyle samo nie rób nic
write-host -f green "jest tyle samo"}

Tak przygotowany skrypt dodajemy do wykonania w harmonogramie zadań np. co kilka minut.