Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2004.06.06;
Скачать: CL | DM;

Вниз

Покритикуйте код   Найти похожие ветки 

 
Романов Р.В. ©   (2004-05-19 14:29) [0]

type
 TErrorCategory = (ecCriticalError, ecInformation, ecErrorWithContinue,
   ecErrorWithCancel);
 TErrorCategories = array[TErrorCategory] of string;

const
 ErrCategory: TErrorCategories = ("Критическая ошибка", "Информация",
   "Ошибка с продолжением работы", "Ошибка с отменой операции");

type
 TErrorInfo = record
   Time: TDateTime;
   Place, Text: string;
   Category: TErrorCategory;
 end;

 TErrorsLog = class
 private
   FErrors: array of TErrorInfo;
   FCount: Integer;
   FLastErrorCount: Integer;
   FFileName: string;
   FEnabled: Boolean;
   function GetError(Index: Integer): TErrorInfo;
 public
   constructor Create;
   destructor Destroy; override;
   procedure Add(APlace, AText: string; ACategory: TErrorCategory);
   procedure Clear;
   procedure SaveToFile(FName: string; AClear: Boolean = True);
   property Error[Index: Integer]: TErrorInfo read GetError;
   property FileName: string read FFileName write FFileName;
   property Enabled: Boolean read FEnabled write FEnabled;
   property Count: Integer read FCount;
 end;

{ TErrorsLog }

procedure TErrorsLog.Add(APlace, AText: string; ACategory: TErrorCategory);
var
 i: Integer;
begin
 if not FEnabled then
   Exit;
 i := Length(FErrors);
 if FCount >= i then
   SetLength(FErrors, Max(i + 100, Trunc(i * 1.1)));
 with FErrors[FCount] do
 begin
   Time := Now();
   Place := APlace;
   Text := AText;
   Category := ACategory;
 end;
 Inc(FCount);
 Inc(FLastErrorCount);
end;

procedure TErrorsLog.Clear;
begin
 SetLength(FErrors, 0);
 FCount := 0;
 FLastErrorCount := 0;
end;

constructor TErrorsLog.Create;
begin
 FFileName := "";
 FEnabled := True;
 Clear;
end;

destructor TErrorsLog.Destroy;
begin
 if FFileName <> "" then
   SaveToFile(FFileName, False);
 SetLength(FErrors, 0);
 inherited;
end;

function TErrorsLog.GetError(Index: Integer): TErrorInfo;
begin
 if (0 <= Index) and (Index < FCount) then
   Result := FErrors[Index]
 else
   Result.Time := 0;
end;

procedure TErrorsLog.SaveToFile(FName: string; AClear: Boolean = True);
var
 F: TextFile;
 i: Integer;
begin
 if not FEnabled then
   Exit;
 try
   AssignFile(F, FName);
   try
     Rewrite(F);
     for i := 0 to FCount - 1 do
       Writeln(F, Format("%s; %s; %s; %s", [
         FormatDateTime("dd.mm.yyyy hh:nn:zzz", FErrors[i].Time),
           ErrCategory[FErrors[i].Category],
           FErrors[i].Place, FErrors[i].Text]));
     if AClear then
       Clear;
   finally
     CloseFile(F);
   end;
 except
 end;
end;


 
Igorek ©   (2004-05-19 14:42) [1]


> procedure Add...

Лучше Add(something)


>    constructor Create;
>    destructor Destroy; override;

После конст/дест-рукторов я обычно пустую строку ставлю.


> var
>  i: Integer;

I лучше.

>  if FCount >= i then
>    SetLength(FErrors, Max(i + 100, Trunc(i * 1.1)));

Сложные выражения я предпочитаю писать так:

SetLength(
   FErrors,
   Max(
       i + 100,
       Trunc(i * 1.1)
   )
);


> destructor TErrorsLog.Destroy;
> begin
>  if FFileName <> "" then
>    SaveToFile(FFileName, False);
>  SetLength(FErrors, 0);
>  inherited;
> end;

destructor TErrorsLog.Destroy;
begin
 inherited
 ...
end;


>  try
>    AssignFile(F, FName);
>...
>  except
>  end;

Зачем давить ошибки?


 
Игорь Шевченко ©   (2004-05-19 14:42) [2]

Критикую:

try
......
except
end;

Давить в корне.


