Artigo
· Jun. 10 9min de leitura

codemonitor.MonLBL - Monitoramento de código ObjectScript Linha-por-linha

Introdução

MonLBL é uma ferramenta para analisar o desempenho da execução de código ObjectScript linha por linha. codemonitor.MonLBL é um wrapper baseado no pacote %Monitor.System.LineByLinedo InterSystems IRIS, projetado para coletar métricas precisas sobre a execução de rotinas, classes ou páginas CSP.

O wrapper e todos os exemplos apresentados neste artigo estão disponíveis no seguinte repositório do GitHub: iris-monlbl-example

Funcionalidades

A ferramenta permite a coleta de diversos tipos de métricas:

  • RtnLine: Número de execuções da linha
  • GloRef: Número de referências a globais geradas pela linha
  • Time: Tempo de execução da linha
  • TotalTime: Tempo de execução total, incluindo sub-rotinas chamadas

Todas as métricas são exportadas para arquivos CSV.

Além das métricas linha a linha, dc.codemonitor.MonLBL coleta estatísticas de globais:

  • Tempo total de execução
  • Número total de linhas executadas
  • Número total de referências a globais
    -T empo de CPU do sistema e do usuário
    • O tempo de CPU do usuário corresponde ao tempo gasto pelo processador executando o código da aplicação.
    • O tempo de CPU do sistema corresponde ao tempo gasto pelo processador executando operações do sistema operacional (chamadas de sistema, gerenciamento de memória, E/S).
      -Tempo de leitura do disco.

Pré-requisitos

Para monitorar o código com MonLBL:
1. Obtenha a classe dc.codemonitor.MonLBL (disponível aqui)
2. As rotinas ou classes a serem analisadas devem ser compiladas com as flags "ck".

⚠️ Aviso Importante

O uso do monitoramento linha a linha impacta o desempenho do servidor. É importante seguir estas recomendações:

  • Use esta ferramenta apenas em um conjunto limitado de código e processos (idealmente para execução única em um terminal).
  • Evite usá-la em um servidor de produção (mas, às vezes, é necessário).
  • Prefira usar esta ferramenta em um ambiente de desenvolvimento ou teste.

Essas precauções são essenciais para evitar problemas de desempenho que possam afetar usuários ou sistemas de produção. Observe que o código monitorado é executado aproximadamente 15-20% mais lentamente do que o código não monitorado.

Uso

Exemplo Básico

// Cria uma instância de MonLBL
Set mon = ##class(dc.codemonitor.MonLBL).%New()

//Define as rotinas a serem monitoradas
Set mon.routines = $ListBuild("User.MyClass.1")

// Começa o monitoramento
Do mon.startMonitoring()

// Código a analisar
// ...

// Para o monitoramento e gera resultados
Do mon.stopMonitoring()

Nota: O monitoramento iniciado aqui é válido apenas para o processo atual. Outros processos que executam o mesmo código não serão incluídos nas medições.

Opções de Configuração

O wrapper oferece várias opções configuráveis:

  • directory:Diretório onde os arquivos CSV serão exportados (o padrão é o diretório Temp do IRIS).
  • autoCompile: Recompila automaticamente as rotinas com as flags "ck" se necessário.
  • metrics: Lista personalizável de métricas a serem coletadas.
  • decimalPointIsComma: Usa uma vírgula como separador decimal para melhor compatibilidade com o Excel (dependendo do seu ambiente local).
  • metricsEnabled:Habilita ou desabilita a coleta de métricas linha a linha.

Exemplo de Uso Avançado

Aqui está um exemplo mais completo (disponível na classe dc.codemonitor.Example):

