Júnior Oliveira

C Sharp; Delphi; ASP .NET; PHP; Javascript;

Registrar Logs no Event Viewer com Delphi

19 May 2011 delphi, di, ioc

Pessoal, estudando formas de gravar logs da aplicação, me lembrei do Event Viewer do Windows, tempo atrás utilizava classes do .Net em C# para fazer este logs e não mais em arquivos textos. Resolvi estudar se era possível utilizar a mesma técnica em Delphi XE e descobri que sim achei interessante compartilhar com vocês. Vale lembrar que não é algo novo, funciona em versões anteriores do Delphi.

E para não ficar apenas em um exemplo de como utilizar o Event Viewer com Delphi, desenvolvi toda uma estrutura de logs de mensagens utilizando Interfaces, onde ficará fácil caso queiram mudar a forma de gravação dos logs das aplicações de vocês, e também, estou utilizando neste exemplo uma arquitetura onde será fácil aplicar IoC (Inversão de Controle) e DI (Injeção de Dependências), vou deixar para falar deste dois Padrões em outro post.

O primeiro código é o nosso enumerator, que irá conter os tipos possíveis de log das mensagens da aplicação. Não é necessário separar este enumerator da unit que irá conter a nossa interface descrita abaixo, fiz está separação apenas para organizar melhor os arquivos da aplicação.

 1 unit LogType;
 2 
 3 interface
 4 
 5 type
 6   TLogType = (Information, Warning, Error);
 7 
 8 implementation
 9 
10 end.

O segundo código é a nossa Interface (Contrato), onde todas as classes que irão realizar logs deverão. Note que estou utilizando summary, ele é usado para descrever informações sobre o tipo ou membros do tipo, para quando formos utilizar já venha à definição do tipo no Intellisense como tipos próprio Delphi e também é uma forma de documentação do código fonte.

 1 unit Logger;
 2 
 3 interface
 4 
 5 uses
 6   LogType;
 7 
 8 type
 9   ILogger = interface
10     /// <summary>
11     /// Registra os logs.
12     /// </summary>
13     /// <param name="message">Mensagem que sera registrada.</param>
14     /// <param name="logType">Tipo do log.</param>
15     procedure Write(message: string; logType: TLogType);
16   end;
17 
18 implementation
19 
20 end.

Abaixo a implementação da nossa classe que será responsável por registrar os logs no Event Viewer do Windows. Não há muito que comentar do código apenas que estou utilizando um arquivo **.ini, localizado no path da aplicação para guardar as configurações do sistema e que não estou usando *Strategy Pattern**, vou deixar o código da forma que está para refatorar ele e explicar melhor este Pattern em outro artigo.

 1 unit LoggerEventViewer;
 2 
 3 interface
 4 
 5 uses
 6   Forms, IniFiles, SysUtils, SvcMgr, Windows, Logger, LogType;
 7 
 8 type
 9   TLoggerEventViewer = class(TInterfacedObject, ILogger)
10   public
11     { Public declarations }
12     procedure Write(message: string; logType: TLogType);
13 
14   end;
15 
16 implementation
17 
18 { TEventView }
19 
20 procedure TLoggerEventViewer.Write(message: string; logType: TLogType);
21 var
22   iniFiles: TIniFile;
23   eventLogger: TEventLogger;
24 
25 begin
26   iniFiles := TIniFile.Create(Format('%s%s', [ExtractFilePath(Forms.Application.ExeName), 'Config.ini']));
27   eventLogger := TEventLogger.Create(iniFiles.ReadString('Application', 'LoggerEventSource', ''));
28   try
29     case logType of
30       TLogType.Error:
31         eventLogger.LogMessage(message, EVENTLOG_ERROR_TYPE, 0, 0);
32 
33       TLogType.Warning:
34         eventLogger.LogMessage(message, EVENTLOG_WARNING_TYPE, 0, 0);
35 
36       TLogType.Information:
37         eventLogger.LogMessage(message, EVENTLOG_INFORMATION_TYPE, 0, 0);
38     end;
39   finally
40     iniFiles.Free();
41     FreeAndNil(eventLogger);
42   end;
43 end;
44 
45 end.

Abaixo a implementação da nossa classe que será responsável por registrar os logs em arquivo texto. A única consideração é que estou utilizando o método WriteAllText da classe TFile contida na nova unit IOUtils para IO (input/output) do Delphi, este método sempre sobrescreve o conteúdo do arquivo (não é recomendado o uso para realizar logs já que não grava a informação que já existia no arquivo, estou utilizando apenas como exemplo), também verifica se existe criado no disco caso não existe ele já cria automaticamente.

 1 unit LoggerFileText;
 2 
 3 interface
 4 
 5 uses
 6   Forms, IniFiles, IOUtils, TypInfo, SysUtils, Logger, LogType;
 7 
 8 type
 9   TLoggerFileText = class(TInterfacedObject, ILogger)
10   public
11     { Public declarations }
12     procedure Write(message: string; logType: TLogType);
13 
14   end;
15 
16 implementation
17 
18 { TEventView }
19 
20 procedure TLoggerFileText.Write(message: string; logType: TLogType);
21 var
22   iniFile: TIniFile;
23 
24 begin
25   message := Format('%s - %s', [GetEnumName(TypeInfo(TLogType), Integer(logType)), message]);
26   iniFile := TIniFile.Create(Format('%s%s', [ExtractFilePath(Application.ExeName), 'Config.ini']));
27   try
28     TFile.WriteAllText(iniFile.ReadString('Application', 'LoggerFilePath', ''), message);
29   finally
30     iniFile.Free();
31   end;
32 end;
33 
34 end.

