Чтение из используемого файла

Общие вопросы программирования, алгоритмы и т.п.

Модератор: Модераторы

Чтение из используемого файла

Сообщение MetallDoctor » 14.08.2013 13:59:11

День добрый.

Возникла у меня задача – анализ работоспособности некоей службы, работающей на сервере. Служба собирает данные из нескольких источников и выдаёт их в виде xml, при этом при отказе она выглядит работающей – отдаёт последний получившийся xml и значится запущенной, единствнное, что нашёл – строка в логе. Логи там небольшие, так что анализировать их легко. Соответственно, набросал программу, которая смотрит на новые строки лога и проверяет, не появилось ли там чего-то плохого, у меня на машине всё отработало как надо. Вот исходники:

Код: Выделить всё
program CheckRDM;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Crt, Classes,  SysUtils, Dialogs, Process;
  { you can add units after this }

VAR
  F1,F2, L:TextFile;
  I, N:Integer;
  S: STRING;
  P: TProcess;

begin
  IF FileExists('C:RDMLogcopy_RDM[' + FormatDateTime('dd-mm-yyyy',Now) + '].log') THEN BEGIN
    Assign(F1, 'C:RDMLogRDM[' + FormatDateTime('dd-mm-yyyy',Now) + '].log');
    Assign(F2, 'C:RDMLogcopy_RDM[' + FormatDateTime('dd-mm-yyyy',Now) + '].log');
    Assign(L, 'c:RDMChecklog.txt');
    Reset(F1);
    Reset(F2);
    Append(L);
    N:=0;
    S:='';
    REPEAT
      ReadLn(F2,S);
      N:=N+1;
    until EoF(F2);
    Close(F2);
    Append(F2);
    For I:= 1 to N DO ReadLn(F1,S);
    WHILE NOT EoF(F1) DO BEGIN
        ReadLn(F1, S);
        IF Pos('GetIdleAgents [error]', S)>0 THEN BEGIN
          P := TProcess.Create(nil);
          P.CommandLine := 'c:RDMCheckbat.bat';
          P.Execute;
          P.Free;
          WriteLn(L, FormatDateTime('yyyy-mm-dd',Now)+' Servise restarted')
        end;
        WriteLn(F2, S)
    end;
    Close(F1);
    Close(F2);
    Close(L);
  end
  ELSE BEGIN
    FileCreate('C:RDMLogcopy_RDM[' + FormatDateTime('dd-mm-yyyy',Now) + '].log');
    Assign(F1, 'C:RDMLogRDM[' + FormatDateTime('dd-mm-yyyy',Now) + '].log');
    Assign(F2, 'C:RDMLogcopy_RDM[' + FormatDateTime('dd-mm-yyyy',Now) + '].log');
    Reset(F1);
    Rewrite(F2);
    S:='';
    WHILE NOT EoF(F1) DO BEGIN
        WriteLn(F1, S);
        ReadLn(F2, S);
    end;
    Close(F1);
    Close(F2);
  end;
end.


Но вот незадача – на сервере уже я не могу её запустить – получаю такую ошибку:
Код: Выделить всё
C:RDMCheck>CheckRDM.exe
An unhandled exception occurred at $00401D64 :
EInOutError : Access denied
  $00401D64  main,  line 54 of CheckRDM.lpr


На 54 строке инструкция «Reset(F1);» и более – ничего. То есть лог работающей программы оно получить не может – просто не может его открыть, хотя скриптовые языки, и система его открывают на лету.

Нашёл вот такую вещь в сети: http://delphiworld.narod.ru/base/open_r ... _file.html
Замена «Reset(F1);» на
Код: Выделить всё
F:=FileMode;
FileMode=0;
Reset(F1);
FileMode:=F;
не помогает, но и ошибки при этом не показывает, значит переменная FileMode существует. Оставление её равной 0 постоянно – может привести к плохо предсказуемым последствиям для открытия других файлов.



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

