Создать лог работы собственного кода
Иногда становится необходимым просмотреть последовательность и затраты времени на выполнение каких-либо команд, функций и т.п. Здесь расскажу о том, как это у меня организовано, какие коды используются.
Первый вопрос - это куда записывать лог-файл. Я принял, что лог должен записываться в определенный каталог, расположенный либо в %temp% текущего пользователя, либо в %appdata%. Оба решения имеют свои плюсы и минусы.
При записи в %temp% вычистить лог очень легко. Это плюс. Удалить логи слишком легко - и это минус.
При записи в %appdata% надо предусмотреть автоматическое удаление слишком разросшихся файлов. Здесь этот вопрос не рассматриваю (просто чтобы не запутать лишними подробностями ;))
Запись лога на сервер я всерьез не рассматриваю,- там слишком много может быть ограничений. А механизм должен работать всегда.
Второй вопрос - это имя лог-файла. В принципе, тут никаких ограничений, но для примера примем, что имя лог-файла должно формироваться по принципу <ДоменКомпьютера>-<ИмяКомпьютера>-<ИмяПользователя>.log. Если объем файла превышает 2Mb, старый файл копируется с указанием даты, и создается новый.
Далее нам гарантированно понадобятся функции показа текущего лога, функции записи в лог нужной информации, а также - внимание! - еще один "переключатель". Последняя функция позволит нам с легкостью переключать режим ведения лога. Например, при разработке, тестировании или при непонятных ситуациях у пользователя можно включить режим лога, а потом уже и анализировать лог.
Получим имя лог-файла:
- (defun _kpblc-get-file-log (/ path file handle)
- ;|
- * Возвращает имя файла лога. Если файл существует и его размер больше 2Mb,
- * то файл автоматически копируется и пересоздается
- |;
- (setq path (_kpblc-dir-create (strcat (vl-string-right-trim "\\" (getenv "appdata")) "\\kpblc\\")))
- (cond
- ((not
- (findfile
- (setq file (strcat path
- (getenv "userdomain")
- "-"
- (getenv "computername")
- "-"
- (getenv "username")
- ".log"
- ) ;_ end of strcat
- ) ;_ end of setq
- ) ;_ end of findfile
- ) ;_ end of not
- file
- )
- ((and (findfile file)
- (> (vl-file-size file) (* 2 (expt 2 20)))
- ) ;_ end of and
- (vl-file-copy
- file
- (strcat (_kpblc-dir-path-no-splash (vl-filename-directory file))
- "\\"
- (vl-filename-base file)
- "_"
- (rtos (getvar "cdate") 2 6)
- ) ;_ end of strcat
- ) ;_ end of vl-file-copy
- (setq handle (open file "w"))
- (close handle)
- file
- )
- (t file)
- ) ;_ end of cond
- ) ;_ end of defun
Потребуется одна служебная функция:
- (defun _kpblc-dir-create (path / tmp)
- ;|
- * Гарантированное создание каталога.
- * Параметры вызова:
- path создаваемый каталог
- |;
- (cond
- ((vl-file-directory-p path) path)
- ((setq tmp (_kpblc-dir-create (vl-filename-directory path)))
- (vl-mkdir
- (strcat tmp
- "\\"
- (vl-filename-base path)
- (cond ((vl-filename-extension path))
- (t "")
- ) ;_ end of cond
- ) ;_ end of strcat
- ) ;_ end of vl-mkdir
- (if (vl-file-directory-p path)
- path
- ) ;_ end of if
- )
- ) ;_ end of cond
- ) ;_ end of defun
Далее напишем тот самый "переключатель" (функция записи лога будет ссылаться на его данные):
- (defun _kpblc-log-toggle (param)
- ;|
- * Функция включения или отключения процедуры лога
- * Параметры вызова:
- param включить лог (t) или отключить (nil)
- |;
- (vl-bb-set '*kpblc-settings*
- (_kpblc-list-add-or-subst (vl-bb-ref '*kpblc-settings*) "log" param)
- ) ;_ end of vl-bb-set
- (princ (strcat "\nЛог "
- (if param
- "запущен"
- "остановлен"
- ) ;_ end of if
- ) ;_ end of strcat
- ) ;_ end of princ
- (princ)
- ) ;_ end of defun
Что здесь происходит? Указанная настройка (вести лог или нет) записывается во внедокументную перемнную *kpblc-settings*. Т.е. указанная настройка будет доступна во всех документах текущей сессии AutoCAD. Такое решение было обусловлено тем, что иногда при открытии какого-либо файла мне уже надо было вести лог выполняющихся действий. Но это мое решение...
Точно так же потребуется одна служебная функция замены элемента в списке:
- (defun _kpblc-list-add-or-subst (lst key value)
- ;|
- * Производит замену или дополнение элемента списка новым
- * Параметры вызова:
- lst обрабатываемый список
- key ключ
- value устанавливаемое значение
- |;
- (if (not value)
- (vl-remove-if (function (lambda (x) (= (car x) key))) lst)
- (if (cdr (assoc key lst))
- (subst (cons key value) (assoc key lst) lst)
- (cons (cons key value)
- (vl-remove-if
- (function
- (lambda (x)
- (= (car x) key)
- ) ;_ end of lambda
- ) ;_ end of function
- lst
- ) ;_ end of vl-remove-if
- ) ;_ end of cons
- ) ;_ end of if
- ) ;_ end of if
- ) ;_ end of defun
Теперь можно приступать уже собственно к разработке функции записи в лог какой-либо информации. Вопрос фактически один: а что мы собираемся вообще записывать?
- Нам может понадобиться время (с точностью, например, до секунд). А может и не понадобиться
- Точно так же может понадобиться указание имени выполняемой lisp-функции
- В некоторых случаях лог должен записываться невзирая на установленные "переключателем" настройки
- Может понадобиться дополнительное пояснение
- В обязательном варианте записываем полное имя текущего документа. Если документ еще не сохранялся, то в качестве имени пишем "Файл не сохранен"
Получается нечто типа:
- (defun _kpblc-log (lst / handle sep)
- ;|
- * выполняет запись сообщения в лог
- * Параметр вызова:
- lst список дополнительных параметров
- '(("time" . <Указывать время>)
- ("cmd" . <Указывается имя функции>)
- ("req" . <Лог выполнять в любом случае>)
- ("msg" . <Дополнительное пояснение>)
- )
- |;
- (if (or (cdr (assoc "req" lst))
- (cdr (assoc "log" (vl-bb-ref '*kpblc-settings*)))
- ) ;_ end of or
- (progn
- (setq sep "\t"
- handle (open (_kpblc-get-file-log) "a")
- ) ;_ end of setq
- (write-line
- (strcat
- (if (= (vla-get-fullname (vla-get-activedocument (vlax-get-acad-object))) "")
- "Файл не сохранен"
- (strcat (vl-string-right-trim "\\" (getvar "dwgprefix")) "\\" (getvar "dwgname"))
- ) ;_ end of if
- sep
- (cond ((cdr (assoc "cmd" lst)))
- (t "Имя lisp не указано")
- ) ;_ end of cond
- sep
- (if (cdr (assoc "time" lst))
- (_kpblc-conv-date-to-string)
- ""
- ) ;_ end of if
- (if (cdr (assoc "msg" lst))
- (strcat sep (cdr (assoc "msg" lst)))
- ""
- ) ;_ end of if
- ) ;_ end of strcat
- handle
- ) ;_ end of write-line
- (close handle)
- ) ;_ end of progn
- ) ;_ end of if
- ) ;_ end of defun
Потребуется функция преобразования текущей даты в формат "ГГГГ-ММ-ДД чч:мм:сс":
- (defun _kpblc-conv-date-to-string (/ date sdate stime)
- ;|
- * Преобразовывает текущую дату и время в строковое представление
- |;
- (setq date (getvar "cdate")
- sdate (fix date)
- stime (- date sdate)
- sdate (itoa sdate)
- stime (itoa (fix (* stime 1e6)))
- ) ;_ end of setq
- (while (< (strlen stime) 6)
- (setq stime (strcat "0" stime))
- ) ;_ end of while
- (strcat (substr sdate 1 4)
- "-"
- (substr sdate 5 2)
- "-"
- (substr sdate 7)
- " "
- (substr stime 1 2)
- ":"
- (substr stime 3 2)
- ":"
- (substr stime 5)
- ) ;_ end of strcat
- ) ;_ end of defun
Теперь осталось только нарисовать "показ" лога. Ну, тут уже совсем просто:
- (defun _kpblc-log-show (/ file)
- ;|
- * Выводит окно лога в отдельном приложении (блокнот)
- |;
- (if (setq file (findfile (_kpblc-get-file-log)))
- (startapp "notepad.exe"
- (_kpblc-get-file-log)
- ) ;_ end of startapp
- (alert "Файл лога не обнаружен!")
- ) ;_ end of if
- (princ)
- ) ;_ end of defun
Немного пояснений. В качестве разделителя данных в лог-файле выбран символ табуляции: это позволит, сменив расширение на csv, выполнить открытие лога в Excel / LibreOffice / etc и выполнить практически любые действия.
В качестве примера использования функций:
- (defun test-log ()
- (_kpblc-log '(("req" . t) ("time" . t) ("cmd" . "test")))
- (_kpblc-log '(("req" . t) ("time" . t) ("cmd" . "test") ("msg" . "Проверочное сообщение")))
- (entmakex (list (cons 0 "LINE")
- (cons 10 '(0. 0. 0.))
- (cons 11 '(10. 10. 0.))
- ) ;_ end of list
- ) ;_ end of entmakex
- (_kpblc-log '(("time" . t) ("cmd" . "test") ("msg" . "Необязательное сообщение")))
- (princ)
- ) ;_ end of defun
В логе будет нечто типа:
Файл не сохранен test 2015-11-26 16:44:44Файл не сохранен test 2015-11-26 16:44:44 Проверочное сообщение
Переключим выполнение лога:
- (_kpblc-log-toggle t)
И снова вызовем (test-log):
Файл не сохранен test 2015-11-26 16:44:44Файл не сохранен test 2015-11-26 16:44:44 Проверочное сообщение
Файл не сохранен test 2015-11-26 16:51:44
Файл не сохранен test 2015-11-26 16:51:44 Проверочное сообщение
Файл не сохранен test 2015-11-26 16:51:44 Необязательное сообщение
Как видно, добавилось "необязательное" сообщение.
Все коды, кроме "тестировочного", можно забрать здесь
Автор: Алексей Кулик
Обсуждение: http://adn-cis.org/forum/index.php?topic=3238
Опубликовано 27.11.2015