>  if FCount >= i then
>    SetLength(FErrors, Max(i + 100, Trunc(i * 1.1)));


Смысл такой непонятной конструкции в чем ?


> destructor TErrorsLog.Destroy;
> begin
>  if FFileName <> "" then
>    SaveToFile(FFileName, False);
>  SetLength(FErrors, 0);
>  inherited;
> end;


Может, вместо SetLength(FErrors, 0); вызывать метод Clear ?


 
vuk ©   (2004-05-19 14:53) [3]

Я бы вообще сразу в файл писал...


 
Романов Р.В. ©   (2004-05-19 14:54) [4]


> > procedure Add...
>
> Лучше Add(something)

Не понял... Вместо нескольких параметров использовать рекорд?
> destructor TErrorsLog.Destroy;
> begin
>  inherited
>  ...
> end;

 Как раз наоборот.

Зачем давить ошибки?
Ну это как бы компонент для обработки ошибок и сам он по идее не должен выдавать ошибки. А то может быть зацикливание если работать с ним в Application.OnException.

>  if FCount >= i then
>    SetLength(FErrors, Max(i + 100, Trunc(i * 1.1)));

Смысл такой непонятной конструкции в чем ?

Не увеличивать каждый раз массив на 1, а на 100 элементов или на 10%.


> Может, вместо SetLength(FErrors, 0); вызывать метод Clear
> ?

Помоему тут можно вообще ничего не вызывать


 
Романов Р.В. ©   (2004-05-19 14:58) [5]


> Я бы вообще сразу в файл писал


Я тоже писал. Но решил перейти на более высокий уровень организации логов


 
Игорь Шевченко ©   (2004-05-19 15:04) [6]


> Не увеличивать каждый раз массив на 1, а на 100 элементов
> или на 10%.


RTFS: TList.SetCapacity


> Помоему тут можно вообще ничего не вызывать


Спорный вопрос. Если метод .Clear сделать виртуальным, то следует вызывать, для того, чтобы наследники смогли сделать свою специфическую ошибку. RTFS: TList.Destroy


> Зачем давить ошибки?
> Ну это как бы компонент для обработки ошибок и сам он по
> идее не должен выдавать ошибки. А то может быть зацикливание
> если работать с ним в Application.OnException


Ну вот и надо добиваться, чтобы он не выдавал ошибок. А то ерунда получается - файл не смог создать (места на диске нету), а для пользователя получается, что все хорошо отработало.


 
Goida   (2004-05-19 15:16) [7]

Код плохой, а может и хороший... Если по другому посмотреть...


 
vuk ©   (2004-05-19 15:29) [8]

to оманов Р.В. ©   (19.05.04 14:58) [5]:
>Но решил перейти на более высокий уровень организации логов
Чем же он более высокий? Возможностью потери данных? :o)


 
Mystic ©   (2004-05-19 15:38) [9]

FErrors: array of TErrorInfo;
Напрашивается TList

Trunc(i * 1.1)
Лучше использовать целочисленную арифметику: (i div 10) * 11. А еще лучше просто умножить на 2.

destructor TErrorsLog.Destroy;
begin
if FFileName <> "" then
  SaveToFile(FFileName, False);
SetLength(FErrors, 0);
inherited;
end;

До этого можно не дожить.

try ... except end
Использовать нежелательно.

Пожелания:
1) Держать буфер ошибок и по мере его заполнения скидывать в файл. Предусмотреть Flush
2) Многопоточность
3) Формировать log-файл по имени EXE-файла по умолчанию
4) Создать один экземпляр ErrorLog по умолчанию.


 
вася   (2004-05-19 15:57) [10]

а чем плох try/except ?


 
Mystic ©   (2004-05-19 16:14) [11]

Что не прийдется голову ломать над тем, почему программа не пишет в log-файл. Особенно это приятно в чужом коде, когда программа не выдает ошибки, но и что задумано не делает. Желательно хотя бы

try
except
 ShowMessage(SErrorMsg);
end;


 
panov ©   (2004-05-19 21:52) [12]

Тоже выложит код для ведения журнала, что-ли... Может тоже покритикуют...


 
Игорь Шевченко ©   (2004-05-19 22:05) [13]

panov ©   (19.05.04 21:52)


> Может тоже покритикуют...


Эт завсегда... :)


 
panov ©   (2004-05-19 22:21) [14]

