Public/Start-SCOMTrace.ps1
|
Function Start-SCOMTrace { <# .Synopsis This will initiate SCOM agent tracing, output trace files, then format the trace files into .log for humans. .EXAMPLE Start-SCOMTrace -General -Verbose .EXAMPLE Start-SCOMTrace -General -TraceSeconds 600 -GeneralGuidLevel Extended -OutPath C:\TraceLogs -Verbose .EXAMPLE Start-SCOMTrace -Specific -TraceSeconds 600 -SpecificTraceName 'MyCustomTrace' -OutPath C:\TraceLogs -Verbose -MaxLogMB 2048 .NOTES Function: Start-SCOMTrace Author: Tyson Paul (https://monitoringguys.com/) Version History: 2024.01.31.1441 - Fixed tmf cab expansion command. 2021.01.21.11047 - Rewrite into proper function for SCOMAgentHelper module 2020.11.20.1727 - v1 #> Param ( # the duration of the trace [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, ValueFromRemainingArguments = $false)] [Alias('Duration')] [int]$TraceSeconds = 300, <# 'Specific' requires workflow override of 'TraceEnabled' property for specific object instance (not group or class). ------- Override Example ------- <Monitoring> <Overrides> <MonitorPropertyOverride ID="WorkflowDebugger.WorkflowTraceOverride" Context="SQLServer!Microsoft.SQLServer.Windows.DBEngine" ContextInstance="2c821943-be80-5733-0b16-59850426eabe" Enforced="false" Monitor="TempDBPerformance!TempDBPerformance.TempDBPercentUsage.Monitor" Property="TraceEnabled"> <Value>true</Value> </MonitorPropertyOverride> </Overrides> </Monitoring> ------- End Override Example ------- #> [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, ParameterSetName='Parameter Set 1')] [switch]$Specific, # Name of the logfile. Used for a Specific trace scenario only. [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, ParameterSetName='Parameter Set 1')] [string]$SpecificTraceName = 'MyCustomWorkflowTrace', # General trace for common workflow types [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, ParameterSetName='Parameter Set 2')] [switch]$General, # Which GUIDs to include in the capture. Used for General trace scenario only. [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, ParameterSetName='Parameter Set 2')] [ValidateSet('Basic', 'Extended', 'Full')] [string]$GeneralGuidLevel = 'Basic', #Max size of log files (circular) [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [int]$MaxLogMB = 1024, # This is the path to the agent Tools folder. Use only if the default path is broken or incorrect in the registry. [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [string]$AgentToolsPath = 'NONE', # Where to output the trace files [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [string]$OutputPath = 'C:\Windows\Logs\OpsMgrTrace' ) #============================================================================== # Basic is most common. It's rare to need/use Extended or Full. This hash is a clever way to control tiers of inclusion. $hashGuidFileNames = [ordered]@{} Switch ($GeneralGuidLevel) { { $_ -match 'Basic|Extended|Full' } { $hashGuidFileNames['TracingGuidsNative'] = 'TracingGuidsNative.txt' $hashGuidFileNames['TracingGuidsScript'] = 'TracingGuidsScript.txt' $hashGuidFileNames['TracingGuidsManaged'] = 'TracingGuidsManaged.txt' } { $_ -match 'Extended|Full' } { $hashGuidFileNames['TracingGuidsConfigService'] = 'TracingGuidsConfigService.txt' $hashGuidFileNames['TracingGuidsDAS'] = 'TracingGuidsDAS.txt' $hashGuidFileNames['TracingGuidsFailover'] = 'TracingGuidsFailover.txt' $hashGuidFileNames['TracingGuidsUI'] = 'TracingGuidsUI.txt' } 'Full' { $hashGuidFileNames['TracingGuidsAdvisor'] = 'TracingGuidsAdvisor.txt' $hashGuidFileNames['TracingGuidsAPM'] = 'TracingGuidsAPM.txt' $hashGuidFileNames['TracingGuidsApmConnector'] = 'TracingGuidsApmConnector.txt' $hashGuidFileNames['TracingGuidsBID'] = 'TracingGuidsBID.txt' $hashGuidFileNames['TracingGuidsNASM'] = 'TracingGuidsNASM.txt' } } If (-NOT (Test-Path $OutputPath -PathType Container)){ New-Item -Path $OutputPath -ItemType Directory -ErrorAction SilentlyContinue } If (-NOT(Test-Path -Path $AgentToolsPath -PathType Container -ErrorAction SilentlyContinue) ) { # Locate tools directory $setupKey = Get-Item -Path 'HKLM:\Software\Microsoft\Microsoft Operations Manager\3.0\Setup' $InstallDirectory = $setupKey.GetValue('InstallDirectory') $AgentToolsPath = Join-Path -Path $InstallDirectory -ChildPath 'Tools' } Set-Location $AgentToolsPath Write-Output "`nAny active traces will first be stopped.`n" # Stop any previous tracing & .\Stoptracing.cmd Write-Output "`n`n`n" If ($General ) { ForEach ($Key in @($hashGuidFileNames.Keys)) { $outFile = (Join-Path -Path $OutputPath -ChildPath "$($Key).etl") Write-Output "Start General trace session for $outFile.`n" # Start general trace .\TraceLogSM.exe -start $Key -flag 0x1F -level 6 -f $outFile -b 64 -ft 10 -cir $MaxLogMB -guid $($hashGuidFileNames[$Key]) } } If ($Specific) { # This hash variable is used for the stop/format commands at the end. $hashGuidFileNames = [ordered]@{ $SpecificTraceName = $SpecificTraceName } $MySpecificTraceFile = Join-Path -Path $OutputPath -ChildPath "$($SpecificTraceName).etl" Write-Output "Start Specific trace session: $SpecificTraceName.`n" # Start trace for specific workflow object instance & .\TraceLogSM.exe -start $SpecificTraceName -flag 0xFF -level 5 -ft 1 -rt -GUID '#c85ab4ed-7f0f-42c7-8421-995da9810fdd' -b 1024 -f $MySpecificTraceFile } Write-Output "`n`nTracing for $TraceSeconds seconds..." $EndTime = (Get-Date).AddSeconds($TraceSeconds) $TMFExists = $false While ((Get-Date) -le $EndTime) { If (((Get-Date).Second)%10 -eq 0) { Write-Output "`n$([math]::Max(([int]($EndTime - (Get-Date)).TotalSeconds),0) ) seconds remain until trace completion..." } If (-NOT $TMFExists){ # Ensure that 'all.tmf' exists in Tools folder $allPath = (Join-Path $AgentToolsPath 'all.tmf') If (-NOT (Test-Path -Path $allPath -PathType Leaf)) { Write-Output "`nIn the meantime, 'all.tmf' not found at this path: [$($allPath)]. No problem, I ($(whoami.exe)) will fix it now." Write-Output "Will extract all .cabs in the TMF folder. Will concatenate all .tmf files from the .\TMF\*.tmf path into a single file: .\all.tmf" $CABs = @(Get-ChildItem -Path (Join-Path $AgentToolsPath 'TMF') -Filter '*.cab') ForEach ($Cab in $CABs) { expand.exe $CAB.FullName -F:*.tmf ".\tmf\$($Cab.BaseName).tmf" } Write-Output "`nBuilding 'all.tmf' file..." Get-Content .\tmf\*.tmf | Out-File .\all.tmf -Encoding utf8 If (-NOT (Test-Path -Path $allPath -PathType Leaf)) { Write-Output "`nFailed to create necessary tmf file at path: [$($allPath)]." } Else { Write-Output "`nGood news! 'all.tmf' now exists at this path: [$($allPath)]." $TMFExists = $True } } Else { Write-Output "`nGood news! 'all.tmf' exists at this path: [$($allPath)]." $TMFExists = $True } } Start-Sleep -Seconds 1 } # If script has been stopped manually for some reason, no problem! You can run the below commands to stop the TRACES and format the logs. # Stop tracing session and format logs for humans to read @($hashGuidFileNames.Keys) | ForEach-Object -Process { Write-Output "`n`nStopping trace of session: $_." & .\TraceLogSM.exe -stop $_ } @($hashGuidFileNames.Keys) | ForEach-Object -Process { Write-Output "`n`nFormating: $(Join-Path -Path $OutputPath -ChildPath "$($_).etl")..." # Format the trace data into plain text for humans & .\TraceFmtSM.exe $(Join-Path -Path $OutputPath -ChildPath "$($_).etl") -tmf .\all.tmf -o $(Join-Path -Path $OutputPath -ChildPath "$($_).LOG") } Write-Output "`nFormat done!`n" } |