Хорошей новостью является то, что вам не нужно ничего делать для обеспечения безопасности потоков, и вам не нужно ничего лишнего или что-то почти тривиальное для чистого выключения. Я расскажу подробности позже.
Плохая новость заключается в том, что у вашего кода есть серьезная проблема, даже до того, как вы доберетесь до этого момента: fileLogger
и consoleLogger
- это тот же объект. От the documentation for getLogger()
:
Верните регистратор с указанным именем или, если имя не указано, верните регистратор, который является корневым регистратором иерархии.
Таким образом, вы получаете корневой регистратор и хранить его в качестве fileLogger
, а затем вы получаете корневой регистратор и хранить его в качестве consoleLogger
. Итак, в LoggingInit
вы инициализируете fileLogger
, затем повторно инициализируете один и тот же объект под другим именем с разными значениями.
Вы может добавить несколько обработчиков к тому же регистратору-и, так как только инициализация вы на самом деле для каждого является addHandler
, ваш код будет сортировать работы по назначению, но только случайно. И только вроде. Вы получите две копии каждого сообщения в обоих журналах, если вы пройдете print_screen=True
, и вы получите копии в консоли, даже если вы пройдете print_screen=False
.
На самом деле нет причин для глобальных переменных; вся точка getLogger()
заключается в том, что вы можете вызывать ее каждый раз, когда вам это нужно, и получать глобальный корневой журнал, поэтому вам не нужно его хранить в любом месте.
Еще одна проблема заключается в том, что вы не избегаете текста, который вы вставляете в HTML. В какой-то момент вы попытаетесь записать строку "a < b"
и попадете в беду.
Менее серьезно, последовательность <p>
тегов, которые не находятся внутри <body>
внутри <html>
, не является допустимым документом HTML. Но множество зрителей позаботятся об этом автоматически, или вы можете отправить свои журналы тривиально перед их отображением.Но если вы действительно хотите, чтобы это было правильно, вам нужно подклассы FileHandler
и добавьте заголовок __init__
, если задан пустой файл, и удалите нижний колонтитул, если он есть, а затем добавьте нижний колонтитул close
.
Возвращаясь к вашему фактическому вопрос:
Вам не нужно никакого дополнительного запирания. Если обработчик правильно реализует createLock
, acquire
и release
(и он называется на платформе с потоками), система ведения журнала автоматически обязательно приобретет блокировку, когда необходимо, чтобы каждое сообщение регистрировалось атомарно.
Насколько я знаю, эта документация не непосредственно сказать, что StreamHandler
и FileHandler
реализации этих методов, она сильно подразумевает его (the text you mentioned in the question говорит «Модуль регистрации предназначен для поточно-без каких-либо специальных работ которые должны выполняться его клиентами »и т. д.). И вы можете посмотреть источник для своей реализации (например, CPython 3.3) и увидеть, что они оба наследуют правильно реализованные методы от logging.Handler
.
Аналогично, если обработчик правильно реализует flush
и close
, лесозаготовительная техника будет убедиться, что он правильно завершен во время нормального завершения работы.
В этой документации объясняется, что StreamHandler.flush()
, FileHandler.flush()
и FileHandler.close()
. В основном это то, что вы ожидаете, за исключением того, что StreamHandler.close()
- это не-op, что означает, что окончательные сообщения журнала на консоли могут потеряться. Из документов:
Обратите внимание, что метод close()
наследуется от Handler
и поэтому не делает никакого вывода, поэтому явный flush()
вызов может понадобиться в разы.
Если это важно для вас, и вы хотите, чтобы исправить это, вам нужно сделать что-то вроде этого:
class ClosingStreamHandler(logging.StreamHandler):
def close(self):
self.flush()
super().close()
, а затем использовать ClosingStreamHandler()
вместо StreamHandler()
.
FileHandler
не имеет такой проблемы.
Обычный способ отправить логи в два места, чтобы просто использовать корневой регистратор с двумя манипуляторами, каждый со своим собственным форматировщиком.
Кроме того, даже если вам нужны два регистратора, вам не нужны отдельные карты console_logging_level_switch
и file_logging_level_switch
; вызов Logger.debug(msg)
- это точно то же самое, что и вызов Logger.log(DEBUG, msg)
. Вам все равно нужно каким-то образом сопоставить свои пользовательские имена уровней debug
и т. Д. С стандартными именами DEBUG
и т. Д., Но вы можете просто выполнить один поиск вместо того, чтобы делать это один раз для каждого регистратора (плюс, если ваши имена - это просто стандартные имена с разными литыми, вы можете обмануть).
Все это хорошо описано в разделе «Multiple handlers and formatters» и остальной части кулинарной книги по лесозаготовкам.
Единственная проблема со стандартным способом сделать это состоит в том, что вы не можете легко отключить ведение журнала консоли на основе сообщений за сообщением. Это потому, что это не нормально. Обычно вы просто регистрируетесь по уровням и устанавливаете уровень журнала выше в журнале файлов.
Но, если вы хотите большего контроля, вы можете использовать фильтры. Например, дайте FileHandler
фильтр, который принимает все, и ваш ConsoleHandler
фильтр, который требует что-то, начиная с console
, а затем воспользуйтесь фильтром 'console' if print_screen else ''
. Это уменьшает WriteLog
почти до одного лайнера.
Вам по-прежнему нужны дополнительные две строки для удаления новых строк, но вы можете даже сделать , что в фильтре или через адаптер, если хотите. (Опять же, см. Поваренную книгу.) И затем WriteLog
действительно is однострочный.
Если вы не ставите блокировку, когда хотите писать, ваши записи в журнале могут смешиваться и делать нечитаемый журнал. – AliBZ
@AliBZ - какие записи журнала? на уровне ведения журнала? не покрываются ли они службой каротажа python? http://stackoverflow.com/questions/2973900/is-pythons-logging-module-thread-safe –
, когда вы используете «fileLogger.info ('1 2 3 4')» в двух разных потоках, ваш последний журнал может быть их смесь, что-то вроде этого «1 2 1 2 3 3 4 4» – AliBZ