>Романов Р.В. ©   (19.05.04 14:29)
Можно здесь код привести тоже?
Там решены некоторые пожелания от Mystic ©   (19.05.04 15:38) [9]

1) Держать буфер ошибок и по мере его заполнения скидывать в файл. Предусмотреть Flush
2) Многопоточность
3) Формировать log-файл по имени EXE-файла по умолчанию


 
vuk ©   (2004-05-19 22:28) [15]

Без особых претензий на что-то.


unit SimpleLogger;

interface
uses
   SysUtils;

function WriteLog( const FileName, Message : string ) : boolean; overload;
function WriteLog( Message : string ) : boolean; overload;
function WriteLog( const FileName : string; const Fmt : string;
 const Args: array of const ) : boolean; overload;
function WriteLog( const Fmt : string;
 const Args : array of const ) : boolean; overload;
procedure SetDefaultLogFileName( const FileName: string );
procedure LockLogging;
procedure UnlockLogging;

var
 WriteLogTime: boolean = false;
 LogTimeFormat: string = "dd.mm.yyyy hh:nn:ss";

implementation
uses
   SyncObjs;

var
  LoggerSync : TCriticalSection;
  DefaultLog : string;

procedure LockLogging;
begin
 LoggerSync.Acquire;
end;

procedure UnlockLogging;
begin
 LoggerSync.Release;
end;

procedure SetDefaultLogFileName( const FileName: string );
begin
 LoggerSync.Acquire;
 try
   DefaultLog := FileName;
 finally
   LoggerSync.Release;
 end;
end;

function WriteLog( const FileName, Message : string ) : boolean;
var
 F : Text;
 s: string;
begin
 Result := true;

 LoggerSync.Acquire;
 try
   Assign( F, FileName );
   if FileExists( FileName ) then Append( F )
     else Rewrite( F );
   try
     if WriteLogTime then
     begin
       try
         s := FormatDateTime(LogTimeFormat, Now);

       except
         s := "";
       end;
       s := Format("%s %s", [s, Message]);
     end else
       s := Message;
     writeln( F, s );
   finally
     CloseFile( F );
   end;
 finally
   LoggerSync.Release;
 end;
end;

function WriteLog( Message : string ) : boolean;
begin
 Result := WriteLog( DefaultLog, Message );
end;

function WriteLog( const FileName : string; const Fmt : string;
 const Args: array of const ) : boolean;
begin
 Result := WriteLog( FileName, Format( Fmt, Args ));
end;

function WriteLog( const Fmt : string; const Args : array of const ) : boolean;
begin
 Result := WriteLog( DefaultLog, Fmt, Args );
end;

initialization
 LoggerSync := TCriticalSection.Create;
 DefaultLog := ChangeFileExt( ParamStr(0), ".log" );

finalization
 LoggerSync.Free;
end.



 
Игорь Шевченко ©   (2004-05-19 22:36) [16]

vuk ©   (19.05.04 22:28)

Я бы в класс свел :)


 
panov ©   (2004-05-19 22:37) [17]


 TLog = class(TThread)
 private
   fHandleList: THandle;     //Ссылка на корневой элемент списка сообщений
   fBuf: PChar;              //Ссылка на буфер для записи журнала
   cs: RTL_CRITICAL_SECTION; //критическая секция для защиты от одновременной
                             //записи из нескольких потоков(TLog.Write)
   fSemaphore: THandle;      //Семафор для ожидания в TLog.Execute
   fName: String;            //Файл журнала
   fHandle: Integer;         //Дескриптор файла
   fMaxSize: Integer;        //Максимальный размер журнала(Кб)
   fMaxQueue: Integer;       //Максимальный размер очереди незаписанных
                             //на диск сообщений(шт.) (РЕЗЕРВ)
   fArchDir: String;         //Каталог для архивных журналов
                             //Каталог запуска программы+"ArchLog\"
   fNumArch: Integer;
   procedure NewMsg(const aMsg: String); //Постановка в очередь нового сообщения
   procedure DeleteMsg(const p:Pointer); //Удаление из очереди сообщения
   procedure SaveLog;        //Запись строки в журнал
   function Open: Boolean;   //Создание и открытие файла журнала
   procedure Archive;         //Перенос активного журнала в архив
   procedure Close;          //Закрытие файла журнала
 public
   constructor Create(const aPathArchive: String;const aMaxSize:Integer);
   destructor Destroy; override;
   procedure Execute; override;
   procedure Write(s: String); //Процедура для записи в журнал
                               //Доступ из других потоков
   property Semaphore: THandle read fSemaphore; //(РЕЗЕРВ)
 end;

