Compartilhar via


Solucionar problemas com o agente do Log Analytics para Windows

Este artigo fornece ajuda para a solução de possíveis erros com o agente de Log Analytics para Windows no Azure Monitor e sugere possíveis soluções para resolvê-los.

Ferramenta de solução de problemas Log Analytics

A ferramenta de solução de problemas do Windows para o agente do Log Analytics é uma coleção de scripts do PowerShell elaborados para ajudar a localizar e diagnosticar problemas com o agente do Log Analytics. Ela é incluída automaticamente com o agente na instalação. A execução da ferramenta deve ser a primeira etapa no diagnóstico de um problema.

Usar a ferramenta de solução de problemas

  1. Abra o prompt do PowerShell como administrador na máquina em que o agente do Log Analytics está instalado.

  2. Vá até o diretório em que seu arquivo está localizado:

    cd "C:\Program Files\Microsoft Monitoring Agent\Agent\Troubleshooter"

  3. Execute o script principal usando este comando:

    .\GetAgentInfo.ps1

  4. Selecione o cenário de solução de problemas.

  5. Siga as instruções no console. Observação: as etapas de logs de rastreamento exigem intervenção manual para interromper a coleta de log. Com base na reprodução do problema, aguarde o tempo de duração e pressione "s" para parar a coleta de logs e prosseguir para a próxima etapa.

    A localização do arquivo de resultados é registrada após a conclusão e uma nova janela do Explorer destacando-a é aberta.

Instalação

A ferramenta de solução de problemas é incluída automaticamente na instalação do agente de Log Analytics Build 10.20.18053.0 e em diante.

Cenários cobertos

A ferramenta de solução de problemas verifica os seguintes cenários:

  • O agente não está relatando a ausência de dados ou dados de pulsação.
  • Falha na implantação da extensão do agente.
  • O agente está travando.
  • O agente está consumindo muita CPU ou memória.
  • Falhas de instalação e desinstalação.
  • Os logs personalizados apresentando problemas.
  • O Gateway do OMS apresentando problemas.
  • Contadores de desempenho apresentando problemas.
  • Os logs do agente não podem ser coletados.

Observação

Execute a ferramenta de solução de problemas quando necessário. Contar com os logs inicialmente ajudará muito nossa equipe de suporte a solucionar o problema com mais rapidez.

Fontes de solução de problemas importantes

Para auxiliar na solução de problemas relacionados ao agente do Log Analytics para Windows, o agente registra eventos no log de eventos do Windows, especificamente em Aplicativo e Serviços\Operations Manager.

Problemas de conectividade

Se o agente estiver se comunicando por meio de um servidor proxy ou firewall, podem existir restrições que impeçam a comunicação do computador de origem e do serviço do Azure Monitor. Se a comunicação for bloqueada devido a uma configuração incorreta, o registro em um workspace poderá falhar ao tentar instalar o agente ou definir que a pós-configuração do agente seja reportada a outro workspace. A comunicação do agente pode falhar após o registro bem-sucedido. Esta seção descreve os métodos para solucionar esse tipo de problema com o agente do Windows.

Verifique se o firewall ou o proxy está configurado para permitir as seguintes portas e URLs descritas na tabela a seguir. Confirme também se a inspeção HTTP não está habilitada para tráfego da web. Ela pode impedir um canal TLS seguro entre o agente e o Azure Monitor.

recurso de agente Portas Direção Ignorar a inspeção HTTPS
*.ods.opinsights.azure.com Porta 443 Saída Sim
*.oms.opinsights.azure.com Porta 443 Saída Sim
*.blob.core.windows.net Porta 443 Saída Sim
*.agentsvc.azure-automation.net Porta 443 Saída Sim

Para obter informações de firewall necessárias para o Azure Governamental, confira Gerenciamento do Azure Governamental. Se você planeja usar o Hybrid Runbook Worker da Automação do Azure para conectar e se registrar no serviço de automação para usar runbooks ou soluções de gerenciamento em seu ambiente, é necessário ter acesso ao número da porta e as URLs descritas em Configurar sua rede para o Hybrid Runbook Worker.

