Pratik PowerShell: Çıkış ve Günlük Kaydı


PowerShell Sorunları için Etkili Günlük Kaydı

Pratik PowerShell serisinin bu bölümünde önemli bir konuya değinmek istiyorum: çıktı ve günlük kaydı. Önceki yazılarımda kod akışını yönetme, hata işleme gibi konulara değinmiştim. Eylemlerin başarısını (veya başarısızlığını) öngörmek için önlemler almanın yanı sıra, sonuçları kodu çalıştıran kişiye bildirip bildirmemeyi ve onları işlerin nasıl yürüdüğü konusunda bilgilendirmek isteyip istemediğinizi ve nasıl bilgilendirmek istediğinizi de düşünmelisiniz. ilerliyorlar.

Ayrıca, verileri işleyen veya yapılandırma değişiklikleri gerçekleştiren bir komut dosyanız varsa bu eylemleri kaydetmek isteyebilirsiniz. Sonuçta, konsola gönderilen çıktı geçicidir ve başsız bir Azure İşlevi veya Runbook'un parçası olarak betikler çalıştırılırken bile görülmeyebilir. Çıktıyı görmek istemeseniz bile, belki bir müşteri bu tür bir raporla ilgilenebilir.

Burada çıktı almak ve komut dosyalarınıza özel günlük kaydı eklemek için en yaygın seçeneklerden bazılarını tartışacağım. Oturum etkileşimini bir dosyaya kaydetmek için kullanılan iki cmdlet olan Start-Transcript ve Stop-Transcript'den kısaca bahsedeceğim. Müşteri ortamlarında yaptığım her şeyin kaydını tutmak için ara sıra bu cmdlet'leri kullanıyorum. Basit olmasına rağmen, daha fazla işlenmesi zor olan yapılandırılmamış bir çıktıdır.

Doğru Çıkış Türü

PowerShell'in ilk günlerinden bu yana topluluk, Write-Host gibi cmdlet'ler üzerinde Write-Output cmdlet'inin kullanımını teşvik etti. Neden? Buradaki fikir, çıktının Write-Output aracılığıyla oluşturulan PowerShell nesneleri olması durumunda, bu çıktının diğer cmdlet'ler kullanılarak daha fazla işlenebilmesidir. Örneğin, çıktıyı işlemek ve depolamak için Where-Object veya Export-Csv'yi kullanabilirsiniz. Write-Host'u kullanmak yalnızca çıktıyı ana bilgisayarın "konsoluna" yazar, böylece bu verileri daha fazla işlemeye yönelik seçenekler sınırlanır. Bu fark, nesne tabanlı PowerShell'i, Linux sistemlerinde yaygın olarak bulunanlar gibi metin tabanlı kabuk ortamlarından farklı kılan şeydir. Tony, Practical 365 ile ilgili daha önceki bir makalede nesne tabanlı PowerShell'den bahsetmişti.

Bunu bir kenara bırakırsak, özellikle karmaşık görevleri çalıştıran komut dosyalarıyla çalışırken veya eğer Senaryo bir çeşit etkileşim gerektirir. Komut dosyanızın farklı türde çıktılar üretmesi de söz konusu olabilir; bu da bunları tek bir çıktı olarak göndermenin pek bir anlam ifade etmemesine neden olur; örneğin, posta kutularının seçilen özelliklerini ve ayrıca yetki verme izinlerini döndürdüğünüzde. İlerleme durumu hakkında fikir vermek için Write-Host kullanarak konsola ek bilgi yazmak, yöneticilerin hiçbir çıktıya bakmak zorunda kalmamasından daha iyidir. Aslında çıktının olmayışı çoğu yöneticinin betiğin çalışıp çalışmadığını merak etmesine neden olabilir.