implementation

function pInc(const p:Pointer;const incr: Integer):Pointer;
begin
 Result := Pointer(Integer(p)+incr);
end;

function pDec(const p:Pointer;const decr: Integer):Pointer;
begin
 Result := Pointer(Integer(p)-decr);
end;

function GetValue(const p: Pointer):Pointer;
begin
 Result := Pointer(Integer(p^));
end;

function GetLenMsg(const p: Pointer):Integer;
begin
 Result := Integer(GetValue(pInc(p,8)));
end;

function GetNextAddrMsg(const p: Pointer):Pointer;
begin
 Result := GetValue(pInc(p,4));
end;

function GetPrevAddrMsg(const p: Pointer):Pointer;
begin
 Result := GetValue(p);
end;

function GetRootMsg(const p: Pointer): Pointer;
begin
 Result := p;
 while GetValue(Result)<>nil do Result := GetPrevAddrMsg(Result);
end;

function GetCountMsg(const aHandle: THandle): Integer;
var
 p: Pointer;
begin
 p := Pointer(aHandle);
 Result := Integer(GetValue(pInc(p,8)));
 Exit;
 while p<>nil do
 begin
   Inc(Result);
   p := GetValue(pInc(p,4));
 end;
end;

procedure SetValue(const pAddr,pDest: Pointer);
begin
 Integer(pDest^) := Integer(pAddr);
end;

procedure mv(const aSrc,aDest: Pointer;const Len: Integer);
begin
 Move(aSrc^,aDest^,Len);
end;

function MemComp(const p1,p2:Pointer;const aLen: Integer): Boolean;
var
 i: Integer;
 tp1,tp2: PChar;
begin
 Result := False;
 tp1 := p1;
 tp2 := p2;
 for i := 0 to aLen-1 do
 begin
   if tp1[i]<>tp2[i] then Exit;
 end;
 Result := True;
end;

function GetMsg(const aSrc: Pointer): String;
var
 p: Pointer;
begin
 p := pInc(aSrc,8);
 SetLength(Result,Integer(GetValue(p)));
 p := pInc(p,4);
 Move(p^,Result[1],Length(Result));
end;

function AddMsg(const aHandle: THandle;const aStr: String):Pointer;
var
 p,pn: Pointer;
 Len: Integer;
begin
 p := Pointer(aHandle);
 SetValue( pInc(GetValue(pInc(p,8)),1),pInc(p,8));
 while GetValue(pInc(p,4))<>nil do p := GetValue(pInc(p,4));
 Len := Integer(GetValue(pDec(Pointer(aStr),4)));

 GetMem(pn,Len+12);

 SetValue(pn,pInc(p,4));
 SetValue(p,pn);
 SetValue(nil,pInc(pn,4));
 SetValue(Pointer(Len),pInc(pn,8));

 mv(@aStr[1],pInc(pn,12),Len);
 Result := pn;
end;

function DelMsg(const aSrc: Pointer): Boolean;
var
 pPrev,pNext: Pointer;
begin
 Result := False;
 if GetPrevAddrMsg(aSrc)=nil then Exit;
 if aSrc=nil then Exit;
 pPrev := GetValue(aSrc);
 pNext := GetValue(pInc(aSrc,4));
 if pNext<>nil then
 begin
   SetValue(pPrev,pNext);
 end;
 SetValue(pNext,pInc(pPrev,4));
 FreeMem(aSrc);
 pPrev := GetRootMsg(pPrev);
 SetValue(pInc(GetValue(pDec(pPrev,8)),1),pInc(pPrev,8));
 Result := True;
end;

procedure DelAllMsg(aHandle: THandle);
var
 p: Pointer;
begin
 p := pInc(Pointer(aHandle),4);
 while DelMsg(GetValue(p)) do ;
end;

function CreateList: THandle;
var
 p: Pointer;
begin
 GetMem(p,12);
 SetValue(nil,p);
 SetValue(nil,pInc(p,4));
 SetValue(nil,pInc(p,8));
 Result := Integer(p);
end;

procedure DeleteList(const aHandle: THandle);
begin
 DelAllMsg(aHandle);
 FreeMem(Pointer(aHandle));
