Como gerar um log de todas as tabelas abertas em memória

Às vezes precisamos criar um log para saber as tabelas que estavam abertas e em quais registros estavam, hoje iremos demonstrar em como fazer isso.

Existem alguns processos no sistema, que acabam rodando e para rastrearmos algo, fica um pouco oneroso, como por exemplo, o da contabilização.

Então pensando nisso, como poderíamos gravar um log genérico, para saber em quais tabelas o sistema esta posicionado? A lógica seria:

  • Na CT5, no campo desejado, colocar uma fórmula, por exemplo, u_zSuaFuncao()
  • Dentro dessa função, acionar a chamada de uma rotina que irá gerar o log, por exemplo, MemoWrite(cPastaNoServidor, u_zLogAlias(1))
  • Na função zLogAlias, pegue o conteúdo da variável pública cFOpened
  • Faça um For, percorrendo até 511 alias (conforme documentação do TDN: https://tdn.totvs.com/display/tec/Alias)
  • Através do comando Select() verifique se o alias está aberto
  • Se sim, grave o conteúdo no log com o nome do alias e o recno posicionado (também com o índice usado e o conteúdo do índice)

Abaixo a função zLogAlias desenvolvida conforme a lógica acima (ela também foi adaptada para retornar um array, caso precisem):

//Bibliotecas
#Include "TOTVS.ch"
   
/*/{Protheus.doc} User Function zLogAlias
Função que percorre todos os alias em memória para gravar em log
@type  Function
@author Atilio
@since 02/06/2022
@param nTipo, Numérico, Se for 1 irá retornar um texto de log e se não for irá retornar um array
@obs Posições do array:
 [1] = Tabela (ex.: SB1)
 [2] = RecNo (ex.: 1342)
 [3] = Índice usado (ex.: 1)
 [4] = Campos que compõem o índice (ex.: B1_FILIAL + B1_COD)
 [5] = Chave do registro posicionado (ex.: '  000001')
/*/
   
User Function zLogAlias(nTipo)
    Local aArea      := FWGetArea()
    Local cAbertos   := cFOpened
    Local nAtual     := 0
    Local xLog       := Nil
    Local cAliasAtu  := ""
    Local nAliasRec  := 0
    Local nIndUsado  := 0
    Local cIndCampos := ""
    Local cIndChave  := ""
    Local nMaximo    := 511
    Default nTipo    := 1
   
    If nTipo == 1
        xLog := "Alias abertos (cFOpened): " + cAbertos + CRLF + CRLF
        xLog += "Alias encontrados na WorkArea: " + CRLF
    Else
        xLog := {}
    EndIf
   
    //Percorre 511 alias (conforme documentação em https://tdn.totvs.com/display/tec/Alias)
    For nAtual := 0 To nMaximo
        //Pega o alias da area atual
        cAliasAtu := Upper(Alias(nAtual))
   
        //Somente se houver alias e Garantia para prevenir se realmente ta aberto, para não confiar apenas na variável pública
        If ! Empty(cAliasAtu) .And. Select(cAliasAtu) > 0
            nIndUsado  := (cAliasAtu)->(IndexOrd())
            cIndCampos := StrTran((cAliasAtu)->(IndexKey(nIndUsado)), "+", " + ")
            cIndChave  := (cAliasAtu)->( &(cIndCampos) )
            nAliasRec  := (cAliasAtu)->(RecNo())
   
            //Pula o registro caso seja tabelas internas
            If Left(cAliasAtu, 3) == "MP_" .Or. ;
               Left(cAliasAtu, 6) == "MPUSR_" .Or. ;
               Left(cAliasAtu, 7) == "MPMENU_" .Or. ;
               Left(cAliasAtu, 6) == "MPGRP_" .Or. ;
               Left(cAliasAtu, 1) == "X" .Or. ;
               Left(cAliasAtu, 4) == "TPH_" .Or. ;
               Left(cAliasAtu, 9) == "PROFALIAS"
                Loop
            EndIf
   
            If nTipo == 1
                xLog += "Tabela: " + cAliasAtu + ;
                "; Recno: " + cValToChar(nAliasRec) + ;
                "; Ordem: " + cValToChar(nIndUsado) +;
                "; Índice: " + cIndCampos +;
                "; Chave: '" + cIndChave + "'" +;
                CRLF
            Else
                aAdd(xLog, {;
                    cAliasAtu,;
                    nAliasRec,;
                    nIndUsado,;
                    cIndCampos,;
                    cIndChave;
                })
            EndIf
        EndIf
    Next
   
    FWRestArea(aArea)
Return xLog

Abaixo um print de como é o resultado, por exemplo, acionando na tela de cadastro de produtos.

Exemplo de tela com as tabelas e índices

Uma dica interessante, seria aproveitar a ideia do Canivete de Atalhos, e adicionar essa função em um atalho para já exibir em tela as tabelas abertas, conforme exemplo abaixo:

SetKey(K_SH_F12, { || ShowLog(u_zLogAlias()) })

Bom pessoal, por hoje é só.

Abraços e até a próxima.

Dan (Daniel Atilio)
Especialista em Engenharia de Software pela FIB. Entusiasta de soluções Open Source. E blogueiro nas horas vagas.

2 Responses

  1. Klaus Schneider Peres disse:

    Bom dia Daniel, só uma observação, como ainda não executei seu exemplo, não sei se há relevância, mas no TDN sobre o comando Alias(), ele vai de 0 (zero) até 511, mas no seu fonte, você inicia de 1, então a posição 0 (zero) nunca será pesquisada. Também não entendi onde deve ser colocada a função. Seria no campo X2_ROTINA da CT5 ou em algum ponto de entrada?

    • Bom dia Klaus, tudo joia?
      Opa, obrigado pelo aviso, já atualizei o exemplo para começar com 0 ao invés de 1.
      Quanto ao exemplo, vamos supor que dentro de um registro seu da CT5, no campo CT5_DEBITO tem a função u_zDebito(). Ai dentro dessa função, você adicionaria o comando para ver as tabelas que estão na memória, por exemplo:

      User Function zDebito()
      
         /* aqui os comandos da sua função */
      
         //Ai aciona a função para gravar as tabelas que estão na memória em um log dentro da Protheus Data
         MemoWrite("\x_pasta\arquivo_log_ct5_debito.txt", u_zLogAlias(1))
      Return xSeuRetorno
      

      Mas você pode acionar também em pontos de entrada ou em outras rotinas, pois ela funciona de forma genérica.

      Grande abraço.

Deixe uma resposta