Napisałem sobie taką oto klasę:
unit Logs;
{==============================================================================}
{$mode objfpc}{$H+}
{==============================================================================}
interface
{==============================================================================}
uses
Classes, SysUtils;
{==============================================================================}
type
TPriority = (pLowest, pLow, pNormal, pHigh, pHighest);
type
TLogData = packed record
Time : String[20];
Msg : String[100];
Priority : TPriority;
end;
PLogRec = ^TLogRec;
TLogRec = record
Next : PlogRec;
Log : TLogData;
end;
{------------------------------------------------------------------------------}
type
TLogClass = class(TObject)
private
First : PLogRec;
Last : PLogRec;
FileName : String;
public
constructor Create;
destructor Destroy; override;
procedure Add(aMsg : String; aPriority : TPriority);
function GetFirst : PLogRec;
procedure Clear;
procedure LoadFromFile(aFileName : String);
procedure SaveToFile(aFileName : String); overload;
procedure SaveToFile; overload;
end;
{==============================================================================}
implementation
{==============================================================================}
constructor TLogClass.Create;
begin
inherited Create;
FileName := StringReplace(DateTimeToStr(now), '-', '.', [rfReplaceAll]);
FileName := StringReplace(FileName, ':', '-', [rfReplaceAll]);
FileName := FileName + '.log';
First := nil;
Last := nil;
end;
{------------------------------------------------------------------------------}
destructor TLogClass.Destroy;
var
ToDelete : PLogRec;
begin
while First <> nil do
begin
ToDelete := First;
First := First^.Next;
Dispose(ToDelete);
end;
inherited Destroy;
end;
{------------------------------------------------------------------------------}
procedure TLogClass.Add(aMsg : String; aPriority : TPriority);
var
NewLog : PLogRec;
begin
New(NewLog);
NewLog^.Log.Msg := aMsg;
NewLog^.Log.Priority := aPriority;
NewLog^.Log.Time := DateTimeToStr(Now);
NewLog^.Next := nil;
if First = nil then
begin
First := NewLog;
Last := First;
end
else
begin
Last^.Next := NewLog;
Last := NewLog;
end;
end;
{------------------------------------------------------------------------------}
function TLogClass.GetFirst : PLogRec;
begin
result := First;
end;
{------------------------------------------------------------------------------}
procedure TLogClass.Clear;
var
ToDelete : PLogRec;
begin
while First <> nil do
begin
ToDelete := First;
First := First^.Next;
Dispose(ToDelete);
end;
inherited Destroy;
end;
{------------------------------------------------------------------------------}
procedure TLogClass.LoadFromFile(aFileName : String);
var
LogFile : File of TLogData;
NewLog : PLogRec;
begin
AssignFile(LogFile, aFileName);
Reset(LogFile);
while not Eof(LogFile) do
begin
New(NewLog);
Read(LogFile, NewLog^.Log);
NewLog^.Next := nil;
if First = nil then
begin
First := NewLog;
Last := First;
end
else
begin
Last^.Next := NewLog;
Last := NewLog;
end;
end;
end;
{------------------------------------------------------------------------------}
procedure TLogClass.SaveToFile(aFileName : String); overload;
var
ToSave : PLogRec;
LogFile : File of TLogData;
begin
AssignFile(LogFile, aFileName);
Rewrite(LogFile);
ToSave := GetFirst;
while ToSave <> nil do
begin
Write(LogFile, ToSave^.Log);
ToSave := ToSave^.Next
end;;
CloseFile(LogFile);
end;
{------------------------------------------------------------------------------}
procedure TLogClass.SaveToFile; overload;
var
ToSave : PLogRec;
LogFile : File of TLogData;
begin
AssignFile(LogFile, FileName);
Rewrite(LogFile);
ToSave := GetFirst;
while ToSave <> nil do
begin
Write(LogFile, ToSave^.Log);
ToSave := ToSave^.Next
end;
CloseFile(LogFile)
end;
{==============================================================================}
end.
I śmiga ona całkiem nieźle jednak mam 2 problemy:
-
Ma ona ograniczoną pojemność, mieści niecałe 13 000 000 logów. Czy jest możliwe zwiększenie pojemności tej listy? Wiem że mogę zmienić długość wiadomości albo datę jakoś normalniej zapisywać ale nie da to aż tak dużego przyrostu pojemności jaki bym sobie życzył. Wiem również, że zmiana kompilatora z 32-bitowego na 64-bitowy też by pomogła bo po prostu bym miał więcej pamięci ale tego robić nie chcę. Na razie jedyne co wymyśliłem to gdy lista się przepełni i wyskoczy wyjątek o braku pamięci to w obsłudze wyjątku zapisać listę do pliku, następnie ją wyczyścić i lecieć zapisywanie od początku.
-
Napisałem też programik do przeglądania logów zapisanych do pliku. Wygląda on tak:
unit Main;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, Grids,
StdCtrls, Logs;
type
{ TForm1 }
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
OpenDialog1: TOpenDialog;
StringGrid1: TStringGrid;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormResize(Sender: TObject);
private
{ private declarations }
public
{ public declarations }
end;
var
Form1 : TForm1;
Log : TLogClass;
FileName : String;
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.FormResize(Sender: TObject);
begin
StringGrid1.Height := Form1.Height - Button1.Height * 2;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
tmpLog : PLogRec;
i : Word;
begin
If OpenDialog1.Execute Then
begin
Log.LoadFromFile(OpenDialog1.FileName);
i := 1;
tmpLog := Log.GetFirst;
while tmpLog <> nil do
begin
StringGrid1.RowCount := StringGrid1.RowCount + 1;
StringGrid1.Cells[0,i] := IntToStr(i);
StringGrid1.Cells[1,i] := tmpLog^.Log.Msg;
StringGrid1.Cells[2,i] := tmpLog^.Log.Time;
case tmpLog^.Log.Priority of
pLowest : StringGrid1.Cells[3,i] := 'LOWEST';
pLow : StringGrid1.Cells[3,i] := 'LOW';
pNormal : StringGrid1.Cells[3,i] := 'NORMAL';
pHigh : StringGrid1.Cells[3,i] := 'HIGH';
pHighest : StringGrid1.Cells[3,i] := 'HIGHEST';
end;
tmpLog := tmpLog^.Next;
Inc(i);
end;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
FreeAndNil(Log);
Form1.Close;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Log := TLogClass.Create;
end;
end.
Z tym programem jest ten problem, że po wczytaniu za dużej ilości logów StringGrid nie może ich wyświetlić. W sensie tak jak by się zawiesił lub nie odświeżył. Jak to obejść bym mógł płynnie przeglądać nawet duże ilości logów? Myślałem nad wczytywaniem porcjami po np. 1000 logów jednak wtedy bym miał problem z cofaniem się. Bo jak wczytam 1000 logów i nie zamknę pliku tylko od razu to wyświetlę to jak dojdę do końca wyczyszczę StringGrida i wczytam kolejny 1000 to jeszcze spoko ale jak bym się chciał cofnąć to już może być problem. Mam pewien pomysł jak ten problem rozwiązać ale najpierw zapytam się czy sama idea jest dobra.