end;

function DelBackSlash(const aDirName: String): String;
begin
 Result := aDirName;
 if aDirName="" then Exit;
 if aDirName[Length(aDirName)]="\"
   then Delete(Result,Length(aDirName),1)
   else Exit;
end;



 
panov ©   (2004-05-19 22:37) [18]


constructor TLog.Create(const aPathArchive: String;const aMaxSize:Integer);
var
 i: Integer;
 Len: Integer;
 tS: String;
begin

 inherited Create(True);

 FreeOnTerminate := True;
 InitializeCriticalSection(cs);

 fMaxQueue := 1000;

 fHandleList := CreateList;
 fName := ParamStr(0);
 Len := Length(fName);

 fName[Len] := "g";
 fName[Len-1] := "o";
 fName[Len-2] := "l";

 tS := fName;

 for i := 1 to Len do
 begin
   if tS[i]="\" then tS[i] := "_";
 end;

 fArchDir := DelBackSlash(aPathArchive);
 FMaxSize := aMaxSize;
 fHandle := 0;
 fNumArch := 0;

 fSemaphore := CreateSemaphore(nil,0,High(Integer),PChar("LogThread_"+tS));

 if fSemaphore=0 then Terminate;

 Resume;
end;

procedure TLog.Execute;
var
 Ret: Cardinal;
begin
//  Write("Start Logging");
 while not Terminated do
 begin
   Ret := WaitForSingleObject(fSemaphore,1);
   if Ret = WAIT_OBJECT_0 then
   begin
     SaveLog;
   end;
   if Ret = WAIT_FAILED then break;
 end;
 if Terminated then
 begin
   repeat
     Ret := WaitForSingleObject(fSemaphore,1);
     if Ret = WAIT_OBJECT_0 then
     begin
       SaveLog;
     end;
   until Ret<>WAIT_OBJECT_0;
 end;
end;

procedure TLog.SaveLog;
var
 ResultCount:Cardinal;
 Len:Integer;
 p: Pointer;
 SizeHigh: Int64;
 Size: Integer;
begin
 if fHandle=0 then Open;
 if fHandle=0 then Exit;
 p := GetNextAddrMsg(Pointer(fHandleList));
 if p=nil then Exit;
 Len := GetLenMsg(p);
 fBuf := pInc(p,12);
 if WriteFile(fHandle,fBuf^,Len,ResultCount,nil) then
 begin
   DeleteMsg(p);
 end;
 Size := GetFileSize(fHandle,@SizeHigh);
 if Size>fMaxSize*1024 then
 begin
   Close;
   Archive;
   Open;
 end;
end;

procedure TLog.NewMsg(const aMsg: String);
var
 cnt: Cardinal;
begin
 AddMsg(fHandleList,FormatDateTime("dd.mm.yyyy hh:nn:ss ",now) + aMsg+#13#10);
 ReleaseSemaphore(fSemaphore,1,@cnt);
end;

procedure TLog.DeleteMsg(const p: Pointer);
begin
 DelMsg(p);
end;

procedure TLog.Close;
begin
 CloseHandle(fHandle);
 FHandle := 0;
end;

function TLog.Open: Boolean;
begin
 Result := True;
 fHandle := CreateFile(
            PChar(String(fName)),
            GENERIC_READ+GENERIC_WRITE,
            FILE_SHARE_READ,
            nil,
            OPEN_EXISTING+CREATE_NEW,
            0,
            0);
 if fHandle=0 then
 begin
   Result := False;
   Exit;
 end;
 SetFilePointer(fHandle,0,nil,FILE_END);
end;

procedure TLog.Archive;
var
 fNameAr: String;
 tCount: Integer;
begin
 if not CreateDirPath(fArchDir) then
 begin
   Terminate;
   Exit;
 end;
 fNameAr := FormatDateTime("ddmm_hhmmss.ar",now);

 tCount := 0;
 while tCount<5 do
 begin
   if CopyFile(
        PChar(FName),
        PChar(fArchDir+"\"+FNameAr),
        False) then
   begin
     while not DeleteFile(PChar(FName)) do;
     break;
   end;
   Inc(tCount);
 end;
end;

procedure TLog.Write(s: String);
begin
 EnterCriticalSection(cs);
   NewMsg(s);
 LeaveCriticalSection(cs);
end;

destructor TLog.Destroy;
begin
 DelAllMsg(fHandleList);
 DeleteList(fHandleList);
 CloseHandle(fSemaphore);
 Close;
 DeleteCriticalSection(cs);
 inherited;
end;

function CreateLog(const aPathArchive: String;const aMaxSize:Integer):TLog;
begin
 Result := TLog.Create(aPathArchive,aMaxSize);
end;

procedure CloseLog(const aLog: TLog);
begin
 aLog.Terminate;
end;

procedure WriteLog(const aLog: TLog;const aMsg: String);
begin
 aLog.Write(aMsg);
end;


 
panov ©   (2004-05-19 22:39) [19]

Позволяет писать в журнал из нескольких потоков со скоростью не менее 1000-1500 сообщений в секунду.


 
Vlad ©   (2004-05-19 22:39) [20]


> panov ©   (19.05.04 22:37) [17]

Красиво :-)
Я бы не догадался наследником TThread сделать


 
vuk ©   (2004-05-19 22:40) [21]

Я бы тоже свел. :o) Но на данный момент используется во многих местах, так что пусть пока так. Опять же если в класс сводить, то все функции сделать методами класса. И еще, чтобы вопросов не возникало. Функции возвращают boolean для того, чтобы их можно было с Assert использовать - у меня иногда в отладочной версии бывает написано Assert(WriteLog(...)). Очень удобно все отключать сразу одной галочкой. :o)


 
vuk ©   (2004-05-19 22:42) [22]

