TMemoLog

Home > Delphi > TMemoLog > TStrToken > unit ImgWrapper > Download

toc

= Classe TMemoLog = Esta classe foi criada para proporcionar uma maneira simples e fácil de colocar mensagens de log por todo o programa para fins de debug. No início era só isso, mas depois a classe foi recebendo mais e mais recursos e alguns deles não são tão simples assim. A boa notícia é que o jeito básico, simples e fácil de usar continua igual. E querendo se aprofundar um pouco mais, recursos mais avançados estão também disponíveis.

A unit já cria uma instância da classe em uma variável global ( **Log ** ) e a não ser que sejam necessárias duas ou mais instâncias (nunca precisei) é recomendado usar essa criada pela unit.

Uso básico
Coloque um **TMemo ** no form. No evento **FormCreate ** do form principal associe o memo ao objeto **Log ** chamando seu método **AssignMemo **, depois ative (ou não) a saída de mensagens setando a propriedade **Active ** para **true ** (o default é **false ** ). A seguir use o método **Log.Msg ** (ou suas variações) para colocar as mensagens de log. Estas mensagens aparecerão no memo. Para limpar o log (e o memo) use o método **Log.Clear **. Veja o exemplo abaixo: code format="pascal" procedure TForm1.FormCreate(Sender: TObject); begin Log.AssignMemo(Memo1); Log.Active:= true; end;

procedure TForm1.Image1Click(Sender: TObject); begin Log.Msg('Clicou na imagem'); end;

procedure TForm1.btClearLogClick(Sender: TObject); begin Log.Clear; // limpa o memo também end;

code

Propriedade Active
Quando a propriedade **Active ** está em false, as mensagens são ignoradas. Use para ativar e desativar as mensagens em trechos específicos do código. Por default, **Active ** inicia em false.

A unit já jaz isso. As mensagens vão sendo colocadas em uma lista de strings enquanto um memo não é associado ao objeto. Assim que o memo é associado, a lista de strings é descarregada nele e liberada em seguida. A partir daí as mensagens são direcionadas para o memo. Esse mecanismo é transparente para o programador.
 * E para logar o código que é executado antes do FormCreate? **

**Obs:** Claro que para isso a propriedade Active deve ser setada para true antes do evento FormCreate.

Linhas **numeradas**
O log pode ter as linhas numeradas, para isso atribua o valor true para a propriedade **LineNumbers **.

**Hora do sistema**
Também é possivel acrescentar a hora do sistema no início de cada linha. Use a propriedade **TimeStamp ** (boolean) para ativar/desativar a hora nas mensagens. A hora será impressa no formato hh:mm:ss:mmm (mmm: milissegundos).

**Salvar em arquivo**
Use o método **Log.SaveToFile(Filename) ** para salvar o conteúdo do log em um arquivo texto. Se o memo já foi associado ao log, então o memo é salvo com seu método de gravação próprio ( **Memo.SaveToFile(Filename) ** ), caso contrário, a lista de strings com as mensagens é salva no arquivo informado. Veja abaixo um recurso mais avançado para gravação em arquivos. Caso o arquivo já exista no disco ele será apagado para gravar o log atual.

**Dica:** Todos os métodos com o prefixo "**log**", logam alguma coisa, ou seja escrevem no log. Os métodos com o prefixo "**get**", retornam alguma coisa mas não escrevem no log. Os métodos sem prefixo, em geral fazem alguma coisa, mas sem retornar ou logar nada. As exceções são o método **Msg** (que sempre loga a mensagem) e seu método complementar **Append** que concatena uma string na última linha do log.

