Запись в лог я использую во всех более-менее серьезных проектах. Логирование помогает и на этапе отладки, и на этапе внедрения (иногда проще попросить прислать лог, чем со слов понять, в чем проблема). Давно уже использую для этих целей маленькую и удобную библиотечку uLog. Все, что от вас потребуется, это добавить ее в uses. Ну и по желанию некоторые настройки. Но даже уже без всяких настроек вы можете писать в лог с помощью процедуры sLog. Пример:
sLog ('MyProgram.log','Значение переменной ='+str);
Первый входной параметр — куда писать, второй — что писать. Если путь прописан не полностью — идет обращение к текущей директории проекта. Если файл не существует - он будет создан автоматически. В логе строчки появляются снабженные временем записи в лог. Пример части лога:
07.07.2010 16:34:11 [243] Starting...(в квадратных скобках после времени - миллисекунды)
07.07.2010 16:34:11 [243] Signature:A951D217D6B5E340 03040002 940000000500000001000000280A00000200000053657276696365205061636B2032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
07.07.2010 16:34:11 [243] LoadConf...
Немного о настройках. Первое, что можно настраивать, - это файл по умолчанию, в который будет писаться лог. Для этого обращаемся к uLog.LogFileName.
Пример:
uLog.LogFileName := ExtractFilePath(GetModuleName(HInstance))+'TaskManager.log';
После установки LogFileName лог будет писаться в указанный файл, если первый входной параметр у sLog не будет задан:
sLog('','Инициализация прошла успешно');
Еще один параметр - EnableMessages. Он отвечает за то, будут ли появляться сообщения об ошибке в этой библиотеке (например, когда файл лога не указан и не задан по умолчанию).
uLog.EnableMessages := false;
И, наконец, самый замечательный параметр. Он передается в sLog третьим. Это "уровень логирования", LogLevel.
slog('','Starting library',2);
slog('',tmpS,3);
Вы сами проставляете этот уровень в зависимости от того, что за информацию пишете в лог. Градация, например, может быть такой:
- мегаважный
- информационный (запустился, ...)
- отладочный
Уровней может быть больше, сколько угодно. При приведенном в предыдущем абзаце варианте вы на этапе разработки используете LogLevel := 3, а когда устанавливаете продукт клиенту, ставите LogLevel 1 или 2. LogLevel удобно задавать в ini-файле и считывать при запуске приложения. Если вдруг у клиента внезапно начнутся какие-то сбои - под вашим руководством он сможет поменять уровень логирования на 3 и прислать вам файл с отладочной информацией, которая поможет вам выяснить причину сбоев.
Листинг этой чудесной библиотечки:
unit uLog;
interface
procedure sLog (aLogFileName: String; aMessage : String;
aLevel : integer = 1 ;
aRewrite : boolean = false ;
aIncludeDateTime : boolean=true ) ;
var
LogLevel : integer = 3 ;
EnableMessages : boolean = true ;
LogFileName : String ;
// 1 - мегаважный
// 2 - информационный (запустился, ...)
// 3 - отладочный
implementation
uses SysUtils, Dialogs, DateUtils;
procedure sLog (aLogFileName: String; aMessage : String;
aLevel : integer = 1 ;
aRewrite : boolean = false ;
aIncludeDateTime : boolean=true ) ;
var
Y,M,D,H,Min,Sec,MSec : word ;
LF : Text ;
S : String ;
S1 : String ;
N : TDateTime ;
begin
{$I-}
if aLogFileName='' then aLogFileName:=LogFileName ;
if aLevel<= LogLevel then
begin
S:='' ;
AssignFile(LF,aLogFileName) ;
if FileExists(aLogFileName) then
begin
if aRewrite then Rewrite(LF) else append(LF) ;
if IOResult<>0 then
if EnableMessages then
ShowMessage('error appending message ' + aMessage) ;
end
else
begin
rewrite(LF) ;
if IOResult<>0 then
if EnableMessages then
ShowMessage('error rewriting message ' + aMessage) ;
end ;
if aIncludeDateTime then
begin
N:=Now ;
DecodeDateTime(N,Y,M,D,H,Min,Sec,MSec);
S1:=' [' + IntToStr(MSec)+ '] ' ;
while Length(S1) <7 do S1:=S1+' ' ;
S:=DateTimeToStr(N) + S1 ;
end;
S:=S + aMessage ;
writeln(LF,S) ;
if IOResult<>0 then
if EnableMessages then
ShowMessage('error writing message ' + aMessage) ;
closeFile(LF) ;
if IOResult<>0 then
if EnableMessages then ShowMessage('error closing message ' + aMessage) ;
end ;
{$I+}
end ;
end.
___
Добавлю ссылку на класс, который написал Егор. С помощью класса можно посмотреть дамп любой области памяти и актуальный стекфлоу. Спасибо ему большое!
___
Чтобы быть в курсе обновлений блога, можно подписаться на RSS.
Я считая, что если уже оформлять в качестве библиотеки — то надо по серьёзному всё:
ОтветитьУдалить- лог класс в качестве синглтона + "защита" от многопоточности;
- реализация сессий, конечно можно разделять лог по файлом, но было бы не менее удобнее создавать различные сессии в одном файле;
- логирование прочих объектов через сериализация;
- запаковка логов в архив и отправка POST запросом... иногда клиенты и лог файл найти не могут, не то, чтобы его куда то отправить =)
p.s. это первое что пришло на ум... а так: работать, работать и ещё раз работать...
+ Нужен крешдамп
ОтветитьУдалить+ Стекфлоу
для сопровождения маленьких проектов, может быть и пойдёт, но всё равно, я бы назвал эту библиотеку простой процедурой записи в файл. Без обид.
Спасибо за комментарии :) Честно сказать, немного удивлена тем, что так серьезно отнеслись к этой статье. Наверное, надо было указать, что для начинающих. Эта простенькая либа (Егор, без обид, но все же буду называть ее так), в небольших проектах самое то - она очень легкая и (для меня) привычная. Думаю, у каждого программиста есть свои наработки, в больших распределенных системах вообще правильнее лог вести не только на клиентской стороне, но часть вообще писать в базу. Но тут, опять же, разговор об этом не идет.
ОтветитьУдалитьИ да, для многопоточных приложений я, понятно, использую другой вариант.
Маша, да всё нормально, ты - молодечик! Я давно тебя читаю, просто не комментил ) Ты всегда так чётко пишешь, а тут простите отписка получилась :) Ну а тут, уж сам Бог велел вставить 10коп. Ты уж на меня не обижайся, ну такой я, прямой. Вот )
ОтветитьУдалитьДа я не обижаюсь, там же написано "без обид" :)
ОтветитьУдалитьВообще, блог рассчитан на определенную аудиторию, статьи не уровня "для профессионалов", об этом я не раз писала. И когда появляются комментарии типа "работать, работать и еще раз работать", хочется просто спросить комментатора, что ж он сам-то ничего полезного не напишет и не выложит для общественности? А обижаться - не, это не мое, на это у меня нет времени :))
Здравствуйте!
ОтветитьУдалитьПро логирование как таковое согласен, но реализация библиотеки оставляет желать лучшего:
1. Например "хитрое" преобразование времени в строку (с циклами, переаллокацией строк) меняется на 1 строчку FormatDateTime('yyyy-mm-dd hh:nn [ss]', Value)
2. Writeln в текстовый файл на стандартном буфере - очень медленно. TFileStream с fmOpenWrite or fmShareDenyWrite будет в разы быстрее, или увеличить буфер.
Это не критика, а рекомендации :)
А.Трухин
Большое спасибо за дельное замечание, обязательно учту!
ОтветитьУдалитьЕсть эксперт для Делфи? GExpert называется. С ним поставляется замечательный модуль DbugIntf.pas, позволяющий в реальном времени отображать информацию в отладочном окошке - очень удобно для программиста во время написания программ. Плюс для всевозможных дампов (стек вызовов и др.) использую модули из JCL (JclDebug.pas, JclHookExcept.pas), которые помогают следить за ошибками на этапе beta-тестирования.
ОтветитьУдалитьМария, я постарался написать некий класс, который думаю пригодится вам. В нём реализован и крешдамп и стекфлоу. Можете использовать его для своих целей. С уважением, Егор.
ОтветитьУдалитьЗабыл ссылку походу, вот она : http://delphiday.blogspot.com/2010/07/tforgirlsdebug.html
ОтветитьУдалитьЕгор, спасибо большое!)))) Посмеялась от души - первый раз в жизни в честь меня назвали класс ))))))) Сейчас отредактирую запись и поставлю активную ссылку.
ОтветитьУдалитьP.S. Прям настроение улучшилось в этот душный день с запахом Гарри.
TMashaDebug теперь можеть получать значения регистров. И ещё TMashaDebug приняли в Torry.net :)
ОтветитьУдалитьhttp://www.torry.net/quicksearchd.php?String=TMashaDebug&Title=Yes
Неужели под Windows нет ни какой штатной системы ведения логов, писать свой велосипед многопоточный плюс производительный (хотя бы кеширующий перед записью). Подозреваю что есть (точнее нашел), но нагулить как им грамотно пользоваться не смог. (в других операционках интерфейс логов един, а систем куда и как писать выше крыши, у javы своя система логов). Сам вышел из положения криво, система логов в каждом потоке своя.
ОтветитьУдалить>Неужели под Windows нет ни какой штатной системы ведения логов
ОтветитьУдалитьEventLog
API: Windows.pas
Обвязка: C:\_dev.tools\RADStudio5\source\Win32\vcl\SvcMgr.pas