Powershell ja sündmuste logid

Aeg-ajalt on vaja otsida sündmuste logidest teatud kindlaid sündmusi.  Ja vahel oleks hea ka ise sündmusi logida.

Vaatame kõigepealt, kuidas sündmusi leida.  Selleks on Powershellis kaks käsku: Get-EventLog ja Get-WinEvent.  Esimest neist tuleks kasutada ainult juhul, kui on vaja otsida sündmusi Windows Server 2003 logidest.  Ja Server 2003 tugi lõpeb sel aastal ära.

Kõigi uuemate OS-ide puhul tuleks kasutada teist käsku.  See oskab otsida sündmusi ka Event Tracing for Windows logidest.  Ja neid logisid on tänapäeval palju.  Alustamegi siis sellest, et otsime meid huvitava logi üles:

Get-WinEvent -ListLog *
Get-WinEvent -ListLog Application

Nüüd teades logi nime, saame sealt otsida sündmusi.  Piirame selle otsimise mingi arvuga, et mitte liiga kaua oodata:

Get-WinEvent -LogName Application -MaxEvents 10
Get-WinEvent -LogName "Windows Powershell" -MaxEvents 10

# vanemad sündmused enne
Get-WinEvent -LogName Application -MaxEvents 10 -Oldest

Tavaliselt ei huvita meid kõik logisse kirjutatud sündmused, vaid ikka spetsiifilised.  Lisaks võivad huvipakkuvad sündmused olla kirjutatud ka mitmesse erinevasse logisse.  Seetõttu saab sündmusi otsida ka mitte logide, vaid hoopis sündmuse allika järgi.  Kõigepealt leiame olemasolevate sündmuste küljest allikad:

(Get-WinEvent -ListLog Application).ProviderNames

Get-WinEvent -ListProvider *
Get-WinEvent -ListProvider *PowerShell
Get-WinEvent -ListProvider Outlook

Nüüd teades allikat, on lihtne saada kätte sündmused:

Get-WinEvent -ProviderName Outlook -MaxEvents 10
Get-WinEvent -ProviderName Microsoft-Windows-PowerShell -MaxEvents 10

Teades täpsemalt mida otsime, saame ka sündmusi täpsemalt filtreerida.  Näiteks võime me vaadata ainult viimase 24 tunni jooksul toimunud sündmusi:

$eile = (Get-Date).AddDays(-1)
Get-WinEvent -FilterHashtable @{
  LogName = "Application"
  StartTime = $eile
}

Või siis otsime me kindlat sündmust, mille puhul on teada ka sündmuse number (EventID):

Get-WinEvent -MaxEvents 10 -FilterHashtable @{
  ProviderName = "Outlook"
  ID = 32
}

Get-WinEvent -FilterHashtable @{
  Logname="Application"
  ProviderName="Application Error"
  Data="iexplore.exe"
}

Kui me oleme eelnevalt rakenduses Event Viewer loonud sündmuste filtri, siis selle saab ette anda ka Powershellile.  Vaja see vaid salvestada XML failiks ja sealt võtta välja element QueryList.  Selle saab ka vaate filtrit muutes vahelehelt XML.

Get-WinEvent -MaxEvents 10 -FilterXml @"
<QueryList>
  <Query Id="0" Path="Application">
    <Select Path="Application">*[System[Provider[@Name='Application Error'] and (Level=2) and (EventID=1000)]]</Select>
  </Query>
</QueryList>
"@

Get-WinEvent -FilterXml ([xml](Get-Content .\filter.xml)) -MaxEvents 10

XML-dokumendiga tasub jännata ainult keerulisemate päringute puhul, mida varem näidatud viisidel ei saa kokku panna (näiteks, kui on vaja leida mitmele erinevale EventID-le vastavaid sündmusi vms.).

Logidesse kirjutamisega on asi natuke keerulisem.  Nimelt tuleks oma skripti jaoks luua uus logi või siis vähemalt uus sündmuste allikas.  Muidu on pärast raske oma skripti sündmusi üles leida.  Ja nii uute logide kui ka uute allikate loomiseks on vaja süsteemiülema õigusi:

$myProvider = "minuskript"

#Requires -RunAsAdministrator
New-EventLog -LogName Application -Source $myProvider
New-EventLog -Source TestApp -LogName TestLog

try {
  Get-WinEvent -ListProvider $myProvider | Out-Null
} catch {
  Start-Process -Verb runas powershell.exe -ArgumentList "New-EventLog -LogName Application -Source $myProvider"
}

Ülaltoodud tegevus tuleb sooritada ühekordselt.  Pärast seda on uus logi/allikas masinas defineeritud ja skript saab rahulikult kirjutada sündmusi:

$params = @{
  LogName = "Application"
  Source = $myProvider
  EventId = 1
  Message = "Minu kohandatud sündmus"
}
Write-EventLog @params

Get-WinEvent -ProviderName $myProvider
Advertisements

Vanade (logi)failide töötlemine serveris

Serverites juhtub ikka, et erinevad rakendused peavad logisid.  Ja logidega kipub ikka see juhtuma, et need muudkui kogunevad ja keegi ei kustuta neid.  Ning siis äkki avastame, et serveris on kettaruum kuhugile ära kadunud.

Sedalaadi probleemide lahendamiseks on hea kasutada Windowsi käsurea utiliiti forfiles.  Windows Server 2003-s on see juba Windowsiga kaasas, varasemates versioonides seda veel ei olnud.  Windows NT4 ja  2000 Resource Kit sisaldavad seda utiliiti ka, ent tasuta kättesaadavate utiliitide hulgas seda kahjuks ei ole.

Tegelikult on forfiles mõeldud failide otsimiseks ning iga leitud faili peal sama käsu käivitamiseks.  Aga vanade logide puhul me just seda tahamegi.

Mõned näited ka.  Kui me tahame kustutada kõik vanemad kui 90 päeva failid kaustast d:\logs ja alamkaustadest, siis seda teeb järgnev käsk:

forfiles /p "d:\LOGS" /s /d -90 /c "cmd /c if @isdir==FALSE del /f /q @file"

Kui Sa aga tahad samast kaustast (ilma alamkaustadeta) ainult *.log failid kustutamise asemel kokku pakkida (ma kasutan selleks utiliiti 7-zip), siis seda teeb järgmine käsk:

forfiles /p "d:\LOGS" /m *.log /d -90 /c "cmd /c if @isdir==FALSE 7z a @fname.zip @file && del /f /q @file"

Selle käsu ainsaks probleemiks on see, et iga fail pakitakse samanimeliseks arhiiviks.  Kui on vaja failid kuu kaupa kokku pakkida, siis tuleks selleks eraldi skript kirjutada, mis arhiivi failinime oskaks välja arvutada.

Teeme samad näited läbi ka PowerShellis. Esimene näide:

Get-ChildItem -Path d:\logs -Recurse |
    Where-Object {-not $_.PSIsContainer} |
    Where-Object {((get-date) - $_.lastwritetime).days -gt 90} |
    Remove-Item

#Requires -Version 3
Get-ChildItem -Path d:\logs -Recurse -File |
    Where-Object {((get-date) - $_.lastwritetime).days -gt 90} |
    Remove-Item

Ja teine näide:

#Requires -Version 3
Get-ChildItem -Path d:\logs -Recurse -File |
    Where-Object {([datetime]::Now - $_.lastwritetime).days -gt 90} |
    Foreach-Object {
        & 7z a $("{0:yyyy.MM}.zip" -f [datetime]::Now.AddDays(-90)) $_.FullName
        Remove-Item $_
    }

Positiivne asja juures on nüüd see, et failid pakitakse kokku ühte arhiivi.  Arhiivi nimi sisaldab aastat ja kuud tänasest päevast 90 päeva tagasi.  Alternatiivina võiks failinime tuletada tänasest päevast 3 kuud tagasi:

"{0:yyyy.MM}.zip" -f (get-date).AddMonths(-3)