Há várias maneiras de verificar se o agente está se comunicando com êxito com o Azure Monitor:

  • Habilite a avaliação de Integridade do Agente do Azure Log Analytics no espaço de trabalho. No painel Integridade do Agente, exiba a coluna Contagem de agentes sem resposta para ver rapidamente se o agente está listado.

  • Execute a consulta a seguir para confirmar se o agente está enviando uma pulsação para o workspace ao qual ele está subordinado. Substitua <ComputerName> pelo nome da máquina virtual.

    Heartbeat 
    | where Computer like "<ComputerName>"
    | summarize arg_max(TimeGenerated, * ) by Computer 
    

    Se o computador estiver se comunicando com êxito com o serviço, a consulta deverá retornar um resultado. Se a consulta não retornou um resultado, primeiro verifique se o agente está configurado para se reportar ao workspace correto. Se estiver configurado corretamente, vá para a etapa 3 e pesquise o log de eventos do Windows para identificar se o agente está registrando o problema que pode estar impedindo a comunicação com o Azure Monitor.

  • Outro método de identificar um problema de conectividade é executar a ferramenta TestCloudConnectivity. Essa ferramenta é instalada por padrão com o agente na pasta %SystemRoot%\Program Files\Microsoft Monitoring Agent\Agent. Em um prompt de comando elevado, vá para a pasta e execute a ferramenta. A ferramenta retorna os resultados e os destaques em que o teste falhou. Por exemplo, a ferramenta pode estar relacionada a uma determinada porta ou URL que foi bloqueada.

    Captura de tela mostrando os resultados da execução da ferramenta TestCloudConnection.

  • Filtre o log de eventos do Operations Manager por Origem do evento Módulos do Serviço de Integridade, HealthService e Conector de Serviço e filtre por Nível de Evento Aviso e Erro para confirmar se há eventos gravados da tabela a seguir. Se houver, examine as etapas de resolução incluídas para cada evento possível.

    ID do evento Fonte Descrição Resolução
    2133 & 2129 Serviço de Integridade Falha na conexão com o serviço do agente. Esse erro pode ocorrer quando o agente não consegue se comunicar diretamente ou por meio de um servidor de firewall/proxy com o serviço do Azure Monitor. Verifique as configurações de proxy do agente ou se o firewall de rede/proxy permite o tráfego TCP do computador para o serviço.
    2138 Módulos do Serviço de Integridade Proxy exige autenticação. Defina as configurações de proxy do agente e especifique o nome de usuário/senha necessários para autenticar com o servidor proxy.
    2129 Módulos do Serviço de Integridade Falha na conexão. Falha na negociação do TLS. Verifique as configurações de TCP/IP do adaptador de rede e as configurações de proxy do agente.
    2127 Módulos do Serviço de Integridade Falha ao enviar dados - recebido código de erro. Se isso ocorrer apenas periodicamente durante o dia, pode ser uma anomalia aleatória que pode ser ignorada. Monitore para entender com que frequência isso acontece. Se ocorrer muitas vezes ao longo do dia, verifique primeiro as configurações de rede e de proxy. Se a descrição incluir o código de erro HTTP 404 e for a primeira vez que o agente tenta enviar dados para o serviço, ele incluirá um erro 500 com um código de erro 404 interno. O erro 404 significa "não encontrado", que indica que a área de armazenamento do novo workspace ainda está sendo provisionada. Na próxima tentativa, os dados serão gravados com êxito no workspace conforme o esperado. Um erro HTTP 403 pode indicar um problema de permissão ou de credenciais. Há mais informações incluídas com o erro 403 para ajudar a solucionar o problema.
    4000 Conector do Serviço Falha na resolução de nomes DNS. A máquina não conseguiu resolver o endereço de internet usado quando enviou dados para o serviço. Esse problema pode devido à configuração incorreta do resolvedor de DNS em sua máquina, de proxy ou um problema temporário de DNS com seu provedor. Se ocorrer periodicamente, isso pode ser causado por um problema transitório relacionado à rede.
    4001 Conector do Serviço Falha na conexão com o serviço. Esse erro pode ocorrer quando o agente não consegue se comunicar diretamente ou por meio de um servidor de firewall/proxy com o serviço do Azure Monitor. Verifique as configurações de proxy do agente ou se o firewall de rede/proxy permite o tráfego TCP do computador para o serviço.
    4002 Conector do Serviço O serviço retornou o código de status HTTP 403 em resposta a uma consulta. Verifique com o administrador de serviços a integridade do serviço. A consulta será repetida mais tarde. Esse erro é gravado durante a fase de registro inicial do agente. Você verá uma URL semelhante a https://<workspaceID>.oms.opinsights.azure.com/AgentService.svc/AgentTopologyRequest. Um código de erro 403 significa "proibido" e pode ser causado por uma ID ou chave de workspace digitada incorretamente. A data e a hora também podem estar incorretas no computador. Se a hora for +/-15 minutos em relação à hora atual, a integração falhará. Para corrigir esse problema, atualize a data e/ou o fuso horário do seu computador Windows.

