Autor Beitrag
MitschL
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 211

Win 98 SE, Win 2000
D5 Pers, D6 Pers und D7 Pro
BeitragVerfasst: Fr 22.04.05 17:51 
Ich grüße in die Runde!

Ich habe zum Thema Logdatei erstellen eine Variante, die ihren Focus auf Funktionen setzt, die ich als nützlich empfinde. Einerseits wollte ich auf das Erstellen einer Instanz verzichten und andererseits wollte ich schon einen Zusammenhang deutlich machen. Das Hinzufügen einer weiteren hier veröffentlichten Unit - nämlich der High-Performance-Counter (RDTSC) erleichterte für mich vielfach diverse Modifikationsansätze - vor allem in Bezug auf Zeitverbrauch. Außerdem habe ich mit einem variablen Puffer gearbeitet, der bei Überschreiten einer bestimmten Größe in die Datei schreibt, um häufige Logausgabe nicht zu stark ausbremsen zu lassen.

Hier erstmal die Unit; Erklärungen folgen:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
255:
256:
257:
258:
259:
260:
261:
262:
263:
264:
265:
266:
267:
268:
269:
270:
271:
272:
273:
274:
275:
276:
277:
278:
279:
280:
281:
282:
283:
284:
285:
286:
287:
288:
289:
290:
291:
292:
293:
294:
295:
296:
297:
298:
299:
300:
301:
302:
303:
304:
305:
306:
307:
308:
309:
310:
311:
312:
313:
314:
315:
316:
317:
318:
319:
320:
321:
322:
323:
324:
325:
326:
327:
328:
329:
330:
331:
332:
333:
334:
335:
336:
337:
338:
339:
340:
341:
342:
343:
344:
345:
346:
347:
348:
349:
350:
351:
352:
353:
unit ULog;

interface

Uses
{!}  UCounter, // TCounter-Klasse für genaue Zeit
  Windows,  // Counter
  classes,  // TFileStream
  forms,  // TApplication
  Sysutils; // FileExists + Counter;


//*********************************************************
//* Klasse: TLog()                                        *
//*********************************************************

type
  TLog = class( TObject )
    private
      _absturzfrei:   Boolean; // für korrekten Abschluß des Logging
      _debug:         Boolean; // erweiterte Ausgabe
{!}      _genauerTimer:  TCounter; // sehr genaue Zeitangabe
      _StartTime:    TDateTime;
{!}      _genaueZeit:    Boolean; // Nutzung von _genauerTimer
      _Daten:         String;
      _Pfad:          String;
      _BufferGroesse: Integer; // Größe des Zwischenspeichers bevor in Datei geschrieben wird


      procedure Schreibe_Datei();

      procedure Erweitere_LogDaten( InfoTyp, Datum, Text: String );

      function  Gib_Zeit(): String;

    public
      class function NewInstance(): TObject; override;

      procedure FreeInstance; override;

      // schreibe in Log-Datei mit Kennung I (Info)
      class procedure ILOG( Text: String );

      // schreibe in Log-Datei mit Kennung W (Warnung)
      class procedure WLOG( Text: String );

      // schreibe in Log-Datei mit Kennung E (Fehler)
      class procedure ELOG( Text: String );

      // schreibe in Log-Datei wie oben, wenn Log-Datei
      // im Debug-Modus angelegt wurde
      class procedure DILOG( Text: String );
      class procedure DWLOG( Text: String );
      class procedure DELOG( Text: String );

      // erstelle neue Log-Datei
      // Pfad muß Dateinamen in gültigem Verzeichnis darstellen oder leer sein
      class procedure OLOG( Pfad: String = ''; BufferGroesse: Integer = 100000;
        NeueDatei: Boolean = false; {!}GenaueZeit: Boolean = false );

      // erstelle neue Log-Datei im Debug-Modus
      class procedure DOLOG( Pfad: String; BufferGroesse: Integer = 100000;
        NeueDatei: Boolean = false; {!}GenaueZeit: Boolean = false );

      // schließe Log-Datei
      class procedure CLOG();
end;

Implementation

var
  GLogDatei: TLog = nil;


//*********************************************************
//* Methode: Schreibe_Datei()                             *
//*********************************************************

procedure TLog.Schreibe_Datei();

var
  datenstrom: TFileStream;