Hata İşleme makalesinde tartışıldığı gibi, çıktı türleri PowerShell'de akışlara göre düzenlenir. Bunu Microsoft Teams'deki kanallar olarak düşünün. Her akış yeniden yönlendirilebilir veya gösterimi bastırılabilir. Kullanılabilir çıkış akışı cmdlet'leri şunlardır:

  • Write-Host, çıktıyı bir akış olarak kabul edilmeyen konsolun kendisine gönderir.
  • Yazma-Çıktı, varsayılan değer olan, işler iyi gittiğinde çıktı göndermek için kullanılan başarı akışı olarak anılır.
  • Yazma-Uyarı, Yazma-Ayrıntılı ve Yazma-Hata Ayıklama ilgili akışa çıktı gönderir. Bu çıktı akışlarının görüntülenmesi, ilgili tercih değişkeninin ayarına bağlıdır; örneğin, $WarningPreference'i, uyarı akışına gönderilen mesajların görüntülenmesini engellemek için SilentlyContinue olarak ayarlayın. İşlevler ve Parametreler hakkındaki önceki makalede belirtildiği gibi, bu davranışı kendi komut dosyalarınız için de denetleyebilirsiniz. Hata ayıklama ve ayrıntılı akışlar varsayılan olarak gösterilmez (Devam).
  • Sonlanmayan hatalarla ilgili mesajları hata akışına göndermek için Yazma Hatası. Hataların işlenmesi ve görüntülenmesi $ErrorActionPreference kullanılarak yapılandırılır.
  • Write-Information, PowerShell 5 ile tanıtıldı. Bilgi akışına çıktı gönderen Write-Host cmdlet'i için bir sarmalayıcıdır.

Bunlara ek olarak, çıktı akışlarıyla ilgilenmeyen ancak bunun yerine bir ilerleme çubuğu görüntülemek için kullanılan kullanışlı Write-Progress cmdlet'i de vardır. Kullanımı biraz daha karmaşıktır ancak özellikle konsol tabanlı görevlerde uzun süren işlemler için incelemeye değer. Yazma İlerlemesi ile ilgili küçük bir tavsiye, başsız komut dosyalarını çalıştırırken bunun bir anlam ifade etmemesidir. Ekran çıktısı da nispeten yoğundur, özellikle de gerekenden daha sık aradığınızda; çubuk bir yüzdeyi gösterir, dolayısıyla daha büyük kümelerle çalışırken her yinelemede bunu güncellemenin bir faydası yoktur. Her N'inci yinelemede güncellenmesi yeterlidir, ör.

$Mailboxes= Get-Mailbox -ResultSize Unlimited
$UpdateEvery= [int]($Mailboxes.Count / 100)
ForEach( $Mailbox in $Mailboxes) {
    $i++
    # Only show/update the progress bar for every ~1% change
    If($i -ge $UpdateEvery) {
        Write-Progress -Activity 'Processing Users ..'
            -Status ('User: {0}' -f $User.Name)
            -PercentComplete ($Index / $Mailboxes.Count) * 100
        $i= 0
    }

    # Do something
}

Yeşil İyidir, Kırmızı Kötüdür

PowerShell'in doğası gereği yöneticiler, yeşil çıktının iyi ve kırmızının kötü haber olduğu renk kodlu çıktıya alışkındır. PowerShell'de her çıkış akışının ekran görüntüsü için kendi rengi vardır. Bu arada, $host.PrivateData'nin renk özelliklerini değiştirerek ve [enum]::GetNames([System.PrivateData) tarafından döndürülen renklerden birine ayarlayarak varsayılan renkleri değiştirebilirsiniz. Kullanılabilir konsol renklerini sıralayan ConsoleColor]). Örneğin, hata akışı rengini griye ayarlamak için $host.PrivateData.ErrorForegroundColor='Gray' komutunu kullanın. Artık işler göze daha kolay gelebilir, ancak hatalar artık göze çarpmayacaktır.

