Show or change StoreFront logging level

Version: 1.4.14
Creator Name: Guy Leech
Date Created: 2018-10-17
Date Modified: 2018-11-26
Scripting language: PS
Download Count: 52

Query or change the logging levels in Citrix StoreFront. Enable more verbose logging when troubleshooting potential StoreFront issues but turn the level back to off or error when finished to reduce the impact on CPU and free disk space. Arguments: Cluster - ff set to true then all servers in the cluster will be discovered and reported/operated on rather than just the selected computer (default is false) Trace Level - specify one of: 'Off', 'Error','Warning','Info','Verbose' or leave empty to query rather than change the current settings (default is empty)
Tags: Citrix,StoreFront,Logging

The Script

#Requires -version 3.0

<#
    Change or show StoreFront tracing settings on one or more SF servers.
    Note that this will restart various services so a brief loss of service is possible when seting the trace level

    Use of this script is entirely at your own risk - the author cannot be held responsible for any undesired effects deemed to have been caused by this script.

    @guyrleech, 2018
#>

## arguments
##   1 required trace level
##   2 operate on all servers in a cluster

[bool]$cluster = $false
[string[]]$servers = @( $env:COMPUTERNAME ) 
[string]$traceLevel = $null
[string[]]$validTraceLevels = @('Off', 'Error','Warning','Info','Verbose')
[int]$outputWidth = 400
$VerbosePreference = 'SilentlyContinue'

if( $args.Count -ge 2 )
{
    $traceLevel = $args[1]
}

if( $traceLevel -and $traceLevel -notin $validTraceLevels  )
{
    Throw "Illegal trace level `"$tracelevel`" specified - valid values are $($validTraceLevels -join ' , ')"
}

if( $args.Count -ge 1 )
{
    $cluster = ( $args[0] -eq 'true' )    
}

## Variables below here generally should not need to be changed
[string]$webConfig = 'web.config' 
[string]$installDirKey = 'SOFTWARE\Citrix\DeliveryServices'
[string]$installDirValue = 'InstallDir'
[string]$moduleInstaller = 'Scripts\ImportModules.ps1'
[string]$diagnosticsNode = 'configuration/system.diagnostics/switches/add'
[string]$logfileNode = 'configuration/system.diagnostics/sharedListeners/add'

# Altering the size of the PS Buffer
$PSWindow = (Get-Host).UI.RawUI
$WideDimensions = $PSWindow.BufferSize
$WideDimensions.Width = $outputWidth
$PSWindow.BufferSize = $WideDimensions

if( [string]::IsNullOrEmpty( $traceLevel ) )
{
    [string]$snapin = 'Citrix.DeliveryServices.Web.Commands'
}
else
{
    [string]$snapin = 'Citrix.DeliveryServices.Framework.Commands' 
}

## retrieve versions so store for second pass
[hashtable]$StoreFrontVersions = @{}

## We will retrieve all the cluster members - although multiple SF servers may have been specified, they may be members of different clusters so we get all unique names
if( $cluster )
{
    $newServers = New-Object -TypeName System.Collections.ArrayList
    ForEach( $server in $servers )
    {
        ## Read install dir from registry so we don't have to load all SF cmdlets
        $reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine,$server)
        if( $reg )
        {
            $RegSubKey = $Reg.OpenSubKey($installDirKey)

            if( $RegSubKey )
            {
                $installDir = $RegSubKey.GetValue($installDirValue) 
                if( ! [string]::IsNullOrEmpty( $installDir ) )
                {
                    $script = Join-Path $installDir $moduleInstaller
                    [string]$version,[string[]]$clusterMembers = Invoke-Command -ComputerName $server -ScriptBlock `
                    {
                        & $using:script
                        (Get-DSVersion).StoreFrontVersion
                        @( (Get-DSClusterMembersName).Hostnames )
                    } 
                    if( ! [string]::IsNullOrEmpty( $version ) )
                    {
                        $StoreFrontVersions.Add( $server , $version )
                    }
                    if( $clusterMembers -and $clusterMembers.Count )
                    {
                        ## now iterate through and add to servers array if not present already although indirectly since we are already iterating over this array so cannot change it
                        $clusterMembers | ForEach-Object `
                        {
                            if( $servers -notcontains $_ -and $newServers -notcontains $_ )
                            {
                                $null = $newServers.Add( $_ )
                            }
                        }
                    }
                    else
                    {
                        Write-Warning "No cluster members found via $server"
                    }
                }
                else
                {
                    Write-Error "Failed to read value `"$installDirValue`" from key HKLM\$installDirKey on $server"
                }
                $RegSubKey.Close()
            }
            else
            {
                Write-Error "Failed to open key HKLM\$installDirKey on $server"
            }
            $reg.Close()
        }
        else
        {
            Write-Error "Failed to open key HKLM on $server"
        }
    }
    if( $newServers -and $newServers.Count )
    {
        Write-Verbose "Adding $($newServers.Count) servers to action list: $($newServers -join ',')"
        $servers += $newServers
    }
    else
    {
        Write-Warning "Only a single server found in this cluster"
    }
}

[int]$badServers = 0 
           