**Gerar arquivo de log**
Salvar o log inteiro em arquivo é útil, mas não adianta muito se o programa tem bugs que podem provocar um "crash". Se o programa for forçado a abortar por qualquer razão, todo o log é perdido antes que possa ser salvo. Para resolver isso foi implementado outro sistema de gravação em disco que grava imediatamente cada mensagem enviada ao log. Para ativar a gravação em arquivo basta setar a propriedade **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 120%;">Log.WriteOnFile ** para **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">true ** (o default é **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">false ** ). A partir daí todas as mensagens serão gravadas no arquivo especificado pela propriedade **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Log.Filename **. O valor default para o nome do arquivo é **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">"Log.txt" ** na mesma pasta em que o executável está rodando, mas pode ser alterado a qualquer momento.

Esteja ciente que o uso deste sistema de gravação de mensagens pode afetar bastante o desempenho do programa especialmente se as mensagens forem geradas dentro de laços de repetição (for, while ou repeat). Para cada mensagem, o arquivo de log é aberto, a mensagem é gravada no final e depois o arquivo é fechado.

**Importante:** Este mecanismo para gerar arquivo de log não tem nenhuma relação com o método que salva o log em arquivo ( **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 120%;">SaveToFile ** ). São mecanismos independentes e se forem usados juntos no mesmo programa é preciso tomar algum cuidado. **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 120%;">SaveToFile ** sempre cria um novo arquivo. Se o arquivo já existia ele é zerado antes de salvar o log. Já o mecanismo controlado por **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 120%;">WriteOnFile ** e **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 120%;">Filename ** só vai criar um novo arquivo se ele não existe. Se o arquivo já existe, as mensagens são acrescentadas no final, mas o conteúdo anterior é sempre mantido.

**Marcação de tempo**
A classe também conta com um cronômetro interno feito para medir o tempo gasto na execução de um trecho de código. O método **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Log.MarkTime ** pega a hora atual e usa como referência. Chame este método no início do código que está sendo avaliádo, equivale a zerar e iniciar o cronômetro. Depois use o método **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Log.logTimeUntilNow ** para logar o tempo decorrido desde a última chamada a **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Log.MarkTime **. O método **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Log.strTimeUntilNow ** retorna a string que seria logada por **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Log.logTimeUntilNow ** mas não loga a string. Finalmente o método **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Log.getMiliSecsUntilNow ** retorna (sem logar) a quantidade de milissegundos desde **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Log.MarkTime **.

**Formatação**
Para formatar mensagens foi implementada uma sobrecarga do método **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Log.Msg ** para aceitar strings formatadas. Veja no código abaixo exemplos de uso desta variação de **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Msg ** (veja também o help do Delphi sobre a função **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Format **, funciona da mesma maneira).

**Indentação**
É possivel criar indentação nas mensagens para facilitar a visualização de pontos de entrada, internos e saída de contextos do programa (funções, blocos de comandos, etc). O efeito é bem parecido com a indentação feita no código fonte dos programas. Por default não são colocados espaços no início das mensagens, o que equivale ao nível zero de indentação. Cada nível de indentação acrescenta uma sequência de espaços no início da mensagem. A quantidade de espaços adicionados a cada nível é controlada pela propriedade
 * <span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Log.IndentStep **, por default é 3. Para aumentar e diminuir o nível de indentação podem ser usados respectivamente os métodos **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Log.IndentIn ** e **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Log.IndentOut ** . Mas a indentação também pode ser controlada pelo parâmetro opcional " **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">indent ** " que está presente em todas as sobrecargas (variações) do método **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Msg ** . Este parâmetro (tipo **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">TIndentType ** ) pode receber um dos seguintes valores:
 * **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">itIn: ** Indica que o nível de indentação deve ser aumentado **antes** de logar a mensagem. Isso faz efeito também nas mensagens seguintes, que usarão o nível de indentação aumentado.
 * **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">itOut: ** Indica que o nível de indentação deve ser diminuído **depois** que a mensagem for logada. As mensagens seguintes usarão o nível de indentação diminuído.
 * **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">itThis: ** Apenas esta mensagem deve ter seu nível de indentação aumentado. Não tem efeito nas mensagens seguintes.
 * **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">itNone: ** Nenhuma mudança no nível de indentação, será usado o nível atual (default).

Veja o exemplo abaixo: code format="pascal" function FuncNivelFuzzy(Param: integer): integer; begin Log.Msg ('>>> FuncNivelFuzzy in', itIn);

inc(Param); Log.Msg('Param: %d', [Param]);

result:= Param; Log.Msg('<<< FuncNivelFuzzy out', itOut); end;

function FuncNivel3(Param: integer): integer; begin Log.Msg ('>>> FuncNivel3 in', itIn);

inc(Param); Log.Msg('Param: %d', [Param]); Param:= FuncNivelFuzzy(Param); Log.Msg('Param: %d', [Param]);

result:= Param; Log.Msg('<<< FuncNivel3 out', itOut); end;

function FuncNivel2(Param: integer): integer; begin Log.Msg ('>>> FuncNivel2 in', itIn);

inc(Param); Log.Msg('Param: %d', [Param]); Param:= FuncNivel3(Param); Log.Msg('Param: %d', [Param]);

result:= Param; Log.Msg('<<< FuncNivel2 out', itOut); end;

procedure ProcNivel1; var Param: integer; begin Log.Msg ('>>> ProcNivel1 in', itIn);

Param:= 1; Log.Msg('Param: %d', [Param]); Param:= FuncNivel2(Param); Param:= FuncNivelFuzzy(Param); Log.Msg('Param: %d', [Param]);

Log.Msg('<<< ProcNivel1 out', itOut); end;

procedure TForm1.btStartClick(Sender: TObject); begin Log.Msg('Iniciando...'); ProcNivel1; Log.Msg('Finalizado.'); end;

code A saída deste código no memo é a que segue: code Iniciando... >>> ProcNivel1 in  Param: 1 >>> FuncNivel2 in     Param: 2 >>> FuncNivel3 in        Param: 3 >>> FuncNivelFuzzy in           Param: 4 <<< FuncNivelFuzzy out Param: 4 <<< FuncNivel3 out Param: 4 <<< FuncNivel2 out >>> FuncNivelFuzzy in     Param: 5 <<< FuncNivelFuzzy out Param: 5 <<< ProcNivel1 out Finalizado. code

Memória
Para ajudar a detectar vazamentos de memória e outros problemas relacionados foram implementados alguns recursos para monitorar o uso da memória:
 * **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 120%;">Log.logAllocatedMem: ** Loga a quantidade de memória alocada pelo programa.


 * **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 120%;">Log.logTotalFreeMem: ** Loga a quantidade de memória livre para o programa. Esta não é uma informação confiável para detectar vazamentos, a quantidade de memória livre não depende apenas de quanta memória foi alocada. Quando a quantidade de memória livre começa a diminuir muito, o sistema operacional aloca mais memória para o programa, então a quantidade de memória livre pode aumentar bastante sem que o programa tenha liberado a memória em uso. Já a quantidade de memória alocada depende apenas do programa, então é uma informação muito mais útil para monitorar vazamentos.


 * **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 120%;">Log.logHeapStatus: ** Status do heap. Inclui a memória alocada e livre.

Os métodos acima se referem ao uso da memória pelo programa como um todo, mas para detectar vazamentos de memória em trechos específicos do programa os métodos abaixo são mais apropriados:
 * **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Log.MarkMem: ** Armazena a quantidade de memória alocada pelo programa para usar como referência. Use este método no início do trecho de código que vai ser monitorado.


 * **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Log.logMemFromMark(aMsg:string): ** Loga a string em aMsg concatenando no final dela a quantidade de memória alocada ou liberada em relação à referência armazenada por MarkMem. O número informado indica a quantidade de bytes alocados (valor positivo) ou liberados (valor negativo) a partir da última chamada a **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Log.MarkMem ** . O valor zero significa que não houve mudança.

**Exceções**
A unit captura exceções não tratadas (quando ocorrem fora de um bloco **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">try ** ) que chegam pelo objeto **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Application ** (unit **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Forms ** ) atribuindo um manipulador de exceções para o evento **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">OnException ** de **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Application **. Caso a aplicação tenha seu próprio manipulador de exceções não tratadas, ele deve ser associado ao evento **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">OnException ** do log ( **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Log.OnException ** ). O que o manipulador do log faz é logar todas as informações disponíveis da exceção (chamando o método **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Log.logException ** ) e depois chamar (caso exista) o manipulador associado ao evento **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">OnException ** do log. Desta forma é possível logar as exceções antes de fazer qualquer tratamento nelas.

Para exceções tratadas (que ocorrem dentro de um bloco **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">try ** ) use o método **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Log.logException ** dentro do bloco **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">except **. Além de logar as informações que podem ser extraídas da própria exceção, o método também permite que seja logada uma string formatada com detalhes adicionais.

Propriedades
Objetos persistentes podem ter suas propriedades salvas e carregadas. Todos os componentes do Delphi, sejam visuais ou não, são descendentes de **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">TPersistent **, que é a classe que implementa a capacidade de salvar e carregar propriedades. Estas propriedades podem ser acessadas e logadas e é isso que o método **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">Log.logPropertiesOf(Obj: TPersistent) ** faz.

Este recurso pode não ser muito importante para acessar propriedades de componentes colocados no form em tempo de desenvolvimento, mas é bem útil para logar componentes criados em tempo de execução, em especial se você estiver desenvolvendo componentes e precisar acompanhar o comportamento deles.

Tokens
O método que gerencia as mensagens é capaz de separar string que contém tokens (sub-strings) separados por algum caracter. Nesse caso, cada token será logado em uma linha diferente. Esse recurso é controlado pela propriedade **<span style="color: #000060; font-family: 'Courier New',Courier,monospace; font-size: 130%;">StringSeparator ** (char). Se for atribuído qualquer caracter a essa propriedade que seja diferente do caracter nulo (#0) então o mecanismo é ativado e o caracter passa a ser considerado o separador de tokens. Para desativar a decodificação de strings basta atribuir o caracter nulo à propriedade. Para informações completas sobre o uso de tokens em strings, veja a documentação da classe **TStrToken**.