Добавлено спустя 21 час 58 минут 12 секунд:
Возможно, я не правильно понял сообщение об ошибке, буду благодарен за мысли на эту тему и/или алгоритм альтернативной реализации, особенно если они помогут обойти эту ошибку.
MetallDoctor
незнакомец
 
Сообщения: 9
Зарегистрирован: 24.08.2010 21:38:33

Re: Чтение из используемого файла

Сообщение alexey38 » 16.08.2013 06:16:33

alexey38
долгожитель
 
Сообщения: 1627
Зарегистрирован: 27.04.2011 19:42:31

Re: Чтение из используемого файла

Сообщение MetallDoctor » 16.08.2013 15:21:54

Спасибо огромное, очень интересно. Часть с
Код: Выделить всё
    FileCreate('C:RDMLogcopy_RDM[' + FormatDateTime('dd-mm-yyyy',Now) + '].log');
    Assign(F1, 'C:RDMLogRDM[' + FormatDateTime('dd-mm-yyyy',Now) + '].log');
    Assign(F2, 'C:RDMLogcopy_RDM[' + FormatDateTime('dd-mm-yyyy',Now) + '].log');
    Reset(F1);
    Rewrite(F2);
    S:='';
    WHILE NOT EoF(F1) DO BEGIN
        WriteLn(F1, S);
        ReadLn(F2, S);
    end;
    Close(F1);
    Close(F2);
помогает разрулить, похоже. Сейчас переписываю и буду пробовать.

Но в части
Код: Выделить всё
        ReadLn(F1, S);
        IF Pos('GetIdleAgents [error]', S)>0 THEN BEGIN
          P := TProcess.Create(nil);
          P.CommandLine := 'c:RDMCheckbat.bat';
          P.Execute;
          P.Free;
          WriteLn(L, FormatDateTime('yyyy-mm-dd',Now)+' Servise restarted')
        end;
        WriteLn(F2, S)
Мне приходится работать со строками, а примеров такого кода найти не получается (f1.ReadAnsiString похоже, не работает). Если Вы приведёте пример, как это сделать не создавая ещё одной копии лога – буду очень благодарен!

Добавлено спустя 1 час 1 минуту 5 секунд:
Похоже, не прошло – даже в самом простом варианте:
Код: Выделить всё
program TestFS;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,  SysUtils
  { you can add units after this };
VAR
  f1,f2:TFileStream;
  s:STRING;

begin
  F1 := TFileStream.Create('C:\RDM\Log\RDM[' + FormatDateTime('dd-mm-yyyy',Now) + '].log', fmOpenRead);
  try
    F2 := TFileStream.Create('C:\RDM\Log\copy_RDM[' + FormatDateTime('dd-mm-yyyy',Now) + '].log', fmCreate);
    try // копирование файла
      F2.CopyFrom(F1, F1.Size)
    finally
      F2.Free;
    end;  // завершение конструкции try (F2)
  finally
    F1.Free;
  end; // завершение конструкции try (F1)
end.


Падает на аналогичной строке «F1 := TFileStream.Create('C:\RDM\Log\RDM[' + FormatDateTime('dd-mm-yyyy',Now) + '].log', fmOpenRead);»

Код: Выделить всё
C:\RDM\Check>TestFS.exe
An unhandled exception occurred at $0040EFDF :
EFOpenError : Unable to open file "C:\RDM\Log\RDM[16-08-2013].log"
  $0040EFDF
  $0040160A  main,  line 16 of TestFS.lpr
MetallDoctor
незнакомец
 
Сообщения: 9
Зарегистрирован: 24.08.2010 21:38:33

Re: Чтение из используемого файла

Сообщение bormant » 16.08.2013 21:55:20

Извините, что немного не по теме в части fpc, но админской задаче -- админское решение, элементарный батник:
Код: Выделить всё
@echo off
cd %~dp0
find "GetIdleAgents [error]" RDMLog_RDM[%date:.=-%].log > nul
if errorlevel 1 goto :eof
echo %date:~-4%-%date:~3,2%-%date:~0,2% Service restarted >> RDMCheckLog.txt
RDMCheckbat.bat

ps. При условии русского формата даты dd.mm.yyyy в выводе echo %date%, в противном случае поправить строки форматирования.


