-
Вроде-бы стандартная ситуация. Создаю файл и пытаюсь в него писать. Из основного модуля все работает. Из потока, действия которого мне тоже надо логгировать - сразу крашится. Как можно решить?
AssignFile(log,GetCurrentDir+'\log.txt'); if FileExists(GetCurrentDir+'\log.txt') then Reset(Log) else Rewrite(log);
В потоке: Writeln(log,TimeToStr(now)+' Connected');
-
Попахивает битвой за дескриптор :)
Как решить - писать из одного места, оформить процедурой, постараться не юзать паскалевские методы работы с файлами. Где log определена, как именно?
-
-
глобальный var основной формы. log:text;
-
код в процедуре будет выполнен в основном потоке и проблем быть не должно
-
sammy спасибо, но это надо гонять данные из потока в основной и там уже записывать. Думал обойтись без этого.
-
Поясню немножко. Поток создает idHTTP и шлет запрос. Ждет ответа и передает его основной форме через Synchronize(MainForm.Update); Мне хотелось-бы скидывать данные из потока в чистом виде до отправки (что посылает) и после отправки (что пришло). Видимо придется передавать их все основной форме :(
-
> Видимо придется передавать их все основной форме :(
Тебе же написали про синхронизацию доступа к файлу или куда ты там логгируешь. Синхронизация это не только Synchronize и не только с GUI потоком. Выкинь паскалевские файлы, возьми TFileStream и синхронизируй запись в него критической секцией.
-
в потоке создай такую же переменную, передавай в поток значение log и пиши в него через метод который вызываешь в synchronization, прям в потоке напишешь procedure thread.log; begin Writeln(log,TimeToStr(now)+' Connected'); end;
procedure thread.execute; begin synchronize(log); end;
p.s. прошу прошения за ошибки, день программиста вчера был :)))
-
TLogger=class(TThread) protected CS:TCriticalSection; FInterval:integer; buff:TStringList; LogName:String; procedure Execute;override; public constructor Create(FileName:String;MaxCnt:integer;interval:integer); destructor Free; procedure Log(S:String); procedure Flush; end;
{ TLogger }
constructor TLogger.Create(FileName: String; MaxCnt, interval: integer); procedure recren(n:string;lvl:integer); begin if (lvl=-1) then begin if (FileExists(n)and FileExists(n+'.0')) then recren(n,0); RenameFile(n,n+'.0'); end else if lvl>MaxCnt then DeleteFile(n+'.'+IntToStr(lvl)) else begin if FileExists(n+'.'+IntToStr(lvl+1)) then recren(n,lvl+1); RenameFile(n+'.'+IntToStr(lvl),n+'.'+IntToStr(lvl+1)); end; end; begin MaxCnt:=MaxCnt-2; recren(FileName,-1); LogName:=FileName; FInterval:=interval; buff:=TStringList.Create; CS:=TCriticalSection.Create; Inherited Create(FInterval<100); end;
procedure TLogger.Execute; begin while true do begin Flush; sleep(FInterval); end; end;
procedure TLogger.Flush; var i,l:integer;T:Text; begin CS.Enter; AssignFile(T,LogName); if FileExists(LogName) then Append(T) else Rewrite(T); l:=buff.Count-1; for i:=0 to l do Writeln(T,buff[i]); buff.Clear; CloseFile(T); CS.Leave; end;
destructor TLogger.Free; begin Suspend; Flush; CS.Free; buff.Free; end;
procedure TLogger.Log(S: String); begin CS.Enter; buff.Add(S); CS.Leave; end;
-
о божечки. как длинно-то ..... unit LogUnit;
interface
procedure Log(const AMsg : string; const AParam : string = '');
implementation
uses Windows, Classes, SysUtils,SyncObjs;
var LogName : string;
cs : TCriticalSection = nil;
procedure Log(const AMsg : string; const AParam : string = '');
var F:Text;
begin
try
cs.Enter;
Assign(F,LogName);
if FileExists(LogName) then Append(F) else Rewrite(F);
Writeln(F,FormatDateTime('dd.mm.yyyy hh:mm:ss',Now),#9,GetCurrentThreadID,#9,AMsg,#9,AParam);
finally
Close(F);
cs.Leave;
end;
end;
initialization
cs := TCriticalSection.Create;
LogName := ChangeFileExt(Trim(ParamStr(0)),'.log');
finalization
cs.Free;
end.
-
> о божечки. как длинно-то .....
... h:= CreateFile(PChar(filePath), $4, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if( h <> INVALID_HANDLE_WALUE )then begin
WriteFile(h, pointer(str)^, Length(str), dwX, 0);
CloseHandle(h);
end; для libc соответсвтенно open(..., O_RDWR or O_CREAT or O_APPEND, ... )/write/close F: Text - для сценария открытие, запись, закрытие - однозначный криминал - т.к. там промежуточный буфер.(TFileStream - еще хуже, т.к. еще дополнителные операции выделения/освобождения памяти под экземпляр и буфер) причём для O_APPEND - атомарность однократного write гарантируется документацией, для FILE_APPEND_DATA - явно не документированно, но пока сбоев не было...
-
да пофик вообще. модулю этому лет 18-19 , засунут он в папку коммон и трудится он круглые сутки прилинкованый к фик знает скольки процессам созданным за все это время.
был бы там хоть малейший реальный криминал, я бы уже об этом знал
-
> F:Text - для сценария открытие, запись, закрытие - однозначный > криминал - т.к. там промежуточный буфер.
Если логов пишется много и выкинуть буфер, то это приведёт к проседанию программы по скорости в разы. Так что без-буфера ну никак нельзя.
> был бы там хоть малейший реальный криминал
Обработка ошибок открытия файла сделана неверно. Лучше TEXT заменить на TFileStream/TMemoryStream
-
там нет обработки ошибок открытия файла
-
> F:Text - для сценария открытие, запись, закрытие - однозначный > криминал - т.к. там промежуточный буфер
Чего ?
-
> Чего ?
function _WriteBytes(var t: TTextRec; const b; cnt : Longint): Pointer;
var
P: PChar;
RemainingBytes: Longint;
Temp: Integer;
begin
Result := @t;
if t.Mode <> fmOutput and not TryOpenForOutput(t) then Exit;
P := t.BufPtr + t.BufPos;
RemainingBytes := t.BufSize - t.BufPos;
while RemainingBytes <= cnt do
begin
Inc(t.BufPos, RemainingBytes);
Dec(cnt, RemainingBytes);
Move(B, P^, RemainingBytes);
Temp := TTextIOFunc(t.InOutFunc)(t);
if Temp <> 0 then
begin
SetInOutRes(Temp);
Exit;
end;
P := t.BufPtr + t.BufPos;
RemainingBytes := t.BufSize - t.BufPos;
end;
Inc(t.BufPos, cnt);
Move(B, P^, cnt);
end;
...
> > F:Text - для сценария открытие, запись, закрытие ... > Если логов пишется много и выкинуть буфер, то это приведёт к проседанию программы по скорости в разы. Так что без-буфера ну никак нельзя.
TTextBuf = array[0..127] of Char; - этот буфер полезен исключительно для форматирования классического типизированного вывода через Write/Writeln... Потоковый вывод(текстовой информации) и аварийные логи это немного разные задачи... И мне пока никто убедительно не смог показать, что его буферизация работает лучше файлового кэша Windows, особенно при конкуретной записи в один файл из нескольких потоков(и тем более процессов)... Буфер даёт весомое увеличение скорости - если выровнен на границу страницы памяти, причём не только по размеру, но и по смещению(и в этом случае небольшой добавочный выигрыш даёт FILE_FLAG_NO_BUFFERING).
-
han_malign © (30.10.17 17:51) [16]
У меня собственно вызвала удивление процитированная фраза. Потому что при закрытии этот буфер (и все другие) записывается в файл (в кеш системы, на диск, неважно).
-
Кстати. Сам пользуюсь ведением протокола, аналогично [10], с заменой критической секции на mutex (у меня несколько процессов пишут в один файл), никаких тормозов не наблюдал.
-
смотря какой объем нужно логировать. там, где большой поток данных лучше использовать отдельный поток для сброса данных на диск. так как из-за антивирусов могут появится огромные просадки в процедуре логирования, сталкивался с таким.
|