At times I needed a solution to something similar to your problem, not as big as but good-looking!
The solution however was to use several threads to solve, as I am not a Delphi developer I had to buy a system and pay for its maintenance!
I've been posting the answer since I edited this question because I was looking for an old friend who solved the problem!
Follow the implementations he did for me!
Unit unt_funcaoLog
type
{Classe responsável pela geração de LOG!}
TGeraLog = Class
private
FCritical : TCriticalSection;
Const C_ARQUIVO = '.\arquivo_log.txt';
{Indica o arquivo em que o LOG será gerado, no caso, no mesmo diretório do executável!}
Class var FInstance : TGeraLog;
{Instância única do objeto!}
Class function GetInstancia: TGeraLog; static;
{Método responsável por instanciar o objeto singleton!}
public
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
procedure GeraLog(Const AReferencia: String; Const AData: TDateTime; Const ATextoLog: String);
{AReferencia..: Referência à Thread chamadora.}
{AData........: Data e Hora da geração do LOG.}
{ATextoLog....: Texto a ser escrito no arquivo de LOG.}
function TryGeraLog(Const AReferencia: String; Const AData: TDateTime; Const ATextoLog: String): Boolean;
{Método que TENTA gerar uma nova linha de LOG!}
{AReferencia..: Referência à Thread chamadora.}
{AData........: Data e Hora da geração do LOG.}
{ATextoLog....: Texto a ser escrito no arquivo de LOG.}
Class property Instancia: TGeraLog read GetInstancia;
{Referência à instância singleton!}
end;
implementation
{ TGeraLog }
procedure TGeraLog.AfterConstruction;
begin
inherited;
DeleteFile(Self.C_ARQUIVO);
Self.FCritical := TCriticalSection.Create;
end;
procedure TGeraLog.BeforeDestruction;
begin
inherited;
Self.FCritical.Free;
end;
procedure TGeraLog.GeraLog(const AReferencia: string; const AData: TDateTime; const ATextoLog: string);
var
_arquivo : TextFile;
sNovaLinha : String;
begin
sNovaLinha := Format('%s|%s|%s', [AReferencia, DateTimeToStr(AData), ATextoLog]);
{Entra na seção crítica!}
Self.FCritical.Enter;
try
AssignFile(_arquivo, Self.C_ARQUIVO);
if (FileExists(Self.C_ARQUIVO)) then
begin
Append(_arquivo);
end
else
begin
Rewrite(_arquivo);
end;
Writeln(_arquivo, sNovaLinha);
CloseFile(_arquivo);
finally
{Sai da seção crítica}
Self.FCritical.Release;
end;
end;
Class function TGeraLog.GetInstancia: TGeraLog;
begin
if not(Assigned(FInstance)) then
begin
FInstance := TGeraLog.Create;
end;
Result := FInstance;
end;
function TGeraLog.TryGeraLog(const AReferencia: String; const AData: TDateTime;
const ATextoLog: String): Boolean;
var
_arquivo : TextFile;
sNovaLinha : String;
begin
sNovaLinha := Format('%s|%s|%s', [AReferencia, DateTimeToStr(AData), ATextoLog]);
{Tenta entrar na seção crítica!}
Result := Self.FCritical.TryEnter;
if (Result = True) then
begin
try
AssignFile(_arquivo, Self.C_ARQUIVO);
if (FileExists(Self.C_ARQUIVO)) then
begin
Append(_arquivo);
end
else
begin
Rewrite(_arquivo);
end;
Writeln(_arquivo, sNovaLinha);
CloseFile(_arquivo);
finally
{Sai da seção crítica}
Self.FCritical.Release;
end;
end;
end;
initialization
finalization
TGeraLog.Instancia.Free;
end.
Unit unt_diversasThreads
type
TDiversaThread = Class(TThread)
private
FReferencia : String;
FDescricaoErro : String;
public
procedure Execute; override;
{Rotina a ser executada pelo Thread que eventualmente gerará uma linha no arquivo de LOG!}
property Referencia: String read FReferencia write FReferencia;
{Referência que será escrito no arquivo de LOG para sabermos de onde veio a linha!}
property DescricaoErro: String read FDescricaoErro;
end;
implementation
{ TDiversasThread }
procedure TDiversaThread.Execute;
var
bGeraLog : Boolean;
begin
inherited;
try
{Loop enquanto o Thread não for finalizado!}
while not (Self.Terminated) do
begin
{Aqui definimos um time para diminuir o consumo de CPU}
Sleep(10);
{Sorteia um número e verifica se o resto da divisão por dois é zero!}
bGeraLog := (Random(1000000) mod 2) = 0;
if (bGeraLog = True) then
begin
{Chama o método de geração de LOG!}
TGeraLog.Instancia.GeraLog(Self.FReferencia, Now, 'O rato roeu a roupa do Rei de Roma');
end;
end;
except
on E: EInOutError do
begin
Self.FDescricaoErro := Format('Erro de I/O #%d - %s', [E.ErrorCode, SysErrorMessage(E.ErrorCode)]);
end;
on E: Exception do
begin
Self.FDescricaoErro := Format('(%s) - %s', [E.ClassName, E.Message]);
end;
end;
end;
end.
I'm writing logs to this call:
TGeraLog.Instancia.GeraLog('QUEM_ENVIOU_LOG', Now, 'TEXTO_QUE_DESEJA');
According to my friend the TryGeraLog function is more efficient when there is multiple access to the same file, but since it is not my case I only use GeraLog.