Write-Host kullanılırken çıktı rengini geçersiz kılmak için aynı renk adları kullanılabilir; ör. Yazma-Ana Bilgisayar 'Her şey yolunda' -Ön Plan Rengi Yeşil. Yalnızca Write-Host'un renklendirmeyi desteklediğini unutmayın; akış çıkışına yönelik cmdlet'ler yapılandırılmış renklerini kullanır; Yazma Hatası için kırmızı ve Yazma Uyarısı için sarı.

Neler Olduğunu Günlüğe Kaydetme

Çıktıların ekrana kaydedilmesini tartıştık, peki ya günlükler oluşturup bunları raporlama veya analiz için başka bir yere göndermeye ne dersiniz? Microsoft'un kendi Log Analytics'i, Windows Eventlog'u veya kötü şöhretli Apache Log4j Hizmeti gibi bazı ürünler bu işlevsellik etrafında oluşturulmuştur. Bu hizmetleri kullanmak, ilgili arayüzlerle konuşmayı gerektirir;

Write-EventLog -LogName Application -Source MyScript -EventId 1001 -EntryType Information -Message 'All is well'

Ancak çıktıyı metin dosyalarına göndererek kullanıma uygun günlük kaydı da gerçekleştirebilirsiniz. İhtiyaç duyulması halinde log dosyalarını daha sonra e-posta yoluyla ilgili kişilere dağıtabilirsiniz. Her şey size ve gereksinimlerinize bağlıdır.

Şimdi müşteri senaryolarında kullandığım bir işlevi kullanarak günlüğe kaydetmenin daha pratik bir örneğine geçelim. Bir yardımcı işlevin kullanılması, iki ifadeye sahip olma ihtiyacını ortadan kaldırır: biri konsola bir şey kaydetmek için, diğeri ise bilgileri bir günlük dosyasında depolamak için. Nesneleri bilgi akışına ve bir dosyaya çıkaran Tee-Object cmdlet'inden yararlanmaya baktım. Benim durumumda, renk kodlaması yapmak ve günlük dosyasına zaman damgaları eklemek istedim, bu nedenle aşağıdaki işlevi oluşturdum:

Function Write-Log {
    param(
        [string]$Message='',
        [ValidateSet('Info','Warning', 'Error', 'Success', 'Verbose', 'Debug')]
        [string]$Severity= 'Info',
        [switch]$LogToFile,
        [string]$LogFileName
    )
    If(!( $LogFileName)) {
        # LogFile parameter not specified (null), so determine one
        $LogFolder= $PSScriptRoot
        $LogName= Split-Path -Path $MyInvocation.ScriptName -LeafBase
        $LogFileName= Join-Path -Path $LogFolder -ChildPath ('{0}-{1}.log' -f [string](Get-Date -format "yyyyMMdd"), $LogName)
    }
    Switch( $Severity) {
        'Warning' {
            $FormattedMessage= '[WARNING] {0}' -f $Message
            Write-Host $FormattedMessage -ForegroundColor $host.PrivateData.WarningForegroundColor -BackgroundColor $host.PrivateData.WarningBackgroundColor
        }
        'Error' {
            $FormattedMessage= '[ERROR] {0}' -f $Message
            Write-Host $FormattedMessage -ForegroundColor $host.PrivateData.ErrorForegroundColor -BackgroundColor $host.PrivateData.ErrorBackgroundColor        
        }
        'Verbose' {
            $FormattedMessage= '[VERBOSE] {0}' -f $Message
            Write-Host $FormattedMessage -ForegroundColor $host.PrivateData.VerboseForegroundColor -BackgroundColor $host.PrivateData.VerboseBackgroundColor
        }
        'Debug' {
            # Only log debug messages if debug stream shown
            If( $DebugPreference -ine 'SilentlyContinue') {
                $FormattedMessage= '[DEBUG] {0}' -f $Message
                Write-Host $FormattedMessage -ForegroundColor $host.PrivateData.DebugForegroundColor -BackgroundColor $host.PrivateData.DebugBackgroundColor
            }
            # Never log debug messages to file
            $LogToFile= $false
        }
        Default {
            $FormattedMessage= '{0}' -f $Message
            Write-Host $FormattedMessage
        }
    }
    If( $LogToFile) {
        [pscustomobject]@{
             Time = [string](Get-Date -format 'yyyyMMddHHmmss')
             Severity = $Severity
             Message = $Message
        } | Export-Csv -Path $LogFileName -Append -NoTypeInformation   
    }
}