Problemas de coleta de dados

Depois que o agente é instalado e reportado a seu(s) workspace(s) configurados, ele pode parar de receber configuração e coletar ou encaminhar desempenho, logs ou outros dados para o serviço, dependendo do que está habilitado e direcionado ao computador. É necessário determinar:

  • Se o tipo de dados é específico ou todos os dados não estão disponíveis no workspace?
  • O tipo de dados é especificado por uma solução ou como parte da configuração da coleta de dados do espaço de trabalho?
  • Quantos computadores são afetados? Um único ou vários computadores que se reportam ao workspace estão afetados?
  • Estava funcionando e parou em uma hora específica do dia, ou nunca foi coletado?
  • A consulta de pesquisa de log que você está usando está sintaticamente correta?
  • O agente já recebeu sua configuração do Azure Monitor?

A primeira etapa na solução de problemas é determinar se o computador está enviando um evento de pulsação.

Heartbeat 
    | where Computer like "<ComputerName>"
    | summarize arg_max(TimeGenerated, * ) by Computer

Se a consulta retornar resultados, você precisará determinar se um certo tipo de dados não foi coletado e encaminhado ao serviço. Esse problema pode ser causado pelo fato de o agente não receber a configuração atualizada do serviço ou algum outro sintoma que impeça o agente de operar normalmente. Execute as etapas a seguir para solucionar problemas adicionais.

  1. Abra um prompt de comando com privilégios elevados no computador e reinicie o serviço do agente digitando net stop healthservice && net start healthservice.

  2. Abra o log de eventos do Operations Manager e procure por IDs de evento 7023, 7024, 7025, 7028 e 1210 da Origem do evento HealthService. Esses eventos indicam que o agente está recebendo a configuração com êxito do Azure Monitor e está monitorando o computador ativamente. A descrição do evento da ID de evento 1210 também especificará na última linha todas as soluções e informações incluídas no escopo do monitoramento no agente.

    Captura de tela que mostra uma descrição da ID do Evento 1210.

  3. Aguarde alguns minutos. Se você não vir os dados esperados nos resultados da consulta ou na visualização, dependendo se você estiver exibindo os dados por meio de uma solução ou de um Insight, no log de eventos do Operations Manager, pesquise por Origens de evento HealthService e Módulos do Serviço de Integridade. Filtre por Nível de Evento Aviso e Erro para confirmar se ele gravou os eventos da tabela a seguir.

    ID do evento Fonte Descrição Resolução
    8000 HealthService Esse evento especificará se um fluxo de trabalho relacionado a desempenho, evento ou outro tipo de dados coletado não puder encaminhar ao serviço para ingestão no espaço de trabalho. A ID do evento 2136 do HealthService de origem é gravada junto com esse evento e pode indicar que o agente não consegue se comunicar com o serviço. Os possíveis motivos podem ser devido à configuração incorreta de proxy e autenticação, interrupção da rede, ou o firewall ou proxy de rede pode não permitir o tráfego TCP do computador para o serviço.
    10102 e 10103 Módulos do Serviço de Integridade O fluxo de trabalho não pôde resolver a fonte de dados. Isso poderá ocorrer se o contador de desempenho ou a instância especificada não existir no computador ou estiver definido incorretamente nas configurações de dados do workspace. Se o contador de desempenho foi especificado pelo usuário, verifique se as informações especificadas seguem o formato correto e existem nos computadores de destino.
    26002 Módulos do Serviço de Integridade O fluxo de trabalho não pôde resolver a fonte de dados. Esse problema poderá ocorrer se o log de eventos do Windows especificado não existir no computador. Esse erro poderá ser ignorado com segurança se o computador não tiver esse log de eventos registrado. Do contrário, se o log de eventos foi especificado pelo usuário, verifique se as informações especificadas estão corretas.