begin
  try
    if ( GLogDatei._Pfad <> '' ) AND ( GLogDatei._Daten <> '' ) then
    begin
      if not FileExists( GLogDatei._Pfad ) then
      begin
        datenstrom := TFileStream.Create( GLogDatei._Pfad, fmCreate );
        FileClose( datenstrom.Handle );
      end;

      datenstrom := TFileStream.Create( GLogDatei._Pfad,
        fmOpenReadWrite or fmShareDenyWrite );

      datenstrom.Seek( 0, soFromEnd );
      datenstrom.Write( PChar( GLogDatei._Daten )^, Length( GLogDatei._Daten ));
      FileClose( datenstrom.Handle );
    end;

  except
    // weiteres Logging verhindern
    GLogdatei.Free();
  end;
end;


//*********************************************************
//* Methode: Erweitere_LogDaten()                         *
//*********************************************************

procedure TLog.Erweitere_LogDaten( InfoTyp, Datum, Text: String );

begin
  GLogDatei._Daten := Format( '%s%s %s: %s'#13#10,
    [GLogDatei._Daten, InfoTyp, Datum, Text] );

  if Length( GLogDatei._Daten ) > GLogDatei._BufferGroesse then
  begin
    GLogDatei.Schreibe_Datei();
    GLogDatei._Daten := '';
  end;
end;


//*********************************************************
//* Methode: Gib_Zeit()                                   *
//*********************************************************

function TLog.Gib_Zeit(): String;

var
  zeit:     TTimeStamp;
  jetzt:     Double;
  jahr,
  monat,
  tag,
  stunde,
  minute,
  sekunde,
  millisek: Word;

begin
{!}  if GLogDatei._genaueZeit then
{!}  begin
{!}    jetzt := GLogDatei._genauerTimer.Stop();
{!}    GLogDatei._genauerTimer.Start();
{!}  end else
{!}  begin
    // Voraussetzung: Zeit läuft vorwärts
    jetzt := Now() - GLogDatei._StartTime;
{!}  end;

  zeit := DateTimeToTimeStamp( GLogDatei._StartTime );
  zeit.Time := zeit.Time + MSecsToTimeStamp( jetzt ).Time;
  zeit.Date := zeit.Date + MSecsToTimeStamp( jetzt ).Date;
  GLogDatei._StartTime := TimeStampToDateTime( zeit );

  DecodeDate( GLogDatei._StartTime, jahr, monat, tag );
  DecodeTime( GLogDatei._StartTime, stunde, minute, sekunde, millisek );

  Result := Format( '%d-%s-%s %s:%s:%s.%s', [jahr,
    Copy( Format( '0%d', [monat] ),     Length( IntToStr( monat ) ),     2 ),
    Copy( Format( '0%d', [tag] ),       Length( IntToStr( tag ) ),       2 ),
    Copy( Format( '0%d', [stunde] ),     Length( IntToStr( stunde ) ),   2 ),
    Copy( Format( '0%d', [minute] ),     Length( IntToStr( minute ) ),   2 ),
    Copy( Format( '0%d', [sekunde] ),   Length( IntToStr( sekunde ) ),   2 ),
    Copy( Format( '00%d', [millisek] ), Length( IntToStr( millisek ) ), 3 )] );
end;


//*********************************************************
//* Methode: NewInstance()                                *
//*********************************************************

class function TLog.NewInstance: TObject;

begin
  if not Assigned( GLogDatei ) then
    GLogDatei := TLog( inherited NewInstance );

  Result := GLogDatei;
end;


//*********************************************************
//* Methode: FreeInstance()                               *
//*********************************************************

procedure TLog.FreeInstance();

begin
  if not _absturzfrei then
    GLogDatei.CLOG();

  GLogDatei := nil;
  inherited FreeInstance;
end;


//*********************************************************
//* Methode: ILOG()                                       *
//*********************************************************

class procedure TLog.ILOG( Text: String );

begin
  if not assigned( GLogDatei ) then
    Exit;

  GLogDatei.Erweitere_LogDaten( 'I', GLogDatei.Gib_Zeit(), Text );
end;


//*********************************************************
//* Methode: WLOG()                                       *
//*********************************************************

class procedure TLog.WLOG( Text: String );

begin
  if not assigned( GLogDatei ) then
    Exit;

  GLogDatei.Erweitere_LogDaten( 'W', GLogDatei.Gib_Zeit(), Text );
end;


//*********************************************************
//* Methode: ELOG()                                       *
//*********************************************************

class procedure TLog.ELOG( Text: String );

begin
  if not assigned( GLogDatei ) then
    Exit;

  GLogDatei.Erweitere_LogDaten( 'E', GLogDatei.Gib_Zeit(), Text );
end;


//*********************************************************
//* Methode: DILOG()                                      *
//*********************************************************

class procedure TLog.DILOG( Text: String );

begin
  if GLogDatei._debug then
    TLog.ILOG( Text );
end;