Последнее мое собщение - в ответ на

Игорь Шевченко ©   (19.05.04 22:36) [16]
>Я бы в класс свел :)


 
Игорь Шевченко ©   (2004-05-19 22:56) [23]


> Позволяет писать в журнал из нескольких потоков со скоростью
> не менее 1000-1500 сообщений в секунду.


То есть, для каждого потока еще и дополнительный поток для записи в журнал создается ?


 
panov ©   (2004-05-19 22:57) [24]

>Игорь Шевченко ©   (19.05.04 22:56) [23]

Нет, используется один экземпляр класса TLog.


 
panov ©   (2004-05-19 23:05) [25]

Еще одну функцию забыл дописат сюда:

function CreateDirPath(const aDir: String): Boolean;
var
 s: String;
 i: Integer;
begin
 Result := False;
 if aDir="" then Exit;
 i := 0 ;
 while i<Length(aDir) do
 begin
   inc(i);
   if (aDir[i]="\") then
   begin
     if (i>1) then
     begin
       if aDir[i-1]<>":" then
       begin
         if not CreateDirectory(PChar(s),nil) then
         begin
           if GetLastError<>ERROR_ALREADY_EXISTS then Exit;
         end;
       end;
     end;
   end;
   s := s + aDir[i];
 end;
 if not CreateDirectory(PChar(s),nil) then
 begin
   if GetLastError<>ERROR_ALREADY_EXISTS then Exit;
 end;
 Result := True;
end;


 
Игорь Шевченко ©   (2004-05-19 23:07) [26]


> Нет, используется один экземпляр класса TLog.


А какой смысл его от TThread наследовать ?


 
panov ©   (2004-05-19 23:08) [27]

>Игорь Шевченко ©   (19.05.04 23:07) [26]

Смысл в том, что лог ведется в отдельном потоке, не тормозит основной поток.
Выстраивается очередь сообщений для записи на диск.


 
Игорь Шевченко ©   (2004-05-19 23:14) [28]

Я могу сказать, что мне не нравится в коде Романова Р.В. и в коде [18]. Лог вообще-топредусмотрен для фиксации неких событий, поэтому любая буферизация в этом случае чревата, как отметил vuk, потерей данных. Это то место, где скорость не нужна и запись должна быть синхронной с событиями, которые надо фиксировать.


 
panov ©   (2004-05-19 23:20) [29]

>Игорь Шевченко ©   (19.05.04 23:14) [28]

Я писал код дне для протоколирования ошибок(их  - в том числе), но в основном для фиксирования некоторых некритичных событий в программе. -)


 
Aldor ©   (2004-05-20 00:01) [30]

panov © [17], [18]

 Действительно красиво.

 А PChar(String(fName)) <- это зачем?


 
Игорь Шевченко ©   (2004-05-20 00:39) [31]