Problemas de Certificado Fixado com Versões Anteriores do Microsoft Monitoring Agent: Alteração Interruptiva

Visão Geral da Alteração da AC raiz

A partir de 30 de junho de 2023, o back-end do Log Analytics não aceitará mais conexões do MMA que referenciam um certificado raiz desatualizado. Esses MMAs são versões mais antigas antes da versão de inverno de 2020 (Agente do Log Analytics) e anteriores ao SCOM 2019 UR3 (SCOM). Qualquer versão, Pacote: 10.20.18053 / Extensão: 1.0.18053.0 ou superior não apresentará nenhum problema, bem como qualquer versão acima do SCOM 2019 UR3. Qualquer agente mais antigo do que isso será interrompido e não estará mais funcionando e carregando no Log Analytics.

O que exatamente está mudando?

Como parte de um esforço de segurança contínuo em vários serviços do Azure, o Azure Log Analytics mudará oficialmente da Raiz da AC Baltimore CyberTrust para a Raiz de AC do DigiCert Global G2. Essa alteração afetará as comunicações do TLS com o Log Analytics se o novo certificado Raiz de AC do DigiCert Global G2 estiver ausente do sistema operacional ou se o aplicativo estiver referenciando a antiga AC Baltimore Root. Isso significa que o Log Analytics não aceitará mais conexões do MMA que usam essa AC raiz antiga depois que ela for desativada.

Produtos de solução

Você pode ter recebido a notificação de alteração interruptiva mesmo que não tenha instalado pessoalmente o Microsoft Monitoring Agent. Isso ocorre porque vários produtos do Azure utilizam o Microsoft Monitoring Agent. Se você estiver usando um desses produtos, poderá ser afetado, pois eles utilizam o Agente do Log Analytics do Windows. Para esses produtos com links abaixo, pode haver instruções específicas que exigirão que você atualize para o agente mais recente.

Como Identificar e remediar Agentes de Interrupção

Para implantações com um número limitado de agentes, é altamente recomendável atualizar seu agente por nó por meio dessas instruções de gerenciamento.

Para implantações com vários nós, escrevemos um script que detectará quaisquer MMAs interruptivas afetadas por assinatura e, em seguida, as atualizará para a versão mais recente. Esses scripts precisam ser executados sequencialmente, começando com UpdateMMA.ps1 e, em seguida, UpgradeMMA.ps1. Dependendo do computador, o script pode demorar um pouco. É necessário o PowerShell 7 ou superior para ser executado para evitar um tempo limite.

UpdateMMA.ps1 esse script passará por VMs em suas assinaturas, marcar para MMAs existentes instaladas e, em seguida, gerará um arquivo .csv de agentes que precisam ser atualizados.

UpgradeMMA.ps1 esse script usará o arquivo de .CSV gerado em UpdateMMA.ps1 para atualizar todos as MMAs interruptivas.

Esses dois scripts podem levar algum tempo para serem concluídos.

# UpdateMMA.ps1
# This script is to be run per subscription, the customer has to set the az subscription before running this within the terminal scope.
# This script uses parallel processing, modify the $parallelThrottleLimit parameter to either increase or decrease the number of parallel processes
# PS> .\UpdateMMA.ps1 GetInventory
# The above command will generate a csv file with the details of VM's and VMSS that require MMA upgrade. 
# The customer can modify the csv by adding/removing rows if needed
# Update the MMA by running the script again and passing the csv file as parameter as shown below:
# PS> .\UpdateMMA.ps1 Upgrade
# If you don't want to check the inventory, then run the script wiht an additional -no-inventory-check
# PS> .\UpdateMMA.ps1 GetInventory & .\UpdateMMA.ps1 Upgrade


# This version of the script requires Powershell version >= 7 in order to improve performance via ForEach-Object -Parallel
# https://docs.microsoft.com/powershell/scripting/whats-new/migrating-from-windows-powershell-51-to-powershell-7?view=powershell-7.1
if ($PSVersionTable.PSVersion.Major -lt 7) 
{
    Write-Host "This script requires Powershell version 7 or newer to run. Please see https://docs.microsoft.com/powershell/scripting/whats-new/migrating-from-windows-powershell-51-to-powershell-7?view=powershell-7.1."
    exit 1
}

$parallelThrottleLimit = 16
$mmaFixVersion = [version]"10.20.18053.0"

