Newsgroup: it.comp.lang.delphi
Titolo: Logging che passione... <g>

Ciao a tutti,
Questa è una piccola unit che permette di avere un file di log "al volo".
Una versione "estesa" la metterò su CodeCentral, adesso la
posto qui per come è ora:
unit uLog;
interface
Type
  TLogger = class
  private
    FFolderName: String;
    FFileName: String;
    FLogFile : TextFile;
  protected
    function GenerateFileName : String;virtual;
  public
    constructor Create( FolderName : String );reintroduce;overload;virtual;
    procedure LogMessage( Msg : String );
    destructor Destroy;override;
    property FileName : String read FFileName;
    property FolderName : String read FFolderName;
  end;
function Logger : TLogger;
implementation
uses Windows;
var _Logger : TLogger;
function Logger : TLogger;
begin
  Result := _Logger;
end;
{ TLogger }
constructor TLogger.Create(FolderName: String);
begin
  Create;
  if Assigned( _Logger ) then Fail
  else begin
         FFolderName := FolderName;
         FFileName := GenerateFileName;
         AssignFile( FLogFile,FFileName );
         Rewrite( FLogFile );
       end;
end;
destructor TLogger.Destroy;
begin
  CloseFile( FLogFile );
  inherited;
end;
function TLogger.GenerateFileName: String;
var FName : PChar;
begin
  GetMem( FName,255 );
  GetTempFileName( PChar( FFolderName ),nil,0,FName );
  Result := FName;
  FreeMem( FName );
end;
procedure TLogger.LogMessage(Msg: String);
begin
  WriteLn( FLogFile,Msg );
end;
initialization
  _Logger := TLogger.Create( 'C:\Documents and
Settings\andrea\Desktop\EventRegLogs' );
finalization
  _Logger.Free;
end.
Ovviamente dovrete cambiare la directory nella sezione "initialization".
Ah, tra l'altro è un esempio funzionante del pattern "singleton".
Andrea

Ah, tra l'altro è un esempio funzionante del pattern "singleton".
O_o
Ho l'impressione che tu stia usando il gruppo per farti "bello",
perchè queste cose non te le posti su un *tuo* sito web?
-- 
"La velocità luce non è sufficiente Comandante Nunziatella, 
dobbiamo passare alla velocità smodata!"

 perchè queste cose non te le posti su un *tuo* sito web?
Eh, quando sara' pronto volentieri!
Gh, cmq è vero alcune volte perdo proprio il filo, parto con una idea ed
arrivo con un'altra... nello specifico, prima avevo pensato di fare una cosa
piu'
"elastica", ma poi ho realizzato che perdevo meno tempo se lo strutturavo
come la
funzione Printer, che ha un approccio di questo tipo( funzione che
restituisce un oggetto
interno ).
Ciao,
Andrea

The DeerBear scriveva fra l'altro:
[...]
Nel constructor c'e' un Fail che non e' definito; inoltre, chiama Create
prima di controllare se _Logger e' gia' stato creato. Se il costructor
lo lasci protetto ti eviti un po' di problemi.
Non controlli il risultato di GetMem ne' di GetTempFileName (anche le
funzioni di sistema a volte falliscono).
Personalmente poi preferisco avere l'oggetto allocato solo se viene
chiamato =>
function Logger: TLogger;
begin
  if _Logger = nil then
    _Logger := TLogger.Create(LogPath);
  Result := _Logger;
end;
nella finalization si controlla se esiste _Logger prima di chiamarne
Free.
Infine, il file rimane aperto sino alla fine dell'applicazione e non
c'e' un limite superiore alle sue dimensioni (non si puo' aprire il file
senza chiudere l'app, il che non e' sempre quel che si vuole).
Normalmente uso qualcosa tipo questa funzione (che includo in +/- tutte
le applicazioni)
procedure HardLog(src: string); overload;
var F: TextFile;
    FSize: Cardinal;
    fn: TFilename;
    srcApp: string;
