public/Get-UnraidLog.ps1

function Get-UnraidLog {
    <#
    .SYNOPSIS
        Retrieves log file contents.

    .DESCRIPTION
        Fetches the content of system log files.
        This function uses Dynamic Parameters to query the Unraid server for the logs exposed through the API,
        providing tab-completion for the -LogName parameter based on what is actually on the server.

    .PARAMETER Path
        Direct path to a log file on the server.

    .PARAMETER LogName
        The name of the log to retrieve (e.g., "syslog", "docker").
        If connected, this list is dynamically populated from the server.

    .PARAMETER Lines
        Number of lines to return.

    .PARAMETER StartLine
        Starting line number for pagination.

    .PARAMETER Session
        Unraid session (defaults to current session).

    .EXAMPLE
        Get-UnraidLog -LogName syslog
    #>

    [CmdletBinding(DefaultParameterSetName = 'ByCommonName')]
    [OutputType('string')]
    param(
        [Parameter(Mandatory, ParameterSetName = 'ByPath')]
        [string]$Path,

        [Parameter()]
        [int]$Lines,

        [Parameter()]
        [int]$StartLine,

        [Parameter()]
        [UnraidSession]$Session = $script:DefaultUnraidSession
    )

    DynamicParam {
        # This dynamically generates the 'LogName' parameter with a ValidateSet
        # populated by the actual logs available on the server.
        
        $paramName = 'LogName'
        $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
        $attributes = New-Object System.Collections.ObjectModel.Collection[System.Attribute]

        # Define standard parameter attributes (Mandatory, Position 0, ParameterSet)
        $paramAttribute = New-Object System.Management.Automation.ParameterAttribute
        $paramAttribute.ParameterSetName = 'ByCommonName'
        $paramAttribute.Mandatory = $true
        $paramAttribute.Position = 0
        $attributes.Add($paramAttribute)

        # Try to fetch logs for the ValidateSet ONLY if we have a default session
        # If we are disconnected, we skip the ValidateSet so the user can still type a name manually
        # or get a generic parameter prompt without the script crashing.
        if ($script:DefaultUnraidSession) {
            try {

                # We use a lightweight query just to get names for the UI
                $gqlQuery = "query GetLogNames { logFiles { name } }"
                
                # Note: We must use the script-scope session directly here because
                # parameter binding for $Session hasn't happened yet.
                $result = Invoke-UnraidQuery -Query $gqlQuery -Session $script:DefaultUnraidSession -ErrorAction Stop
                
                if ($result.logFiles) {
                    $validLogNames = $result.logFiles.name
                    $validateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($validLogNames)
                    $attributes.Add($validateSetAttribute)
                }
            }
            catch {
                # If the dynamic fetch fails (timeout, api error), we sililently ignore it
                # and return the parameter without validation to ensure the cmdlet remains usable.
                Write-Debug "Dynamic parameter fetch failed: $_"
            }
        }

        # Create the dynamic parameter
        $runtimeParam = New-Object System.Management.Automation.RuntimeDefinedParameter($paramName, [string], $attributes)
        $paramDictionary.Add($paramName, $runtimeParam)

        return $paramDictionary
    }

    process {
        # Because LogName is dynamic, it doesn't exist as a regular variable and must be pulled from BoundParmams
        if ($PSBoundParameters.ContainsKey('LogName')) {
            $requestedLogName = $PSBoundParameters['LogName']
            
            # Now we need to resolve the friendly Name to a file Path.
            $gqlQuery = "query GetLogPaths { logFiles { name path } }"
            $result = Invoke-UnraidQuery -Query $gqlQuery -Session $Session

            $targetLog = $result.logFiles | Where-Object { $_.name -eq $requestedLogName } | Select-Object -First 1

            if ($targetLog) {
                $Path = $targetLog.path
                Write-Verbose "Resolved LogName '$requestedLogName' to path '$Path'"
            }
            else {
                Write-Error "Log '$requestedLogName' not found on server."
                return
            }
        }

        # Construct arguments for the retrieval query
        $queryArgs = [System.Collections.Generic.List[string]]::new()
        $queryArgs.Add("path: `"$Path`"")

        if ($PSBoundParameters.ContainsKey('Lines')) { $queryArgs.Add("lines: $Lines") }
        if ($PSBoundParameters.ContainsKey('StartLine')) { $queryArgs.Add("startLine: $StartLine") }

        $argString = $queryArgs -join ", "

        $gqlQuery = @"
        query GetLogFile {
            logFile($argString) {
                content
            }
        }
"@


        Write-Verbose "Fetching log content from: $Path"

        $result = Invoke-UnraidQuery -Query $gqlQuery -Session $Session 

        if ($result.logFile) { return $result.logFile.content }
        else { Write-Warning "File not found or empty response for path: $Path" }
    }
}