Как я могу записывать журналы в один файл, используя параллельный цикл foreach-object?

Проблема

Я пытаюсь записывать журналы в один файл журнала с помощью модуля PSlogging PowerShell.

Скрипт использует параллель Foreach-объекта.

Сценарий работает, но иногда сценарий застревает между ними.

Пожалуйста, помогите мне решить эту проблему? Есть ли лучший способ добиться этого?

Powershell ядро ​​7

код

#Script Version
$sScriptVersion = '1.0'

$sScriptVersion = '1.0'
$sLogName = 'custom.log'
#Log File Info
$sLogPath = "C:\Users\Dixon\Desktop\automation\azure\log"
$sLogFile = Join-Path -Path $sLogPath -ChildPath $sLogName

Start-Log -LogPath  $sLogPath -LogName $sLogName  -ScriptVersion $sScriptVersion

$allServers = $(Get-AzVM -Status) 

try {
    $allServers | ForEach-Object -parallel {

        $sScriptVersion = '1.0'
        $sLogName = "parallel-" + $_.Name +".log"
        #Log File Info
        $sLogPath = "C:\Users\Dixon\Desktop\automation\azure\log"
        $sLogFile = Join-Path -Path $sLogPath -ChildPath $sLogName

        Import-Module .\modules\pwshAzMod\pwshAzMod.psd1 -Force

        $version = "1.0.11"
       
        
        $vmName = $_.Name
        $vmLocation = $_.Location
        $vmResourceGroupName = $_.ResourceGroupName
        $powerState = $_.PowerState | Out-String
        if ($powerState -match "running") {
            if ($_.OSProfile.WindowsConfiguration) {
                Write-LogInfo -LogPath $sLogFile -Message  "Start --- Windows Servers --- $vmResourceGroupName $vmLocation $vmName $version $powerState"
                Write-LogInfo -LogPath $sLogFile -Message  "Done --- Windows Servers --- $vmResourceGroupName $vmLocation $vmName $version"
            } 
            elseif ($_.OSProfile.LinuxConfiguration) {
 
                $extensionName = "CustomScript"
                Write-LogInfo -LogPath $sLogFile -Message  "Start --- Linux Servers --- $vmResourceGroupName $vmLocation $vmName $version $powerState"
                Write-LogInfo -LogPath $sLogFile -Message  "Done --- Linux Servers --- $vmResourceGroupName $vmLocation $vmName $version"
            }
            else {
                Write-LogInfo -LogPath $sLogFile -Message  "OS Type not handled"
            }
        }
        else {
            if ($_.OSProfile.WindowsConfiguration) {
                Write-LogInfo -LogPath $sLogFile -Message  "Deallocated --- Windows Servers --- $vmResourceGroupName $vmLocation $vmName $version $powerState"
            }
            elseif ($_.OSProfile.LinuxConfiguration) {
                Write-LogInfo -LogPath $sLogFile -Message  "Deallocated --- Linux Servers --- $vmResourceGroupName $vmLocation $vmName $version $powerState"
            }
            else {
                Write-LogInfo -LogPath $sLogFile -Message "Deallocated --- Other Servers --- $vmResourceGroupName $vmLocation $vmName $version $powerState"
            }
        }
    }  -ThrottleLimit 5
}
catch {
    $errorMessage = $_
    $errorMessage
}
finally {
    Get-Content $sLogPath/parallel-*.log | Add-Content $sLogFile
    Remove-Item –path $sLogPath\*  -Filter parallel*
    Stop-Log -LogPath $sLogFile
}


person Dixon Joseph Dalmeida    schedule 21.03.2020    source источник
comment
Кстати: вам не нужно $(...) для захвата вывода команды в переменную - подойдет $allServers = Get-AzVM -Status.   -  person mklement0    schedule 21.03.2020
comment
Предположительно, Write-LogInfo из PSLogging модуля не был разработан с учетом параллелизма, что означает что вам нужно будет использовать явную синхронизацию между потоками.   -  person mklement0    schedule 21.03.2020
comment
@ mklement0 Чем я могу пользоваться, если не PSLogging?   -  person Dixon Joseph Dalmeida    schedule 22.03.2020
comment
Один из вариантов - разместить дополнительную логику для явной синхронизации вокруг вызовов Write-LogInfo, как описано в этом ответе.   -  person mklement0    schedule 22.03.2020


Ответы (2)


Он не будет в порядке, если вы его не отсортируете:

1..10 | foreach-object -Parallel { sleep 1;$_ } | set-content file.txt
person js2010    schedule 02.10.2020

Я наткнулся на это через Google, и мне не понравилось ни одно из решений, которые я нашел, поэтому я придумал свое собственное ведение журнала, которое вы, возможно, захотите попробовать. В этом примере показано, как безопасно записать в текстовый файл в текущем каталоге в параллельном цикле foreach:

$tw = [System.IO.TextWriter]::Synchronized([System.IO.File]::AppendText("$PSScriptRoot\log.txt"))

0..100 | Foreach-Object -Parallel {
  $writer = $Using:tw
  $writer.WriteLine("hello $_")
}
$tw.Close()

При желании оберните все это в try / finally с помощью $ tw.Close () в блоке finally, чтобы гарантировать, что буфер файла журнала будет очищен, а затем закрыт, если сценарий прерывается.

Объект TextWriter обрабатывает блокировку за вас и работает так же, как объект StreamWriter. Обратите внимание, что это предполагает, что вы не пытаетесь также читать из файла в каком-либо из потоков, для этого вам понадобится что-то намного более сложное.

person Dragoon    schedule 02.10.2020
comment
Я не собираюсь делать Stack Exchange, который ожидает отругать вас за то, что вы не ответили на то, что они спросили, потому что я думаю, что это лучшая альтернатива методу, который они хотели. Однако ответ другого пользователя лучше отвечает, как заставить его работать с их существующей методологией. Я думаю, что между вашим и их решением может быть другое решение, которое может быть более подходящим, но в целом я склонен согласиться с вашим решением больше, чем с исправлением их решения, так хорошо для вас, что я хотел бы дать вам замечание по этому поводу, прежде чем голосовать за Мне было поручено пересмотреть этот новый ответ на старый вопрос. - person Ben Personick; 03.10.2020
comment
Правильно, основная причина, по которой я разместил его здесь, заключается в том, что это первый вопрос, на который я пришел, когда использовал Google, поэтому более вероятно, что кто-то другой, пытающийся сделать то же самое, захочет попробовать этот подход. Я мог бы опубликовать его в другом решении, которое я нашел для записи в файл XML, о котором упоминал другой комментатор, но зная, насколько легко это сделать в .net, мне не хватало только одной части информации, которая была тем $ Using: bit to оставьте $ tw в поле для записи. Вероятно, это не идиоматический способ сделать это в PowerShell, как решение XML, но он короткий и помогает. - person Dragoon; 03.10.2020
comment
Я часто предпочитаю, чтобы кто-то написал конкретный ответ на заданный вопрос, вместо ссылки, даже если ответ аналогичен ответам на другой вопрос, именно по этой причине. Люди уже гуглили, что хотят получить ответ на заданный вопрос. - person Ben Personick; 03.10.2020