>    OPEN_EXISTING+CREATE_NEW,


OPEN_ALWAYS

RTFS: Windows.pas


 
Романов Р.В. ©   (2004-05-20 06:42) [32]


> panov ©   (19.05.04 22:37) [18]
> fNameAr := FormatDateTime("ddmm_hhmmss.ar",now);


"ddmm_hhnnss.ar"


> Игорь Шевченко ©   (19.05.04 23:14) [28]
> Лог вообще-топредусмотрен для фиксации неких событий, поэтому
> любая буферизация в этом случае чревата, как отметил vuk,
> потерей данных

Я вообще то хотел сделать сброс в TStream а из него можно было бы писать хоть в файл хоть в мемо. Но все же учту это замечание, думаю сделать флажок прямой записи в файл. Но есть один вопрос по поводу try except. Запись в файл очень даже небезопасная операция и в случае возникновения потока ошибок - несколько сотен штук и наложении на это событие ошибки записи в файл пользователю придется долго закрывать окна с сообщениями об ошибке. А если при ошибке записи в файл выдавать сообщение и отключать запись в файл? Как такой вариант оцените?


> Игорь Шевченко ©   (19.05.04 15:04) [6]
> RTFS: TList.SetCapacity

Насчет SetCapacity думаю не будет пользоваться этим свойством в моем компоненте, поэтому такое как у меня выделение памяти считаю очень даже оправданым. Если в программе много ошибок памяти резервируется больше, если мало меньше. Хотя при прямой записи в файл это не нужно.

Есть идея организовывать иерархию из этих компанентов. Добавить свойство Parent и если он определен, то все ошибки пересылаются этому Parent`у а уж он их накапливает или пишет в свой файл.
Думаю такой вариант будет удобен например когда несколько человек разрабатывают свои компоненты со встроенным TErrorsLog и тестируют, а при использовании их в одном приложении для TErrorsLog этих компонентов назначается Parent - TErrorsLog приложения и они автоматически пишут свои ошибки в лог приложения. Это бред?


 
panov ©   (2004-05-20 09:49) [33]

>Романов Р.В. ©   (20.05.04 06:42) [32]

Писать лучше действительно таким образом, чтобы другие приложения могли читать из файла параллельно...


 
panov ©   (2004-05-20 10:02) [34]

>Романов Р.В. ©   (20.05.04 06:42) [32]

Есть идея организовывать иерархию из этих компанентов. Добавить свойство Parent и если он определен, то все ошибки пересылаются этому Parent`у а уж он их накапливает или пишет в свой файл.

Мне кажется, что можно написать com-сервер, реализующий запись в журнал, тогда можно будет писать протокол сразу с нескольких копьютеров в сети одновременно...


 
Igorek ©   (2004-05-20 10:20) [35]


> destructor TErrorsLog.Destroy;
> begin
>  inherited
>  ...
> end;

Да, я ошибся. Сбило с толку то, что Дельфи создает так деструктор по Ctrl+Shift+C

За что Ю.З. отправили в R/O?


 
Романов Р.В. ©   (2004-05-20 10:38) [36]


> panov ©   (20.05.04 10:02) [34]


Под 98 не будет работать.
И мне кажется что реализация с com-сервером громоздкая. Гараздо проще в случае необходимости отправлять локальный отчет по заданному адресу.


 
Игорь Шевченко ©   (2004-05-20 10:42) [37]

Романов Р.В. ©   (20.05.04 06:42)


> Насчет SetCapacity думаю не будет пользоваться этим свойством
> в моем компоненте, поэтому такое как у меня выделение памяти
> считаю очень даже оправданым. Если в программе много ошибок
> памяти резервируется больше, если мало меньше. Хотя при
> прямой записи в файл это не нужно.


Я подсказал, куда надо посмотреть, чтобы использовать похожий алгоритм, более понятный постороннему читателю.


> Запись в файл очень даже небезопасная операция и в случае
> возникновения потока ошибок - несколько сотен штук и наложении
> на это событие ошибки записи в файл пользователю придется
> долго закрывать окна с сообщениями об ошибке. А если при
> ошибке записи в файл выдавать сообщение и отключать запись
> в файл? Как такой вариант оцените?


