Public/Invoke-DeliveryOptimizationLogParser.ps1
<#
.Synopsis Script parse the output of Get-DeliveryOptimizationLog and show the details in a grid view .DESCRIPTION #************************************************************************************************************ # Disclaimer # # This sample script is not supported under any Microsoft standard support program or service. This sample # script is provided AS IS without warranty of any kind. Microsoft further disclaims all implied warranties # including, without limitation, any implied warranties of merchantability or of fitness for a particular # purpose. The entire risk arising out of the use or performance of this sample script and documentation # remains with you. In no event shall Microsoft, its authors, or anyone else involved in the creation, # production, or delivery of this script be liable for any damages whatsoever (including, without limitation, # damages for loss of business profits, business interruption, loss of business information, or other # pecuniary loss) arising out of the use of or inability to use this sample script or documentation, even # if Microsoft has been advised of the possibility of such damages. #************************************************************************************************************ Script to parse the output of Get-DeliveryOptimizationLog and show the details in a grid view Select a job and hit ok to see more details The script is made to help troubleshoot Delivery Optimization issues #> Function Invoke-DeliveryOptimizationLogParser { [CmdletBinding()] param() # Test if the script is running with admin rights if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)) { Write-Warning "This function requires to be run with admin rights. Please run it as administrator." return } # We also need powershell 7 or higher to run this script if ($PSVersionTable.PSVersion.Major -lt 7) { Write-Warning "This function requires PowerShell 7 or higher. Please run it with PowerShell 7 or higher." return } $GetDeliveryOptimizationLog = Get-DeliveryOptimizationLog # Load some previous exported data from a different machine #$GetDeliveryOptimizationLog = Import-Clixml -Path "C:\temp\Get-DeliveryOptimizationLog.xml" if ($null -eq $GetDeliveryOptimizationLog) { Write-Host "No Delivery Optimization logs found. Exiting." return } $searchPattern = 'Job\s(?<jobid>[0-9a-fA-F\-]+),\sDisplayName: (?<jobtype>.*)|TraceDownloadStartImpl|TraceDownloadCompletedImpl' [array]$searchResult = $GetDeliveryOptimizationLog | select-string -Pattern $searchPattern #| ogv # First pass to get the job id and type $outObject = [System.Collections.Generic.List[System.Object]]::new() $jobHashTable = @{} $i = 1 foreach ($line in $searchResult) { Write-Progress -Activity "Processing" -Status "Processing line $i of $($searchResult.Count)" -PercentComplete (($i / $searchResult.Count) * 100) $i++ # match again to work with groups easier $matches = $null if ($line.Line -imatch 'Job\s(?<jobid>[0-9a-fA-F\-]+),\sDisplayName: (?<jobtype>.*)') { $jobID = $matches.jobid $jobType = $matches.jobtype $downloadStart = $null # Prevent duplicates in the hashtable if ($jobHashTable.ContainsKey($jobID)) { continue } $tempObj = [PSCustomObject][ordered]@{ JobId = $jobID JobType = $jobType DownloadStartUTC = $null DownloadSec = $null SessionSec = $null DownloadUrl = $null CacheHost = $null DownloadActions = $null Background = $null Mode = $null DownloadModeSrc = $null IsVpn = $null FileMB = $null ReqMB = $null CdnMB = $null MccMB = $null RledbatMB = $null LanMB = $null LinkLocalMB = $null GroupMB = $null InetMB = $null LcacheMB = $null ConnCdn = $null ConnMcc = $null ConnLan = $null ConnLinkLocal = $null ConnGroup = $null ConnInet = $null DownMbits = $null UpMbits = $null } # Lets get the download start and download completed lines for this job id $startSearchPattern = ".*(TraceDownloadStartImpl).*($jobID).*" [array]$returnValueStart = $searchResult.Line | select-string -Pattern $startSearchPattern # replace everything except the datetime if ($returnValueStart.Count -ge 1) { $downloadStart = $returnValueStart[0].Matches.Value -replace '^(?<lookup>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{7}Z).*', '${lookup}' $tempObj.DownloadStartUTC = Get-Date($downloadStart) -format 'yyyy-MM-dd HH:mm:ss' $tempObj.DownloadUrl = $returnValueStart[0].Matches.Value -replace '.*(?<lookup>https?://[^\s,]+).*', '${lookup}' $tempObj.CacheHost = $returnValueStart[0].Matches.Value -replace '.*cacheHost = (?<lookup>.*?), localFile.*', '${lookup}' -replace 'GP:' $tempObj.Background = $returnValueStart[0].Matches.Value -replace '.*background\? (?<lookup>.*?),.*', '${lookup}' $tempObj.Mode = $returnValueStart[0].Matches.Value -replace '.*downloadMode = (?<lookup>.*?),.*', '${lookup}' $tempObj.DownloadModeSrc = $returnValueStart[0].Matches.Value -replace '.*downloadModeSrc = (?<lookup>.*?),.*', '${lookup}' $tempObj.IsVpn = $returnValueStart[0].Matches.Value -replace '.*isVpn = (?<lookup>.*?),.*', '${lookup}' } $endSearchPattern = ".*(TraceDownloadCompletedImpl).*($jobID).*" [array]$returnValueEnd = $searchResult.Line | select-string -Pattern $endSearchPattern # replace everything except the datetime if ($returnValueEnd.Count -ge 1) { # We need the download completed time from the last item of the array in case we have multiple entries # Multiple entries are possible if the job is retried for some reason or if the jobs paused and resumed $downloadCompleted = $returnValueEnd[-1].Matches.Value -replace '^(?<lookup>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{7}Z).*', '${lookup}' #$tempObj.DownloadCompleted = $downloadCompleted $tempObj.DownloadActions = $returnValueEnd.Count $fileData = $returnValueEnd[-1].Matches.Value -replace '.*bytes: (?<lookup>\[.*?\]),.*', '${lookup}' $tempObj.FileMB = "{0:N2}" -f [math]::round(($fileData -replace '\[File:\s(?<lookup>\d+),.*', '${lookup}') / 1MB,2) $tempObj.ReqMB = "{0:N2}" -f [math]::round(($fileData -replace '.*req: (?<lookup>\d+),.*', '${lookup}') / 1MB,2) $tempObj.CdnMB = "{0:N2}" -f [math]::round(($fileData -replace '.*CDN: (?<lookup>\d+),.*', '${lookup}') / 1MB,2) $tempObj.MccMB = "{0:N2}" -f [math]::round(($fileData -replace '.*DOINC: (?<lookup>\d+),.*', '${lookup}') / 1MB,2) $tempObj.RledbatMB = "{0:N2}" -f [math]::round(($fileData -replace '.*rledbat: (?<lookup>\d+),.*', '${lookup}') / 1MB,2) $tempObj.LanMB = "{0:N2}" -f [math]::round(($fileData -replace '.*LAN: (?<lookup>\d+),.*', '${lookup}') / 1MB,2) $tempObj.LinkLocalMB = "{0:N2}" -f [math]::round(($fileData -replace '.*LinkLocal: (?<lookup>\d+),.*', '${lookup}') / 1MB,2) $tempObj.GroupMB = "{0:N2}" -f [math]::round(($fileData -replace '.*Group: (?<lookup>\d+),.*', '${lookup}') / 1MB,2) $tempObj.InetMB = "{0:N2}" -f [math]::round(($fileData -replace '.*inet: (?<lookup>\d+),.*', '${lookup}') / 1MB,2) $tempObj.LcacheMB = "{0:N2}" -f [math]::round(($fileData -replace '.*lcache: (?<lookup>\d+),.*', '${lookup}') / 1MB,2) $connectionData = $returnValueEnd[-1].Matches.Value -replace '.*conns: (?<lookup>\[.*?\]),.*', '${lookup}' $tempObj.ConnCdn = $connectionData -replace '.*CDN: (?<lookup>\d+),.*', '${lookup}' $tempObj.ConnMcc = $connectionData -replace '.*DOINC: (?<lookup>\d+),.*', '${lookup}' $tempObj.ConnLan = $connectionData -replace '.*LAN: (?<lookup>\d+),.*', '${lookup}' $tempObj.ConnLinkLocal = $connectionData -replace '.*LinkLocal: (?<lookup>\d+),.*', '${lookup}' $tempObj.ConnGroup = $connectionData -replace '.*Group: (?<lookup>\d+),.*', '${lookup}' $tempObj.DownMbits = "{0:N2}" -f [math]::round(($returnValueEnd[-1].Matches.Value -replace '.*downBps: (?<lookup>\d+),.*', '${lookup}') / 1000000,2) $tempObj.UpMbits = "{0:N2}" -f [math]::round(($returnValueEnd[-1].Matches.Value -replace '.*upBps: (?<lookup>\d+),.*', '${lookup}') / 1000000,2) $tempObj.DownloadSec = "{0:N2}" -f [math]::round(($returnValueEnd[-1].Matches.Value -replace '.*timeMs: (?<lookup>\d+),.*', '${lookup}') / 1000,2) $tempObj.SessionSec = "{0:N2}" -f [math]::round(($returnValueEnd[-1].Matches.Value -replace '.*sessionTimeMs: (?<lookup>\d+),.*', '${lookup}') / 1000,2) } $outObject.Add($tempObj) # Hashtable to avoid duplicate entries $jobHashTable.Add($matches.jobid, $matches.jobtype) } } Write-Progress -Activity "Processing" -Completed -Status "Processing line $i of $($searchResult.Count)" -PercentComplete (($i / $searchResult.Count) * 100) # output the data to a grid view $ogvTitle = 'Delivery Optimization Jobs - Select a job and hit ok to see more details' Do { $selectedItem = $outObject | Out-GridView -Title $ogvTitle -OutputMode Single if ($selectedItem) { # Create calculated values $dateTime = @{Label="DateTime";Expression={Get-Date($_.Line -replace '^(?<lookup>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{7}Z).*', '${lookup}') -Format 'yyyy-MM-dd HH:mm:ss'}} # Just the jobID $jobIdFilter = @{Label="JobId";Expression={$selectedItem.JobId}} # Everything except the datetime at the start of the line $lineString = @{Label="LogText";Expression={$_.Line -replace '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{7}Z(?<lookup>.*)', '${lookup}'}} # Passthru to wait for user input and then rerun the selection $null = $GetDeliveryOptimizationLog | select-string -Pattern $selectedItem.JobId | Select-Object -Property $dateTime, $jobIdFilter, $lineString | Out-GridView -Title 'Delivery Optimization Job Details' -PassThru } } While ($selectedItem) } |