Bu işlev aşağıdaki hedefleri gerçekleştirir:

  • Konsola ve dosyaya yazmak için yalnızca bir komut gereklidir.
  • Sağlanmadığında günlük dosyası, ISO 8601 biçimindeki komut dosyası adını ve tarihini temel alır.
  • Önem kategorileri sağlayabilirsiniz.
  • Dosyaya kaydedilen iletiler, metin tabanlı sıralama ve filtreleme için yararlı olan ISO 8601 biçiminde bir zaman damgası içerir.
  • Önem derecesi, mesajların konsol çıkışına renk kodlaması için kullanılır.
  • Günlük dosyası adında bir zaman damgası kullanarak, günlük olarak ayrı bir günlük oluştururuz, böylece günlük rotasyonunu basitleştirir ve eski günlükleri temizleriz.

İşlevi mevcut bir betiğe gömerken ve örneğin betiğinizde aşağıdaki komutu kullanarak çağırırken:

Write-Log -Severity Info -Message 'Well, hello there'

Konsol çıktısına ek olarak zaman damgalı bir giriş içeren bir günlük dosyası oluşturulur, örneğin:

"Time","Severity","Message"
"20240820154704","Info"," Well, hello there"

Örnek neden düz metin dosyasına değil de CSV dosyasına aktarılıyor? Benim durumumda, günlük dosyalarına işlenen kullanıcı nesnesinin kimliği gibi ekstra alanlar eklememe olanak tanıyor. Ayrıca alanların sıralanması ve filtrelenmesi, günlük iletilerine göre daha kolaydır, bu da daha fazla filtrelemeye veya seçime olanak tanır. Ayrıca bu durumda günlükleri kullanmak isteyen kişiler, verileri filtrelemek ve sıralamak için Excel'e aktarmayı tercih ettiğinden CSV dosyaları daha iyi bir seçimdir. Ancak bu gereksinime sahip değilseniz, metin dosyaları için İçerik Ekle'yi kullanacak şekilde Dışa Aktarma CSV'sini değiştirin.

İpucu: Hatalı çıktılar oluşturursanız komut dosyası için girdi olarak kullanılabilecek bir yapı kullanmayı düşünün. Örneğin, bir kullanıcı nesnesindeki bir işlem başarısız olduğunda Identity özelliğini ve başarısızlığın nedenini ekleyin. Bu CSV çıktısı, arızanın nedeni düzeltildikten sonra tekrar bir giriş dosyasına dönüştürülebilir. Bu girişleri orijinal kaynaktan tek tek seçmek zorunda kalmayacağınız için bu size biraz zaman kazandırabilir.

Bir işlevi kullanmanın başka bir yararı da, eğer bir gün SQL Server veya Eventlog gibi başka bir hedefe giriş yapmaya karar verirseniz, betiğinizi değiştirmenize gerek kalmaması, bunun yerine sadece gerekli kodu yardımcı işleve eklemeniz ve belki biraz temizlik yapmanızdır. gerekli bağlantıların kurulması.

Bu makaleyi sonuçlandırmak için, herhangi bir hassas bilgiyi günlük dosyalarına kaydetmemeye dikkat edin. Günlük dosyaları üçüncü taraflarla paylaşılabilir veya posta yoluyla bildirim gruplarına gönderilebilir. Gerektiğinde mesajı sterilize ettiğinizden emin olun.

İlgili makaleler