pps. Ну а по теме форума:
Код: Выделить всё
F1 := TFileStream.Create(..., fmOpenRead or fmShareDenyNone);
или
Код: Выделить всё
oldFileMode := FileMode;
FileMode := fmOpenRead or fmShareDenyNone;
Reset(F1);
FileMode := oldFileMode;

Добавлено спустя 1 час 21 минуту 9 секунд:
ppps. И небольшое расследование по поводу FileMode, имевшее место не так давно: viewtopic.php?f=45&t=8799
Аватара пользователя
bormant
постоялец
 
Сообщения: 407
Зарегистрирован: 21.03.2012 11:26:01

Re: Чтение из используемого файла

Сообщение MetallDoctor » 28.08.2013 15:41:44

Спасибо большое.
Так получилось, что сервис доработали и задача отпала, но всё-равно большое спасибо, наверняка конструкция «fmOpenRead or fmShareDenyNone» ещё где пригодится.
Батник я и сам пытался набросать, но уткнулся в массу мелочей, типа нетривиального нахождения изменившихся с последней проверки строк. Ведь нужно и сам лог сохранить – мало ли что другое из него понадобится, и при каждой новой проверке не натыкаться снова и снова на ту же строку. Я пока никак не пойму, как в этом коде этот момент учтён. Откровенно говоря пару строк в нём я вообще не понимаю, буду благодарен если поясните.

Как-то так:
Код: Выделить всё
@echo off
отключение вывода.
Код: Выделить всё
cd %~dp0
Вообще непонятно. Должно менять currentdirectory, но на что?
Код: Выделить всё
find "GetIdleAgents [error]" RDMLog_RDM[%date:.=-%].log > nul
Ищет в файле текущего лога строку с ошибкой, вывод – вникуда.
Код: Выделить всё
if errorlevel 1 goto :eof
Откуда уровень ошибки возьмётся я не понял, на конец какого файла прыгать – тоже.
Код: Выделить всё
echo %date:~-4%-%date:~3,2%-%date:~0,2% Service restarted >> RDMCheckLog.txt
Записать в лог сообщение о том, что сервис дёрнули и, наконец,
Код: Выделить всё
RDMCheckbat.bat
запуск батника, трогающего саму службу. Я код из батника планировал перетянуть в программу после отладки, ну да не суть, самого же батника я не оставлял.

Мне sh роднее, а там помогает diff.
MetallDoctor
незнакомец
 
Сообщения: 9
Зарегистрирован: 24.08.2010 21:38:33

Re: Чтение из используемого файла

Сообщение bormant » 28.08.2013 17:07:22

MetallDoctor писал(а):
Код: Выделить всё
cd %~dp0
Вообще непонятно. Должно менять currentdirectory, но на что?
%0 -- сам батник (нулевой параметр, ParamStr(0)), через ~ идут модификаторы: d -- drive, p -- path, итого: диск:\путь до текущего исполняемого файла .bat, остальное, не менее интересное,-- for /? в помощь.

MetallDoctor писал(а):
Код: Выделить всё
find "GetIdleAgents [error]" RDMLog_RDM[%date:.=-%].log > nul
Ищет в файле текущего лога строку с ошибкой, вывод – вникуда.
Код: Выделить всё
if errorlevel 1 goto :eof
Откуда уровень ошибки возьмётся я не понял, на конец какого файла прыгать – тоже.
find оставляет за собой errorlevel 0, когда строка не найдена, 1, когда найдена.
:eof -- встроенная метка конца текущего исполняемого файла .bat.

MetallDoctor писал(а):
Код: Выделить всё
echo %date:~-4%-%date:~3,2%-%date:~0,2% Service restarted >> RDMCheckLog.txt
Записать в лог сообщение о том, что сервис дёрнули
Записать в смысле ДОписать.

MetallDoctor писал(а):
Код: Выделить всё
RDMCheckbat.bat
запуск батника, трогающего саму службу.
Я бы сказал запуск с передачей управления, без возврата в вызвавший. Если нужен возврат, тогда call RDMCheckbat.bat