//*********************************************************
//* Methode: DWLOG()                                      *
//*********************************************************

class procedure TLog.DWLOG( Text: String );

begin
  if GLogDatei._debug then
    TLog.WLOG( Text );
end;


//*********************************************************
//* Methode: DELOG()                                      *
//*********************************************************

class procedure TLog.DELOG( Text: String );

begin
  if GLogDatei._debug then
    TLog.ELOG( Text );
end;


//*********************************************************
//* Methode: OLOG()                                       *
//*********************************************************

class procedure TLog.OLOG( Pfad: String; BufferGroesse: Integer;
  NeueDatei: Boolean; {!}GenaueZeit: Boolean );

begin
  if assigned( GLogDatei ) then
    Exit;

  GLogDatei := TLog.Create();
  GLogDatei._debug := false;
  GLogDatei._Daten := '';
  GLogDatei._BufferGroesse := BufferGroesse;
{!}  GLogDatei._genaueZeit := GenaueZeit;

  if Length( trim( Pfad ) ) > 0 then
    GLogDatei._Pfad  := Pfad
  else
    GLogDatei._Pfad := ExtractFilePath( Application.EXEName ) + 'LogFile.log';

  if NeueDatei then
    DeleteFile( GLogDatei._Pfad );

  GLogDatei._StartTime := Now();

{!}  if GLogDatei._genaueZeit then
{!}  begin
{!}    GLogDatei._genauerTimer := TCounter.Create();
{!}    GLogDatei._genauerTimer.Start();
{!}  end;
  GLogDatei._absturzfrei := false;
  GLogDatei.ILOG( '--> Starte Logging <--' );
end;


//*********************************************************
//* Methode: DOLOG()                                      *
//*********************************************************

class procedure TLog.DOLOG( Pfad: String; BufferGroesse: Integer;
  NeueDatei: Boolean; {!}GenaueZeit: Boolean );

begin
  if assigned( GLogDatei ) then
    Exit;

  TLog.OLOG( Pfad, BufferGroesse, NeueDatei, {!}GenaueZeit );
  GLogDatei._debug := true;
end;


//*********************************************************
//* Methode: CLOG()                                       *
//*********************************************************

class procedure TLog.CLOG();

begin
  if not assigned( GLogDatei ) then
    Exit;

  GLogDatei.ILOG( '--> Logging beendet <--' );
  GLogDatei.Schreibe_Datei();
  GLogDatei._absturzfrei := true;

{!}  if GLogDatei._genaueZeit then
{!}    GLogDatei._genauerTimer.Free();

  GLogDatei.Free();
end;

end.


Benutzen tut man die Unit indem man beim Start TLog.OLOG(...) bzw. TLog.DOLOG(...) aufruft.
Alle Parameter können ignoriert werden, werden sie gesetzt, sollte man darauf achten, daß das Logfile in einem gültigen Verzeichnis angelegt werden kann (oder man läßt es weg und es wird 'logfile.log' im Programmverzeichnis angelegt) und die Puffer-Größe nicht übertrieben wird. Der Parameter NeueDatei lohnt sich hauptsächlich zu Debug-Zwecken oder wenn wirklich nur die zuletzt laufende Instanz wichtig ist. Und die genaue Zeit schlußendlich ermöglicht die Angabe auf wenige Tausendstel Sekunden genau - Hierfür wird die andere hier bekannte Unit gebraucht.
Nur wenn TLog.D-/OLOG aufgrufen worden, wird in das Logfile geschrieben. Ansonsten führen die weiteren Aufrufe nichts aus und können also stehen bleiben.
TLog.ILOG, TLog.ELOG und TLog.WLOG schließlich sind jene Aufrufe, die stets Information, Fehler oder Warnung in das Logfile schreiben. Ihre Debug-Pendants sind TLog.DILOG, TLog.DELOG und TLog.DWLOG. Während die einen also immer schreiben, schreiben die anderen nur im Debug-Modus.
Abschließen sollte man das Ganze natürlich noch mit TLog.CLOG, um die letzten Reste zu schreiben.

Ich habe in der Unit die Bereiche mit einem {!} markiert, die entfernt werden können, falls man auf die genaue Zeit verzichten möchte. Bei den Parametern ist natürlich darauf zu achten, daß das ',' bzw. ';' vor dem jeweiligen Parameter mit entfernt wird. :-)

Und natürlich wie gehabt: Wer Fehler findet oder andere Dinge lieber verbessert sehen möchte; nur frei heraus damit.

gegrüßt!
MitschL

_________________
"Bloßes Ignorieren ist noch keine Toleranz." (Theodor Fontane)