ClassMethod MonitorGenerateNumber(parameters As %DynamicObject) As %Status
{
    Set sc = $$$OK
    Try {
        // Exibe parâmetros recebidos
        Write "* Parameters:", !
        Set formatter = ##class(%JSON.Formatter).%New()
        Do formatter.Format(parameters)
        Write !

        // Cria e configura o monitor
        Set monitor = ##class(dc.codemonitor.MonLBL).%New()

        // AVISO: Em produção, defina o autoCompile para $$$NO
        // e manualmente compile o código a monitorar
        Set monitor.autoCompile = $$$YES
        Set monitor.metricsEnabled = $$$YES
        Set monitor.directory = ##class(%File).NormalizeDirectory(##class(%SYS.System).TempDirectory())
        Set monitor.decimalPointIsComma = $$$YES

        // Configure a rotina para o monitor (formato "int" da classe)
        // Para achar o nome exato da rotina, use o comando:
        // Do $SYSTEM.OBJ.Compile("dc.codemonitor.DoSomething","ck")
        // A linha "Compiling routine XXX" deve exibir o nome da rotina
        Set monitor.routines = $ListBuild("dc.codemonitor.DoSomething.1")

        // Comece a monitorar
        $$$TOE(sc, monitor.startMonitoring())

        // Execute o código a monitorar com tratamento de erro
        Try {
            Do ##class(dc.codemonitor.DoSomething).GenerateNumber(parameters.Number)

            // Importante: Sempre pare o monitoramento
            Do monitor.stopMonitoring()
        }
        Catch ex {
            // Pare o monitoramento mesmo em caso de erro
            Do monitor.stopMonitoring()
            Throw ex
        }
    }
    Catch ex {
        Set sc = ex.AsStatus()
        Do $SYSTEM.Status.DisplayError(sc)
    }

    Return sc
}

Este exemplo demonstra várias melhores práticas importantes:

  • Uso de um bloco Try/Catch para tratamento de erros.
  • Interrupção sistemática do monitoramento, mesmo em caso de erro.
  • Configuração completa do monitor.

Exemplo com Páginas CSP

O MonLBL também permite o monitoramento de páginas CSP. Aqui está um exemplo (também disponível na classedc.codemonitor.ExampleCsp):

ClassMethod MonitorCSP(parameters As %DynamicObject = {{}}) As %Status
{
    Set sc = $$$OK
    Try {
        // Exibe parâmetros recebidos
        Write "* Parameters:", !
        Set formatter = ##class(%JSON.Formatter).%New()
        Do formatter.Format(parameters)
        Write !

        // Cria e configura o monitor
        Set monitor = ##class(dc.codemonitor.MonLBL).%New()
        Set monitor.autoCompile = $$$YES
        Set monitor.metricsEnabled = $$$YES
        Set monitor.directory = ##class(%File).NormalizeDirectory(##class(%SYS.System).TempDirectory())
        Set monitor.decimalPointIsComma = $$$YES

        // Para monitorar uma página CSP, use a rotina gerada
        // Exemplo: /csp/user/menu.csp --> classe: csp.menu --> rotina: csp.menu.1
        Set monitor.routines = $ListBuild("csp.menu.1")

        // Páginas CSP necessitam de objetos %session, %request, e %response
        // Crie esses objetos com os parâmetros necessários
        Set %request = ##class(%CSP.Request).%New()
        // Configure os parâmetros de requisição se necessário
        // Set %request.Data("<param_name>", 1) = <value>
        Set %request.CgiEnvs("SERVER_NAME") = "localhost"
        Set %request.URL = "/csp/user/menu.csp"

        Set %session = ##class(%CSP.Session).%New(1234)
        // Configure os dados da sessão se necessário
        // Set %session.Data("<data_name>", 1) = <value>

        Set %response = ##class(%CSP.Response).%New()

        // Comece o monitoramento
        $$$TOE(sc, monitor.startMonitoring())

        Try {
            // Para evitar a exibição do conteúdo da página CSP no terminal,
            // use a classe IORedirect para redirecionar sa saída para nula
            // (requere instalação via zpm "install io-redirect")
            Do ##class(IORedirect.Redirect).ToNull() 

            // Chame a página CSP via seu método OnPage 
            Do ##class(csp.menu).OnPage()

            // Restaure a saída padrão
            Do ##class(IORedirect.Redirect).RestoreIO()

            // Pare o monitoramento
            Do monitor.stopMonitoring()
        }
        Catch ex {
            // Sempre restaure a saída e pare o monitoramento no caso de erro
            Do ##class(IORedirect.Redirect).RestoreIO()
            Do monitor.stopMonitoring()

            Throw ex
        }
    }
    Catch ex {
        Set sc = ex.AsStatus()
        Do $SYSTEM.Status.DisplayError(sc)
    }

    Return sc
}