Caso queiram gravar os logs também em banco de dados ou deixar já uma classe que permita está ação, é necessário apenas implementar a forma de acesso e gravação no banco de dados dentro do método Write, vocês irão notar mais abaixo que a forma de utilização da classe não irá mudar.

 1 unit LoggerDatabase;
 2 
 3 interface
 4 
 5 uses
 6   Logger, LogType;
 7 
 8 type
 9   TLoggerDatabase = class(TInterfacedObject, ILogger)
10   public
11     { Public declarations }
12     procedure Write(message: string; logType: TLogType);
13 
14   end;
15 
16 implementation
17 
18 { TEventView }
19 
20 procedure TLoggerDatabase.Write(message: string; logType: TLogType);
21 begin
22   { Implementar a forma de acesso e gravacao na base de dados. }
23 end;
24 
25 end.

Abaixo o código da nossa classe que será responsável por gerenciar os logs da aplicação. Note que existem dois métodos Create, um está recebendo uma variável ILogger que é a nossa interface que contém os métodos responsáveis por fazer o log da aplicação, este é um exemplo de IoC. E o outro método Create irá chamar o método descrito acima passando a forma de autenticação que será utilizada na aplicação por padrão, este é um exemplo de DI. Da forma que foi desenvolvida está classe fica fácil usar um Container de Dependências que será assuntos de outro post.

 1 unit LogManager;
 2 
 3 interface
 4 
 5 uses
 6   Logger, LogType;
 7 
 8 type
 9   TLogManager = class
10   private
11     { Private declarations}
12     _logger: ILogger;
13 
14   public
15     { Public declarations }
16 
17     /// <summary>
18     ///
19     /// </summary>
20     /// <param name="logger"></param>
21     constructor Create(const logger: ILogger); overload;
22 
23     /// <summary>
24     ///
25     /// </summary>
26     constructor Create(); overload;
27 
28     /// <summary>
29     /// Registra os logs do tipo informacao.
30     /// </summary>
31     /// <param name="message">Informacao que sera registrada.</param>
32     procedure WriteInfo(message: string);
33 
34     /// <summary>
35     /// Registra os logs do tipo atencao.
36     /// </summary>
37     /// <param name="message">Mensagem de atencao que sera registrada.</param>
38     procedure WriteWarning(message: string);
39 
40     /// <summary>
41     /// Registra os logs do tipo erro.
42     /// </summary>
43     /// <param name="message">Erro que sera registrado.</param>
44     procedure WriteError(message: string);
45 
46   end;
47 
48 implementation
49 
50 uses
51   LoggerEventViewer, LoggerFileText;
52 
53 { TLogManager }
54 
55 constructor TLogManager.Create(const logger: ILogger);
56 begin
57   _logger := logger;
58 end;
59 
60 constructor TLogManager.Create();
61 begin
62   Create(TLoggerFileText.Create());
63 end;
64 
65 procedure TLogManager.WriteInfo(message: string);
66 begin
67   _logger.Write(message, TLogType.Information);
68 end;
69 
70 procedure TLogManager.WriteWarning(message: string);
71 begin
72   _logger.Write(message, TLogType.Warning);
73 end;
74 
75 procedure TLogManager.WriteError(message: string);
76 begin
77   _logger.Write(message, TLogType.Error);
78 end;
79 
80 end.

Para utilizar as nossas classes de log é necessário antes colocar no uses as units abaixo.

1 uses
2   Logger, LogManager, LoggerEventViewer;

Existem duas formas de utilizar a nossa classe LogManager. A primeira forma, é apenas instanciar o nosso objeto logManager do tipo TLogManager sem passar nenhum objeto no construtor, sendo assim a nossa classe será responsável por instanciar a variável do tipo ILogger com a classe padrão de realizar logs do sistema. Segue abaixo um exemplo.

 1 var
 2   logManager: TLogManager;
 3 
 4 begin
 5   logManager := TLogManager.Create();
 6   try
 7     logManager.WriteInfo('Mensagem de info do projeto de estudo!');
 8     logManager.WriteWarning('Mensagem de warning do projeto de estudo!');
 9     logManager.WriteError('Mensagem de error do projeto de estudo!');
10   finally
11     FreeAndNil(logManager);
12   end;
13 end;

A segunda forma é criar uma variável do tipo ILogger e depois instanciar este objeto com a classe na qual deseja realizar o log da aplicação, e passar como parametro no construtor do método Create da classe TLogManager. Note que a forma de utilização é a mesma para todos os casos, o que altera é apenas a classe que irá instanciar o nosso objeto, com isto, mudar a forma que se realiza o log e a manutenção do código se torna muito mais simples e fácil. Segue abaixo um exemplo.

 1 var
 2   logger: ILogger;
 3   logManager: TLogManager;
 4 
 5 begin
 6   logger := TLoggerEventViewer.Create();
 7   logManager := TLogManager.Create(logger);
 8   try
 9     logManager.WriteInfo('Mensagem de info do projeto de estudo!');
10     logManager.WriteWarning('Mensagem de warning do projeto de estudo!');
11     logManager.WriteError('Mensagem de error do projeto de estudo!');
12   finally
13     FreeAndNil(logManager);
14   end;
15 end;

Abaixo as configurações do nosso arquivo de configuração Config.ini localizado junto ao executável do projeto.

1 [Application]
2 //Caminho que sera gravado o arquivo de log.
3 LoggerFilePath='C:\EstudoDelphi.txt'
4 
5 //Nome da fonte do Event Viewer.
6 LoggerEventSource='EstudoDelphi'

Para abrir o Event Viewer do Windows, basta ir no Executar e digitar: eventvwr.msc. A imagem abaixo mostrar como ficará o log registrado. Para melhor visualização clique na imagem.

print

Se quiserem o código fonte do exemplo, é só clicar aqui. Espero que tenham gostado deste artigo. Agradeço a visita.