function GetVmsWithMMAInstalled
{
    param(
        $fileName
    )

    $vmList = az vm list --show-details --query "[?powerState=='VM running'].{ResourceGroup:resourceGroup, VmName:name}" | ConvertFrom-Json
    
    if(!$vmList)
    {
        Write-Host "Cannot get the VM list, this script can only detect the running VM's"
        return
    }

    $vmsCount = $vmList.Length
    
    $vmParallelThrottleLimit = $parallelThrottleLimit
    if ($vmsCount -lt $vmParallelThrottleLimit) 
    {
        $vmParallelThrottleLimit = $vmsCount
    }

    if($vmsCount -eq 1)
    {
        $vmGroups += ,($vmList[0])
    }
    else
    {
        # split the vm's into batches to do parallel processing
        for ($i = 0; $i -lt $vmsCount; $i += $vmParallelThrottleLimit) 
        { 
            $vmGroups += , ($vmList[$i..($i + $vmParallelThrottleLimit - 1)]) 
        }
    }

    Write-Host "Detected $vmsCount Vm's running in this subscription."
    $hash = [hashtable]::Synchronized(@{})
    $hash.One = 1

    $vmGroups | Foreach-Object -ThrottleLimit $parallelThrottleLimit -Parallel {
        $len = $using:vmsCount
        $hash = $using:hash
        $_ | ForEach-Object {
            $percent = 100 * $hash.One++ / $len
            Write-Progress -Activity "Getting VM Inventory" -PercentComplete $percent
            $vmName = $_.VmName
            $resourceGroup = $_.ResourceGroup
            $responseJson = az vm run-command invoke --command-id RunPowerShellScript --name $vmName -g $resourceGroup --scripts '@UpgradeMMA.ps1' --parameters "functionName=GetMMAVersion" --output json | ConvertFrom-Json
            if($responseJson)
            {
                $mmaVersion = $responseJson.Value[0].message
                if ($mmaVersion) 
                {
                    $extensionName = az vm extension list -g $resourceGroup --vm-name $vmName --query "[?name == 'MicrosoftMonitoringAgent'].name" | ConvertFrom-Json
                    if ($extensionName) 
                    {
                        $installType = "Extension"
                    }
                    else 
                    {
                        $installType = "Installer"
                    }
                    $csvObj = New-Object -TypeName PSObject -Property @{
                        'Name'           = $vmName
                        'Resource_Group' = $resourceGroup
                        'Resource_Type'  = "VM"
                        'Install_Type'   = $installType
                        'Version'        = $mmaVersion
                        "Instance_Id"    = ""
                    }
                    $csvObj | Export-Csv $using:fileName -Append -Force
                } 
            } 
        }
    }
}

function GetVmssWithMMAInstalled
{
    param(
        $fileName
    )

    # get the vmss list which are successfully provisioned
    $vmssList = az vmss list --query "[?provisioningState=='Succeeded'].{ResourceGroup:resourceGroup, VmssName:name}" | ConvertFrom-Json   

    $vmssCount = $vmssList.Length
    Write-Host "Detected $vmssCount Vmss running in this subscription."
    $hash = [hashtable]::Synchronized(@{})
    $hash.One = 1

    $vmssList | Foreach-Object -ThrottleLimit $parallelThrottleLimit -Parallel {
        $len = $using:vmssCount
        $hash = $using:hash
        $percent = 100 * $hash.One++ / $len
        Write-Progress -Activity "Getting VMSS Inventory" -PercentComplete $percent
        $vmssName = $_.VmssName
        $resourceGroup = $_.ResourceGroup

        # get running vmss instance ids
        $vmssInstanceIds = az vmss list-instances --resource-group $resourceGroup --name $vmssName --expand instanceView --query "[?instanceView.statuses[1].displayStatus=='VM running'].instanceId" | ConvertFrom-Json
        if ($vmssInstanceIds.Length -gt 0) 
        {
            $isMMAExtensionInstalled = az vmss extension list -g $resourceGroup --vmss-name $vmssName --query "[?name == 'MicrosoftMonitoringAgent'].name" | ConvertFrom-Json
            if ($isMMAExtensionInstalled ) 
            {
                # check an instance in vmss, if it needs an MMA upgrade. Since the extension is installed at VMSS level, checking for bad version in 1 instance should be fine.
                $responseJson = az vmss run-command invoke --command-id RunPowerShellScript --name $vmssName -g $resourceGroup --instance-id $vmssInstanceIds[0] --scripts '@UpgradeMMA.ps1' --parameters "functionName=GetMMAVersion" --output json | ConvertFrom-Json
                $mmaVersion = $responseJson.Value[0].message
                if ($mmaVersion) 
                {
                    $csvObj = New-Object -TypeName PSObject -Property @{
                        'Name'           = $vmssName
                        'Resource_Group' = $resourceGroup
                        'Resource_Type'  = "VMSS"
                        'Install_Type'   = "Extension"
                        'Version'        = $mmaVersion
                        "Instance_Id"    = ""
                    }
                    $csvObj | Export-Csv $using:fileName -Append -Force
                }
            }
            else 
            {
                foreach ($instanceId in $vmssInstanceIds) 
                {
                    $responseJson = az vmss run-command invoke --command-id RunPowerShellScript --name $vmssName -g $resourceGroup --instance-id $instanceId --scripts '@UpgradeMMA.ps1' --parameters "functionName=GetMMAVersion" --output json | ConvertFrom-Json
                    $mmaVersion = $responseJson.Value[0].message
                    if ($mmaVersion) 
                    {
                        $csvObj = New-Object -TypeName PSObject -Property @{
                            'Name'           = $vmssName
                            'Resource_Group' = $resourceGroup
                            'Resource_Type'  = "VMSS"
                            'Install_Type'   = "Installer"
                            'Version'        = $mmaVersion
                            "Instance_Id"    = $instanceId
                        }
                        $csvObj | Export-Csv $using:fileName -Append -Force
                    }
                }
            }
        }      
    }
}