Да нет, ошибка при записи в файл обычно одна возникает. Насчет выдачи сообщения и отключении записи в файл, мне идея не нравится тем, что она в принципе сводит на нет весь принцип лога. Можно попробовать автоматически переключить файл (на другой диск, например) и выдать предупреждение о том, что файл протокола был перенаправлен. Если же и эта попытка не увенчается успехом, тогда отключать возможность протоколирования вообще.
Кстати, могу предложить такую идею (успешно внедрена): в случае потока однотипных или одинаковых сообщений, выводить в лог только первое из серии однотипных, а после нее писать фразу вроде "to next line same as above" до первого сообщения, не совпадающего с последним записанным.

> Есть идея организовывать иерархию из этих компанентов. Добавить
> свойство Parent и если он определен, то все ошибки пересылаются
> этому Parent`у а уж он их накапливает или пишет в свой файл.
> Думаю такой вариант будет удобен например когда несколько
> человек разрабатывают свои компоненты со встроенным TErrorsLog
> и тестируют, а при использовании их в одном приложении для
> TErrorsLog этих компонентов назначается Parent - TErrorsLog
> приложения и они автоматически пишут свои ошибки в лог приложения.
> Это бред?


Да нет, не бред, вполне, на первый взгляд, нормальная идея.


 
Романов Р.В. ©   (2004-05-20 11:18) [38]


> Игорь Шевченко ©   (20.05.04 10:42) [37]
> Я подсказал, куда надо посмотреть, чтобы использовать похожий
> алгоритм, более понятный постороннему читателю.


Посмотрел я туда. У меня более простой алгоритм. На что обратить внимание? На то что память резервируется блоками кратными 4? Или то что код увеличения размера вынесен в отдельную процедуру?


> Насчет выдачи сообщения и отключении записи в файл, мне
> идея не нравится тем, что она в принципе сводит на нет весь
> принцип лога. Можно попробовать автоматически переключить
> файл (на другой диск, например) и выдать предупреждение
> о том, что файл протокола был перенаправлен.

Тогда такой вариант. Создаем виртуальный метод обработки этой ошибки. По умолчанию в нем реализован диалог выбора нового файла для лога или его отключение. У программиста существует возможность написать свой обработчик этой ошибки.


> в случае потока однотипных или одинаковых сообщений, выводить
> в лог только первое из серии однотипных, а после нее писать
> фразу вроде "to next line same as above" до первого сообщения,
> не совпадающего с последним записанным.

Хорошая идея. Спасибо.


 
Игорь Шевченко ©   (2004-05-20 11:28) [39]


> Тогда такой вариант. Создаем виртуальный метод обработки
> этой ошибки. По умолчанию в нем реализован диалог выбора
> нового файла для лога или его отключение. У программиста
> существует возможность написать свой обработчик этой ошибки.


Совсем хорошая идея.

Могу поделиться, как сделано в одной из задач у нас.
У нас логгер - это отдельный процесс (сервис), обмен данными с ним происходит через Memory Mapped File или через mailslot, запись в файл, разумеется, прямая, переключение файлов (сложный механизм, каждая подсистема в свой файл может лог писать) и их архивирование происходит автоматически, либо по таймеру, либо по размеру файла лога (задается опцией).
Чем привлекателен такой способ: Если процесс логгера по какой-либо причине умер, то при следующем его запуске сообщения в его буфере сохраняются, и он может записать их без потерь.


 
Романов Р.В. ©   (2004-05-20 12:17) [40]


> Игорь Шевченко ©

Про SetCapacity поясните.


> У нас логгер - это отдельный процесс (сервис),

Это хорошая идея. Если для него разработать компонент для связи с сервесом то один логер можно применять при отадке многих процессов. Гуд.. гуд... есть над чем подумать...



Страницы: 1 2 вся ветка

Текущий архив: 2004.06.06;
Скачать: CL | DM;

Наверх




Память: 0.63 MB
Время: 0.024 c
14-1084564681
Piter
2004-05-14 23:58
2004.06.06
Ни у кого нету заготовок СЧЕТОВ-ФАКТУР?


1-1085399864
Basilio
2004-05-24 15:57
2004.06.06
Отладчик в дельфи не останавливается на breakpoints


1-1085311717
Leos
2004-05-23 15:28
2004.06.06
вывод текста в PaintBox.canvas


14-1084908537
Juster~~
2004-05-18 23:28
2004.06.06
Компонент


4-1083050607
luka
2004-04-27 11:23
2004.06.06
клавиатура и мышь