Pontos chave para monitorar páginas CSP:

  1. Identificação da Rotina: Uma página CSP é compilada em uma classe e uma rotina. Por exemplo/csp/user/menu.csp gera a classe csp.menu e a rotina csp.menu.1.

  2. **Ambiente CSP **:É necessário criar objetos de contexto CSP (%request, %session, %response) para que a página seja executada corretamente.

  3. Redirecionamento de Saída:: Para evitar a exibição de conteúdo HTML no terminal, você pode usar a ferramenta IORedirect (disponível no OpenExchange via zpm "install io-redirect").

  4. Chamada da Página: A execução é feita através do método OnPage() da classe gerada.

Exemplo de Saída

Aqui está um exemplo da saída obtida ao executar o método MonitorGenerateNumber:

USER>d ##class(dc.codemonitor.Example).MonitorGenerateNumber({"number":"100"})
* Parameters:
{
  "number":"100"
}

* Metrics are exported to /usr/irissys/mgr/Temp/dc.codemonitor.DoSomething.1.csv
* Perf results:
{
  "startDateTime":"2025-05-07 18:45:42",
  "systemCPUTime":0,
  "userCPUTime":0,
  "timing":0.000205,
  "lines":19,
  "gloRefs":14,
  "diskReadInMs":"0"
}

Nesta saída, podemos observar:

1.Exibição dos parâmetros de entrada.
2. Confirmação de que as métricas foram exportadas para um arquivo CSV.
3.Um resumo do desempenho global em formato JSON, incluindo:
- Data e hora de início
- Tempo de CPU do sistema e do usuário
- Tempo total de execução
- Número de linhas executadas
- Número de referências a globais
- Tempo de leitura do disco

Interpretando os Resultados CSV

Após a execução, arquivos CSV (um por rotina na lista de rotinas $ListBuild) são gerados no diretório configurado. Esses arquivos contêm:
-Número da linha
-Métricas coletadas para cada linha
- Código-fonte da linha (nota: se você não compilou com a flag "k", o código-fonte não estará disponível no arquivo CSV)

Aqui está um exemplo do conteúdo de um arquivo CSV exportado: (dc.codemonitor.DoSomething.1.csv):

Line RtnLine GloRef Time TotalTime Code
1 0 0 0 0 ;dc.codemonitor.DoSomething.1
2 0 0 0 0 ;Generated for class dc.codemonitor.DoSomething...
3 0 0 0 0 ;;59595738;dc.codemonitor.DoSomething
4 0 0 0 0 ;
5 0 0 0 0 GenerateNumber(n=1000000) methodimpl {
6 1 0 0.000005 0.000005 For i=1:1:n {
7 100 0 0.000019 0.000019 Set number = $Random(100000)
8 100 0 0.000015 0.000015 Set isOdd = number # 2
9 100 0 0.000013 0.000013 }
10 1 0 0.000003 0.000003 Return }

Nesta tabela, podemos analisar:

  • RtnLine: Indica quantas vezes cada linha foi executada (aqui, as linhas 6 e 10 foram executadas uma vez)
  • GloRef: Mostra as referências globais geradas por cada linha
  • Time: Apresenta o tempo de execução específico de cada linha
  • TotalTime: Exibe o tempo total, incluindo chamadas para outras rotinas

Esses dados podem ser facilmente importados para uma planilha para uma análise aprofundada. As linhas que mais consomem recursos em termos de tempo ou acesso a dados podem, assim, ser facilmente identificadas.

Observação sobre a Eficiência do Cache

A eficiência do cache do banco de dados (buffer global) pode mascarar problemas reais de desempenho. Durante a análise, o acesso aos dados pode parecer rápido devido a esse cache, mas pode ser muito mais lento em certas condições de uso no mundo real.

Em sistemas de desenvolvimento, você pode limpar o cache entre as medições com o seguinte comando:

Do ClearBuffers^|"%SYS"|GLOBUFF()

⚠️ ATENÇÃO: Tenha cuidado com este comando, pois ele se aplica a todo o sistema. Nunca o utilize em um ambiente de produção, já que pode impactar o desempenho de todas as aplicações em execução.

Conclusão

O monitoramento linha a linha é uma ferramenta valiosa para analisar o desempenho do código ObjectScript. Ao identificar precisamente as linhas de código que consomem mais recursos, ele permite que os desenvolvedores economizem um tempo significativo na análise de gargalos de desempenho.

Discussão (0)1
Entre ou crie uma conta para continuar