[array]$results = @( ForEach( $server in $servers )
{
    if( [string]::IsNullOrEmpty( $traceLevel ) )
    {
        ## keyed on file name with value as the XML from that file
        [hashtable]$configFiles = Invoke-Command -ComputerName $server -ScriptBlock `
        {
            Add-PSSnapin $using:snapin
            [hashtable]$files = @{}
            $dsWebSite = Get-DSWebSite
            $dsWebSite.Applications | ForEach-Object `
            {
                $app = $_
                [string]$configFile = Join-Path $app.Folder $using:webConfig
                [xml]$content = $null

                if( Test-Path $configFile -ErrorAction SilentlyContinue )
                {
                    $content = (Get-Content $configFile)
                    if( $content )
                    {
                        $files.Add( $configFile , $content )
                    }
                }
            }
            $files
        }
        Write-Verbose "Got $($configFiles.Count) $webConfig files from $server"
        [hashtable]$states = @{}
        $configFiles.GetEnumerator() | ForEach-Object `
        {
            [xml]$node = $_.Value
            [string]$fileName = $_.Key
            $diags = $null
            try
            {
                $diags = @( $node.SelectNodes( "//$diagnosticsNode" ) )
                $logFile = $node.SelectSingleNode( "//$logfileNode" )  ## should only be one
            }
            catch { }
            if( $diags )
            {
                $diags | ForEach-Object `
                {
                    $thisSwitch = $_
                    [string]$module = ($thisSwitch.Name -split '\.')[-1]
                    $info = $null 
                    try
                    {
                        $info = [pscustomobject]@{ 'Server' = $server ; 'Trace Level' = $thisSwitch.Value ; 'Config File' = $fileName ; 'Module' = $module }
                        [string]$version = $StoreFrontVersions[ $server ]
                        if( ! [string]::IsNullOrEmpty( $version ) )
                        {
                            Add-Member -InputObject $info -MemberType NoteProperty -Name 'StoreFront Version' -Value $version
                        }
                        if( $logFile )
                        {
                           Add-Member -InputObject $info -NotePropertyMembers @{ 'Log File' = $logFile.initializeData ; 'Max Size (KB)' = $logFile.maxFileSizeKB  }
                           ## Seems this isn't present for all SF versions
                           if( Get-Member -InputObject $logFile -Name fileCount -ErrorAction SilentlyContinue )
                           {
                               Add-Member -InputObject $info -MemberType NoteProperty -Name 'Log File Count' -Value $logfile.fileCount
                           }
                        }
                        $states.Add( $thisSwitch.Value , [System.Collections.ArrayList]( @( $info ) ) )
                    }
                    catch
                    {
                        if( ! [string]::IsNullOrEmpty( $thisSwitch.Name ) -and $info )
                        {
                            $null = $states[ $thisSwitch.Value ].Add( $info )
                        }
                    }
                }
            }
        }
        $states.GetEnumerator() | select -ExpandProperty Value ## push into results array
        if( $states.Count -gt 1 )
        {
            Write-Warning "Trace levels are inconsistent on $server - $(($states.GetEnumerator()|Select -ExpandProperty Name) -join ',')"
            $states.GetEnumerator() | ForEach-Object { Write-Verbose "$($_.Name) :`n`t$(($_.Value|select -ExpandProperty 'Config File') -join ""`n`t"" )" }
            $badServers++
        }
        elseif( ! $states.Count )
        {
            Write-Warning "No trace levels found on $server"
        }
        Write-Host "$server : logging level is $(($states.GetEnumerator()|select -ExpandProperty Name ) -join ',')" ## Can't be Write-Output otherwise will be captured into the results array
    }
    else
    {
        Write-Host "Setting trace level to $traceLevel on $server"
        Invoke-Command -ComputerName $server -ScriptBlock { Add-PSSnapin $using:snapin ; Set-DSTraceLevel -All -TraceLevel $using:traceLevel }
    }
} )

if( $results.Count )
{
    [string]$title = $( if( $badServers )
    {
        "Inconsistent settings found on $badServers out of"
    }
    else
    {
        ## Now check it's the same consistent setting across all servers
        [string]$lastLevel = $null
        [bool]$matching = $true
        [int]$different = 0
        ForEach( $server in $servers )
        {
            [string]$thisLevel = $results | Where-Object { $_.Server -eq $server } | Select -First 1 -ExpandProperty 'Trace Level'
            if( $lastLevel -and $thisLevel -ne $lastLevel)
            {
                $matching = $false
                $different++
            }
            $lastLevel = $thisLevel
        }
        if( $matching )
        {
            "Consistent settings found on all"
        }
        else
        {
            "Different settings found on $different out of"
        }
    } ) + " $($servers.Count) StoreFront servers $($servers -join ' ')"
    
    $title

    [hashtable]$params = @{ 'AutoSize' = $true}
    if( $badServers )
    {
        $fields = [System.Collections.ArrayList]( @( 'Module','Trace Level','Config File','Log File','Max Size (KB)','Log File Count' ) )
        $params.Add( 'GroupBy' , 'Trace Level' )
    }
    else
    {
        $fields = [System.Collections.ArrayList]( @( 'Log File','Max Size (KB)','Log File Count' ) )
    }
    if( $servers.Count -gt 1 )
    {
        $fields.Insert( 0 , 'Server' )
    }
    if( $badServers )
    {
        $results | Select $fields | Format-Table @params
    }
    else
    {
        $results | Select $fields | Sort -Unique 'Log File' | Format-Table @params
    }
}