function Upgrade
{
    param(
        $fileName = "MMAInventory.csv"
    )
    Import-Csv $fileName | ForEach-Object -ThrottleLimit $parallelThrottleLimit -Parallel {
        $mmaVersion = [version]$_.Version
        if($mmaVersion -lt $using:mmaFixVersion)
        {
            if ($_.Install_Type -eq "Extension") 
            {
                if ($_.Resource_Type -eq "VMSS") 
                {
                    # if the extension is installed with a custom name, provide the name using the flag: --extension-instance-name <extension name>
                    az vmss extension set --name MicrosoftMonitoringAgent --publisher Microsoft.EnterpriseCloud.Monitoring --force-update --vmss-name $_.Name --resource-group $_.Resource_Group --no-wait --output none
                }
                else 
                {
                    # if the extension is installed with a custom name, provide the name using the flag: --extension-instance-name <extension name>
                    az vm extension set --name MicrosoftMonitoringAgent --publisher Microsoft.EnterpriseCloud.Monitoring --force-update --vm-name $_.Name --resource-group $_.Resource_Group --no-wait --output none
                }
            }
            else {
                if ($_.Resource_Type -eq "VMSS") 
                {
                    az vmss run-command invoke --command-id RunPowerShellScript --name $_.Name -g $_.Resource_Group --instance-id $_.Instance_Id --scripts '@UpgradeMMA.ps1' --parameters "functionName=UpgradeMMA" --output none
                }
                else 
                {
                    az vm run-command invoke --command-id RunPowerShellScript --name $_.Name -g $_.Resource_Group --scripts '@UpgradeMMA.ps1' --parameters "functionName=UpgradeMMA" --output none
                }
            }
        }
    }
}

function GetInventory
{
    param(
        $fileName = "MMAInventory.csv"
    )

    # create a new file 
    New-Item -Name $fileName -ItemType File -Force
    GetVmsWithMMAInstalled $fileName
    GetVmssWithMMAInstalled $fileName
}

switch ($args.Count)
{
    0 {
        Write-Host "The arguments provided are incorrect."
        Write-Host "To get the Inventory: Run the script as: PS> .\UpdateMMA.ps1 GetInventory"
        Write-Host "To update MMA from Inventory: Run the script as: PS> .\UpdateMMA.ps1 Upgrade"
        Write-Host "To do the both steps together: PS> .\UpdateMMA.ps1 GetInventory & .\UpdateMMA.ps1 Upgrade"
    }
    1 {
        $funcname = $args[0]
        Invoke-Expression "& $funcname"
    }
    2 {
        $funcname = $args[0]
        $funcargs = $args[1]
        Invoke-Expression "& $funcname $funcargs"
    }
}