MetallDoctor писал(а):Я код из батника планировал перетянуть в программу после отладки, ну да не суть, самого же батника я не оставлял.
Тут уж звиняйте, что в сообщении видел, о том и писал ;-)
Аватара пользователя
bormant
постоялец
 
Сообщения: 407
Зарегистрирован: 21.03.2012 11:26:01

Re: Чтение из используемого файла

Сообщение MetallDoctor » 28.08.2013 18:20:07

bormant писал(а):
MetallDoctor писал(а):Я код из батника планировал перетянуть в программу после отладки, ну да не суть, самого же батника я не оставлял.

Тут уж звиняйте, что в сообщении видел, о том и писал ;-)
Детали. Не критично.

В целом понятно всё, кроме одного – как выходит, что он не будет дёргать батник «RDMCheckbat.bat» при каждом прохождении, если в этот день произошло залипание. И что он будет делать если произойдёт второе залипание?
MetallDoctor
незнакомец
 
Сообщения: 9
Зарегистрирован: 24.08.2010 21:38:33

Re: Чтение из используемого файла

Сообщение bormant » 30.08.2013 15:38:07

В целом понятно всё, кроме одного – как выходит, что он не будет дёргать батник «RDMCheckbat.bat» при каждом прохождении, если в этот день произошло залипание. И что он будет делать если произойдёт второе залипание?
В вашем коде подобной обработки не было, поэтому считал эту часть решенной. Если это не так, проще всего при обнаружении условия предварительно переименовать лог на дату/время обнаружения или дописать его в конец другого файла и затем удалить, то есть что-то вроде
Код: Выделить всё
if errorlevel 1 goto :eof
mv RDMLog_RDM[%date:.=-%].log RDMLog_RDM[%date:.=-%_%time::=-%].log
RDMCheckbat.bat

Код: Выделить всё
if errorlevel 1 goto :eof
echo.>>old.log
copy /b old.log+RDMLog_RDM[%date:.=-%].log
del RDMLog_RDM[%date:.=-%].log
RDMCheckbat.bat
Аватара пользователя
bormant
постоялец
 
Сообщения: 407
Зарегистрирован: 21.03.2012 11:26:01

Re: Чтение из используемого файла

Сообщение MetallDoctor » 30.08.2013 18:08:45

В вашем коде подобной обработки не было
Вот этот кусок:
Код: Выделить всё
    REPEAT
      ReadLn(F2,S);
      N:=N+1;
    until EoF(F2);
    Close(F2);
    Append(F2);
    For I:= 1 to N DO ReadLn(F1,S);
Смотрится сколько уже записано во второй файл и отматывает до этой же отметки первый.

И вот это:
Код: Выделить всё
    WHILE NOT EoF(F1) DO BEGIN
        ReadLn(F1, S);
        IF Pos('GetIdleAgents [error]', S)>0 THEN BEGIN
          P := TProcess.Create(nil);
          P.CommandLine := 'c:\RDM\Checkbat.bat';
          P.Execute;
          P.Free;
          WriteLn(L, FormatDateTime('yyyy-mm-dd',Now)+' Servise restarted')
        end;
        WriteLn(F2, S)
    end;
Строка читается из файла, проверяется и записывается в другой.

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

Кот BAT-ника занятно выглядит. При случае посмотрю, на что ещё способен batch.
MetallDoctor
незнакомец
 
Сообщения: 9
Зарегистрирован: 24.08.2010 21:38:33

Re: Чтение из используемого файла

Сообщение bormant » 30.08.2013 19:32:15

MetallDoctor писал(а):Смотрится сколько уже записано во второй файл и отматывает до этой же отметки первый.
Ага, прошу пардону, читал "по диагонали", просмотрел...
Аватара пользователя
bormant
постоялец
 
Сообщения: 407
Зарегистрирован: 21.03.2012 11:26:01


Вернуться в Общее

Кто сейчас на конференции

Сейчас этот форум просматривают: MailRu[bot] и гости: 9

Рейтинг@Mail.ru