begin
  fn := vRootDir + cstrLogName;
  try
    srcApp := ChangeFileExt(ExtractFileName(ParamStr(0)), '');
    AssignFile(F, fn);
    try
      FSize := 0;
      if FileExists(fn) then
        FSize := FileGetSize(fn);
      if (not FileExists(fn)) or (FSize > 1048576) then
        Rewrite(F)
      else
        Append(F);
      writeln(F, FormatDateTime('yyyy/mm/dd hh:nn:ss.zzz - ', Now) +
srcApp +
        ' - ' + src);
    finally
      CloseFile(F);
    end;
  except
    ; // nessuna eccezione deve uscire di qui
  end;
end;
Saluti
Paolo Arosio
-- 
"La dimostrazione dell'esistenza di specie intelligenti nell'universo
 e' che non ci vengono a trovare"

 Paolo Arosio
Andrea

come la
Ma questo succede a tutti (almeno a me) spesso e volentieri :)
Pero' mi chiedo perchè postare su un NG un codice sballato per di piu'
non richiesto?
Tutto qui ;-)
P.S.
Alcuni consigli se vuoi fare dei log.
1) i nomi dei file di log è utile che siano facilmente riconoscibili -
es 20031107.txt
2) Al logger si passa il messaggio e poi il logger stesso si deve
occupare di formattare il messaggio con informazioni extra come data
ora e gravità dell'evento.
3) Il logger deve essere totalmente "fault tolerant", pensa che
schifezza se un programma si pianta proprio perchè il logger va in
errore. Quindi devi gestire le eccezioni sull' I/O, cosa che non fai
4) Sarebbe utile che la classe si occupasse pure di eliminare i log
piu' vecchi di N giorni
Mi fermo perche' sto diventando lungo e pedante :p


 piu' vecchi di N giorni
Beh se faccio qualcosa di piu' complesso, lo vendo! <g>
Andrea

 la 6 ).
Vero, come l'hai scovato? ma soprattutto, cosa si suppone che faccia?


 ma soprattutto, cosa si suppone che faccia?
Fa "fallire" la costruzione dell'oggetto.
Tu, in teoria se le cose non sono cambiate( non ho provato a vedere se si
puo' ancora fare ),
dovresti poter scrivere una cosa di questo tipo :
if TMioOggetto.Create then
in questo caso "Create" puo' essere usato come una
funzione booleana per verificare se la costruzione è andata a buon fine.
Era comodissima, appunto, nel TP perché ti consentiva di sapere "dove
stavi". :-)
Se non ricordo male, comunque, ormai la Fail dovrebbe essere
implementata con una RunError e non piu' al modo "antico" :-)


 implementata con una RunError e non piu' al modo "antico" :-)
A proposito di RunError, i "vecchi" sicuramente si ricorderanno che in TP
veniva usata per segnare i metodi come astratti( RunError( 211 ); ) :-)
Eh, vecchi ricordi... ^_^
Andrea

rompendo le palle di questa cosa, ok?
Fai quello che vuoi, io  plonko solo i troll (cosa che tu non sei), ma
se leggo certe schifezze non lo mando certo a dire.
E qui chiudo e locco il thread.


 Mi fermo perche' sto diventando lungo e pedante :p
Aggiungerei la possibilità di avere più "livelli" di logging, in modo ad
esempio da far scrivere solo le eccezioni oppure tutto il dettaglio
Io sto pensando di fare proprio una cosa del genere... :-) 
Ah, a proposito, non c'è la possibilità di sapere il nome della procedura
che ha appena chiamato un'altra procedura?
-- 
Lis
ICQ@Home #7317603sette     ICQ@Work #16285702tre
Mercatino: 
http://snurl.com/sito_lis

 procedura che ha appena chiamato un'altra procedura?
Senti a me, lascia stare <g>
Dovresti competere con MadExcept di Mathias Rauen e,
credimi, è dura :-)
www.madshi.net
 tanto per capirci :-)
Ciao,
Andrea

    constructor Create( FolderName : String );reintroduce;overload;virtual;
Dio mio!...

 
Lo conosco (di fama, non l'ho ancora provato), ma a me serve semplicemente
per fare in modo che il logging, quando richiesto, scriva automaticamente il
nome del form e procedura da cui arriva il messaggio, per non doverlo
scrivere io a mano:
per esempio con una   WriteDebugMsg('pippo') vorrei ritrovare sul log
"TFrmMain.FormCreate: Pippo".
Al momento invece devo aggiungere io "TFrmMain.FormCreate: " al messaggio
che voglio scrivere
