Ведение журнала – Django Документация на русском

abdcdbfbedefadc Новости

Java.util.logging

Данный фреймворк включен в стандарт и поставляется вместе с JDK, поэтому ничего дополнительно скачивать и подключать вам не надо. JUL имеет следующие уровни логгирования по возрастанию: FINEST, FINER, FINE, CONFIG, INFO, WARNING, SEVERE, а так же ALL и OFF, включающий и отключающий все уровни соответственно.

Логгер создается вызовом одного из статических методов класса java.util.logging.Logger:

Logger log = Logger.getLogger(LoggingJul.class.getName());

Методы логгера могут принимать в качестве аргументов строковые сообщения, шаблоны сообщений, исключения, ресурсы локализованных текстовок сообщений, а также, начиная с Java 8, поставщиков строковых сообщений:

// Строковое сообщение
String stringMessage = "Сообщение";
// Строковое сообщение с параметрами
String stringMessageFormat ="Сообщение {0}";
// Исключение
Throwable throwable = new Throwable();
// ResourceBundle хранящий сообщения
ResourceBundle resourceBundle = ResourceBundle.getBundle("logging.jul.bundle");
// Поставщик сообщений
Supplier<String> stringMessageSupplier = ()->"Сообщение";

Выделяется две группы методов: название которых соответствует уровню логгирования и методы log, loggp, logrb, принимающие уровень логгирования в качестве параметра с типом Level. Первая группа содержит методы двух типов: принимающих строковое сообщение или поставщика строковых сообщений:

log.info(stringMessage);
log.info(stringMessageSupplier);

Вторая группа методов имеет следующие вариации:

// Вывести сообщение с указанием уровня логгирования
log.log(new LogRecord(Level.INFO, stringMessage));
log.log(Level.INFO, stringMessage);
log.log(Level.INFO, stringMessageSupplier);
log.log(Level.INFO, stringMessageFormat, args);
log.log(Level.INFO, stringMessage, throwable );
log.log(Level.INFO, throwable, stringMessageSupplier);
// Вывести сообщение с указанием уровня логгирования, класса и метода
log.logp(Level.INFO, "ClassName", "MethodName", stringMessage);
log.logp(Level.INFO, "ClassName", "MethodName", stringMessageSupplier);
log.logp(Level.INFO, "ClassName", "MethodName", stringMessageFormat, args);
log.logp(Level.INFO, "ClassName", "MethodName", stringMessage, throwable);
log.logp(Level.INFO, "ClassName", "MethodName", throwable, stringMessageSupplier);
// Вывести сообщение с указанием уровня логгирования, класса,
// метода и resourceBundle, хранящего сообщения
log.logrb(Level.INFO, "ClassName", "MethodName", resourceBundle, "messageId");
log.logrb(Level.INFO, "ClassName", "MethodName", resourceBundle, "messageId", throwable);
// Вывести сообщение об ошибке
log.throwing("ClassName","MethodName", throwable);

Теперь обратимся к конфигурации фреймворка. По умолчанию JUL будет выводить сообщения на консоль, однако можно задать конфигурацию в файле свойств. Для задания способа вывода сообщений необходимо для вашего логгера указать какие хендлеры он будет использовать.

Существует следующие классы хендлеров: FileHandler, ConsoleHandler, StreamHandler, SocketHandler, MemoryHandler. Особенностью JUL является то, что настройки хендлеров задаются в целом для всего класса, а не для конкретного экземпляра, что может порождать не мало проблем, например если вам потребуется сообщения различных логгеров выводить в различные файлы или с различным форматированием. Рассмотрим простой пример конфигурационного файла:

# Настройки глобального логгера
handlers =java.util.logging. FileHandler
.level=ALL
# Конфигурация файлового хендлера
java.util.logging.FileHandler.level =ALL
java.util.logging.FileHandler.formatter =java.util.logging.SimpleFormatter
java.util.logging.FileHandler.limit = 1000000
java.util.logging.FileHandler.pattern   = log.txt
# Конфигурация консольного хендлера
java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.pattern = log.log
java.util.logging.ConsoleHandler.formatter =java.util.logging.SimpleFormatter

Для того что бы JUL применил данную конфигурацию нужно передать параметр -Djava.util.logging.config.file = , либо при старте приложения выполнить код:

LogManager.getLogManager().readConfiguration(<ваш класс>.class.getResourceAsStream("logging.properties"));

Log4j

Данный фреймворк на текущий момент имеет уже вторую версию, которая увы не совместима с первой. Поскольку первая версия log4j существует достаточно давно и, в виду ее большой популярности, существует не мало статей на просторах интернета, сегодня мы рассмотрим вторую. Для использования log4j2 вам необходимо подключить библиотеки

. Log4j имеет несколько отличное от JUL именование уровней логгирования: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, а так же ALL и OFF включающий и отключающий все уровни соответственно.


Логгер создается вызовом статического метода класса org.apache.logging.log4j.Logger:

Logger log = LogManager.getLogger(LoggingLog4j.class);
// или
Logger log = LogManager.getLogger(“name”);

Логгер умеет принимать помимо привычных нам String, Object и Throwable еще два новых типа — MapMessage и Marker:

// Карта сообщений (напечатается как msg1="Сообщение 1” msg2="Сообщение 2”)
MapMessage mapMessage = new MapMessage();  
mapMessage.put("msg1", "Сообщение 1");
mapMessage.put("msg2", "Сообщение 2");
// Маркер, объект по которому можно фильтровать сообщения
Marker marker = MarkerManager.getMarker("fileonly");
// Строковое сообщение
String stringMessage = "Сообщение";
// Строковое сообщение с параметрами
String stringMessageFormat = "Сообщение {}, от {}";
// Исключение
Throwable throwable = new Throwable();
// Объект
Object object = new Object();

В классическом для логгеров стиле методы делятся на два типа: совпадающие с названием уровня логгирования и методы log, принимающие уровень логгирования в качестве параметра. Первые имеют вид:

log.info(mapMessage);
log.info(object);
log.info(stringMessage);
log.info(marker, mapMessage);
log.info(marker, object);
log.info(marker, stringMessage);
log.info(object, throwable);
log.info(stringMessage, throwable);
log.info(stringMessageFormat, args);
log.info(marker, mapMessage, throwable);
log.info(marker, object, throwable);
log.info(marker, stringMessageFormat, args);
log.info(marker, stringMessage, throwable);
log.throwing(throwable);

Методы log в log4j2 выглядят так:

log.log(Level.INFO, mapMessage);
log.log(Level.INFO, object);
log.log(Level.INFO, stringMessage);
log.log(Level.INFO, marker, mapMessage);
log.log(Level.INFO, marker, object);
log.log(Level.INFO, marker, stringMessage);
log.log(Level.INFO, object, throwable);
log.log(Level.INFO, stringMessageFormat, args);
log.log(Level.INFO, stringMessage, throwable);
log.log(Level.INFO, marker, mapMessage, throwable);
log.log(Level.INFO, marker, object, throwable);
log.log(Level.INFO, marker, stringMessageFormat, args);
log.log(Level.INFO, marker, stringMessage, throwable);
log.throwing(Level.INFO, throwable);

Если не определить конфигурацию, то при запуске log4j2 выдаст гневное сообщение, о том, что конфигурация не задана и будет печатать ваши сообщения на консоль уровнем не ниже ERROR. Конфигурация log4j2 задается несколькими вариантами: xml, json, yaml.

Конфигурация log4j2 состоит из описания логгеров, аппендеров и фильтров. Для более детального изучения обратитесь к документации, сейчас же лишь отметим пару ключевых моментов. Во-первых, есть различные вкусности в виде фильтров, в том числе и по маркерам:

Во-вторых, имеется широкий круг классов аппендеров, в том числе асинхронные аппендеры и аппендеры оборачивающие группу других аппендеров:

Стоит также заметить, что log4j может создавать множество различающихся аппендеров одного и того же класса, например несколько файловых аппендеров, которые пишут в разные файлы.


Рассмотрим пример конфигурации, в которой объявлены два логгера (корневой и для нашего класса), первый из которых пишет в файл log.log, а второй пишет в log2.log с использованием фильтрации по маркеру:

Logback

Данный фреймворк используется только в связке с оберткой SLF4J, которую мы будем рассматривать позднее. Для начала работы вам необходимы

, а также


Взаимодействие с логгером мы будем осуществлять через API предоставляемый оберткой SLF4J. Уровни логгирования совпадают с log4j. Создание логгера в таком случае выглядит следующим образом:

org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LoggingLogback.class);
// или
org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger("name");

API позволяет выводить строковые сообщения, шаблоны строковых сообщений, исключения, а также использовать маркеры:

// Строковое сообщение
String stringMessage = "Сообщение";
// Шаблон сообщения
String stringMessageFormat = "Сообщение {} {}";
// Ошибка
Throwable throwable = new Throwable();
// Маркер
Marker marker = MarkerFactory.getMarker("marker");

Названия методов совпадают с уровнями логгирования и имеют вид:

log.info(stringMessage);
log.info(stringMessageFormat, args);
log.info(stringMessage, throwable);
log.info(marker, stringMessage);
log.info(marker, stringMessage, throwable);
log.info(marker,stringMessageFormat, args);


Теперь рассмотрим непосредственны функционал logback. Конфигурация ищется в classpath в следующем порядке:

  1. Пытается найти logback.groovy
  2. Иначе пытается найти logback-test.xml
  3. Иначе пытается найти logback.xml
  4. Иначе использует базовую конфигурацию — выводим сообщения на консоль

Основными элементами конфигурации являются логгеры, аппендеры, лайауты, и фильтры.

Имеются следующие фильтры:


Имеются следующие аппендеры:

О том что такое Layouts и Encoders в logback предлагаю прочитать подробно в документации, а сейчас лишь приведу простой пример файла logback.xml:

Slf4j

Как уже говорилось ранее SLF4J является оберткой над logback, а также над JUL, log4j, или JCL, а также над любым логгером, который реализует ее интерфейс. Для работы с SLF4J нужны библиотека slf4j-api-1.x.x.jar и реализация одного из логгеров либо заглушка.

Как правило реализации всех логгеров ( кроме logback) поставляются вместе с SLF4J и имеют названия на подобии slf4j-jcl-1.x.jar, slf4j-log4j12-1.x.jar, slf4j-nop-1.x.jar и т.п. Если в classpath не будет найдена реализация логгера ( или заглушка nop)

API SLF4J мы рассмотрели в предыдущем пункте, поэтому давайте рассмотрим еще одну возможность обертки. В идеальном мире мы должны выводить сообщения через интерфейс обертки, и тогда у нас все будет хорошо, но реальный жестокий мир говорит о том, что всем нам приходится взаимодействовать со сторонними библиотеками или кодом, в которых используются другие логгеры и которые знать не знают о SLF4J.

Что бы не подстраиваться под каждый логгер, а пустить все сообщения через одну реализацию интерфейса SLF4J, можно использовать bridging. В поставке обертки содержаться библиотеки jcl-over-slf4j.jar, log4j-over-slf4j.jar и jul-to-slf4j.jar, которые переопределяют поведение соответствующих логгеров и перенаправляют сообщения в обертку.

Что бы стало понятнее выше сказанное, рассмотрим пример. Допустим у нас имеются следующие логгеры:

java.util.logging.Logger  julLog = java.util.logging.Logger.getLogger("julLog");
java.util.logging.Logger  log4jLog = java.util.logging.Logger.getLogger("log4jLog");
org.slf4j.Logger          slf4jLog = org.slf4j.LoggerFactory.getLogger(LoggingSlf4j.class);

julLog.info("Сообщение от jul");
log4jLog.info("Сообщение от log4j");
slf4jLog.info("Сообщение от slf4j");


Мы хотим, что бы сообщение от JUL записывались в один файл, от log4j в другой, а от slf4j выводились на консоль. В качестве реализации обертки будем использовать logback, конфигурация сего безобразия будет выглядеть следующим образом:

Гарантии сохранности лога

Несмотря на некоторые возможности NLog по авто записи логов, нет гарантии сохранности лога при завершении процесса.

Что интересно, попытка завершить запись обработкой события AppDomain.ProcessExit не совсем корректна. В конфигурации может быть настроено много разных способов записи в лог, в том числе и по сети. А обработчик этого события находится в ограниченном окружении. В .

Первое, что следует сделать, это обработать событие AppDomain.UnhandledException. В нем следует записать в лог полную информацию об ошибке и вызвать LogManager.Flush(). Обработчик этого события использует тот же поток, который и вызвал исключение, а по окончании, немедленно выгружает приложение.private static readonly Logger Log = LogManager.

GetCurrentClassLogger();<br/><br/>public static void Main(string[] args)<br/>{<br/>AppDomain.

CurrentDomain.UnhandledException  = OnUnhandledException;<br/>(…)<br/>LogManager.Flush();<br/>}<br/><br/>static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)<br/>{<br/>Log.

Fatal(“Unhandled exception: {0}”, e.ExceptionObject);<br/>LogManager.Flush();<br/>}

Кроме того, следует вызывать LogManager.Flush() везде, где потенциально возможно завершение процесса. В конце всех не фоновых потоков.

Если ваше приложение представляет собой win-service или Asp.Net, то следует обработать соответствующие события начала и завершения кода.

Жизненный цикл vspehere снапшотов

…и места, где они обитают.

Часто (а на самом деле практически всегда) понимать, в какой момент создавался снапшот, в какой был удалён и что с ним происходило, весьма важно для получения целостной картины мира реплик и бекапов. Поэтому открываем лог таски и ищем в нём API вызовы для создания и консолидации (удаления) снапшотов. Создание снапшота отлично ищется по Create snapshot

[02.11.2020 05:01:09] <29> Info [VimApi] Create snapshot, ref "vm-26", name "VEEAM BACKUP TEMPORARY SNAPSHOT", description "Please do not delete this snapshot. It is being used by Veeam Backup.", memory "False", quiesce "False"
[02.11.2020 05:01:11] <29> Info [Soap] Snapshot VM 'vm-26' was created. Ref: 'snapshot-540'

Удаление снапшота ищется по Removing snapshot.

[01.11.2020 05:02:30] <26> Info [Soap] Removing snapshot 'snapshot-536'
[01.11.2020 05:02:30] <26> Info [VimApi] RemoveSnapshot, type "VirtualMachineSnapshot", ref "snapshot-536", removeChildren "False"
[01.11.2020 05:02:32] <26> Info [VmSnapshotTracker] Snapshot id: 'snapshot-536' closed
[01.11.2020 05:02:32] <26> Info [Soap] Loaded 2 elements
[01.11.2020 05:02:32] <26> Info [ViSnapshot] Checking if disks consolidation is needed for vm 'vm-26'
[01.11.2020 05:02:32] <26> Info [ViSnapshot] Consolidation is not needed

Здесь что хочется отметить: по загадочной причине в логе VMware.log зачастую нет никакой информации об ошибках произошедших во время создания/удаления снапшота. Поэтому полную картину мира надо восстанавливать по всем логам vSphere, включая логи VPXD и HOSTD.

С точки зрения vSphere, в VMware.log обычно есть только события на создание снапшота:

2021-05-24T13:58:57.921Z| vmx| I125: SnapshotVMX_TakeSnapshot start: 'VEEAM BACKUP TEMPORARY SNAPSHOT', deviceState=0, lazy=0, quiesced=0, forceNative=0, tryNative=1, saveAllocMaps=0 cb=35DE089A0, cbData=35F700420 
....
2021-05-24T13:59:01.111Z| vcpu-0| I125: VigorTransport_ServerSendResponse opID=a9e2725d-8d3e-41c3-bea7-40d55d4d13fe-3088830-h5c:70160745-a8-af-3321 seq=187319: Completed Snapshot request. 

И на удаление:

2021-05-24T13:59:31.878Z| vmx| I125: SNAPSHOT: SnapshotDeleteWork '/vmfs/volumes/5b0c32e4-3bc067bb-614b-f4e9d4a8b220/test_build/test_build.vmx' : 29
....
2021-05-24T13:59:35.411Z| vcpu-0| I125: ConsolidateEnd: Snapshot consolidate complete: The operation completed successfully (0).

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

Зато как только мы заводим разговор о репликах, всё становится ещё веселее. VMware.log ведётся только когда виртуалка работает. А для реплицированных машин нормальное состояние — быть выключенными. Так что этот лог вам никак помочь не может. Поэтому здесь нам понадобятся логи vCenter или хоста.

Самый простой способ их добыть — это ПКМ на vCenter > Export System Logs. В открывшемся визарде обязательно выбираем “Include vCenter server logs” и дожидаемся скачивания результата. Логи влёгкую могут весить несколько гигабайт, а скачиваться будут через браузер. Так что советую делать это на машине максимально близкой к VC. Самое интересное для нас внутри, это:

Теперь давайте сравним, как будут выглядеть записи об одних и тех же операция в логе VBR, логе vpxd, и логе hostd. Здесь мы опустим моменты создания снапшота на исходной машине, так как нас интересует только реплика.

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

//Veeam Task log:
[19.05.2020 18:41:04] <20> Info [SnapReplicaVm] Find working snapshots for replica vm '[name 'TinyLinux2_replica', ref 'vm-3411']'
[19.05.2020 18:41:04] <20> Info [SnapReplicaVm] Reverting vm '[name 'TinyLinux2_replica', ref 'vm-3411']' to restore point '5/19/2020 15:37:13'
[19.05.2020 18:41:04] <20> Info [Soap] Reverting snapshot snapshot-3415
[19.05.2020 18:41:04] <20> Info [VimApi] RevertSnapshot, type "VirtualMachineSnapshot", ref "snapshot-3415"
//VPXD:
2020-05-19T15:41:00.508Z info vpxd[04767] [Originator@6876 sub=vpxLro opID=7717265a] [VpxLRO] -- BEGIN task-32169 -- snapshot-3415 -- vim.vm.Snapshot.revert -- 52e02fc8-9bbb-74da-63ac-74879274c5a3(52e50ab9-39d5-0996-21ce-3877ead7a6e9)

Использование журнала ¶

После того, как вы настроили свои регистраторы, обработчики, фильтры и средства форматирования, вам необходимо поместить вызовы регистрации в свой код. Использование фреймворка ведения журнала работает следующим образом:

Вот и все! Каждый раз, когда bad_mojoусловие активируется, будет записываться запись в журнал ошибок.

Именование логгеров

Вызов для logging.getLogger()получения (создания, если необходимо) экземпляра регистратора. Экземпляр регистратора идентифицируется по имени. Это имя используется для идентификации регистратора в целях настройки.

По соглашению, имя регистратора обычно __name__является именем модуля Python, который содержит регистратор. Это позволяет фильтровать и обрабатывать вызовы журналирования для каждого модуля. Однако, если у вас есть другой способ организации сообщений журнала, вы можете указать любое имя, разделенное точками, для идентификации вашего регистратора:

Пунктирные пути имен регистраторов определяют иерархию.
project.interestingРегистратор считается родителем
project.interesting.stuffрегистратора; projectрегистратор является родителем project.interestingрегистратора.

Почему важна иерархия? Ну, потому что регистраторы могут быть настроены на
передачу своих вызовов регистрации своим родителям. Таким образом, вы можете определить единый набор обработчиков в корне дерева регистраторов и захватывать все вызовы регистрации в поддереве регистраторов. Регистратор определен в projectпространстве имен будет перехватывать все сообщения протоколирования , выданные на project.interestingи project.interesting.stuffлесорубов.

Это распространение можно контролировать для каждого регистратора. Если вы не хотите, чтобы конкретный регистратор передавался своим родителям, вы можете отключить это поведение.

Краткое руководство по ведению журнала ¶

Django использует встроенный loggingмодуль Python для ведения системного журнала. Использование этого модуля подробно обсуждается в собственной документации Python.

Состав игроков

Конфигурация ведения журнала Python состоит из четырех частей:

Логгеры

Регистратор – это точка входа в систему регистрации. Каждый регистратор представляет собой именованную корзину, в которую можно записывать сообщения для обработки.

Регистратор настроен на уровень ведения журнала . Этот уровень журнала описывает серьезность сообщений, обрабатываемых регистратором. Python определяет следующие уровни журнала:

Каждое сообщение, записываемое в регистратор, является записью журнала . Каждая запись журнала также имеет уровень журнала, указывающий серьезность этого конкретного сообщения. Запись журнала также может содержать полезные метаданные, описывающие регистрируемое событие. Это может включать такие детали, как трассировка стека или код ошибки.

Когда сообщение отправляется регистратору, уровень журнала сообщения сравнивается с уровнем журнала регистратора. Если уровень журнала сообщения соответствует уровню журнала самого регистратора или превышает его, сообщение будет подвергнуто дальнейшей обработке. В противном случае сообщение будет проигнорировано.

Как только регистратор определил, что сообщение необходимо обработать, оно передается обработчику .

Обработчики

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

Как и регистраторы, обработчики также имеют уровень ведения журнала. Если уровень журнала записи журнала не соответствует или превышает уровень обработчика, обработчик проигнорирует сообщение.

У регистратора может быть несколько обработчиков, и каждый обработчик может иметь свой уровень журнала. Таким образом, можно предоставить различные формы уведомления в зависимости от важности сообщения. Например, вы можете установить один обработчик, который пересылает ERRORи
CRITICALсообщения службе подкачки, в то время как второй обработчик регистрирует все сообщения (включая ERRORи CRITICALсообщения) в файле для последующего анализа.

Фильтры

Фильтр используется для обеспечения дополнительного контроля над тем, какие записи журнала передаются от регистратора к обработчику.

По умолчанию обрабатывается любое сообщение журнала, соответствующее требованиям уровня журнала. Однако, установив фильтр, вы можете добавить дополнительные критерии к процессу ведения журнала. Например, вы можете установить фильтр, который разрешает ERRORотправку сообщений только из определенного источника.

Фильтры также можно использовать для изменения записи журнала перед отправкой. Например, вы можете написать фильтр, который понижает рейтинг
ERRORзаписей журнала до WARNINGзаписей, если выполняется определенный набор критериев.

Фильтры могут быть установлены на регистраторах или на обработчиках; несколько фильтров можно использовать в цепочке для выполнения нескольких действий фильтрации.

Логгеры ¶

Django предоставляет несколько встроенных регистраторов.

django.db.backends

Сообщения, относящиеся к взаимодействию кода с базой данных. Например, каждый оператор SQL уровня приложения, выполняемый запросом, регистрируется на
DEBUGуровне этого регистратора.

Сообщения в этот регистратор имеют следующий дополнительный контекст:

По соображениям производительности ведение журнала SQL включается, только если для
settings.DEBUGнего установлено значение True, независимо от уровня ведения журнала или установленных обработчиков.

Эта регистрация не включает в себя инициализацию рамки уровня (например
) или запросы управления транзакциями (например ,
, и ). Включите ведение журнала запросов в своей базе данных, если вы хотите просмотреть все запросы к базе данных.SETTIMEZONEBEGINCOMMITROLLBACK

django.security.*

Регистраторы безопасности будут получать сообщения о любых
SuspiciousOperationошибках, связанных с безопасностью. Для каждого подтипа ошибки безопасности, включая все ошибки, есть подзаголовок
SuspiciousOperation. Уровень события журнала зависит от того, где обрабатывается исключение. Большинство событий регистрируется как предупреждение, тогда как любое сообщение SuspiciousOperation, достигнутое обработчиком WSGI, регистрируется как ошибка. Например, когда HTTP- Hostзаголовок включен в запрос от клиента, который не соответствует ALLOWED_HOSTS, Django вернет ответ 400, а сообщение об ошибке будет записано в
django.security.DisallowedHostрегистратор.

Эти события djangoжурнала по умолчанию будут поступать в средство ведения журнала, которое присылает сообщения об ошибках администраторам DEBUG=False. Запросы, приводящие к ответу 400 из-за a SuspiciousOperation, не будут регистрироваться в django.request
регистраторе, а только в django.securityрегистраторе.

Чтобы отключить определенный тип SuspiciousOperation, вы можете переопределить этот конкретный регистратор, следуя этому примеру:

Другие django.securityрегистраторы, не основанные на SuspiciousOperation:

Настройка логирования ¶

Недостаточно просто поместить в код вызовы логирования. Вам также необходимо настроить средства ведения журнала, обработчики, фильтры и средства форматирования, чтобы обеспечить возможность использования вывода журнала.

Библиотека ведения журнала Python предоставляет несколько методов настройки ведения журнала, от программного интерфейса до файлов конфигурации. По умолчанию Django использует формат dictConfig .

Чтобы настроить ведение журнала, вы используете LOGGINGсловарь настроек ведения журнала. Эти параметры описывают средства ведения журнала, обработчики, фильтры и средства форматирования, которые вы хотите использовать в настройках ведения журнала, а также уровни журналов и другие свойства, которые должны иметь эти компоненты.

По умолчанию этот LOGGINGпараметр объединяется с конфигурацией ведения журнала Django по умолчанию, используя следующую схему.

Если для disable_existing_loggersключа в LOGGINGdictConfig задано значение True(что является dictConfigзначением по умолчанию, если ключ отсутствует), то все регистраторы из конфигурации по умолчанию будут отключены.

Отключенные регистраторы – это не то же самое, что удаленные; регистратор по-прежнему будет существовать, но молча отбрасывает все, что было зарегистрировано в нем, даже не передавая записи в родительский регистратор. Таким образом, вы должны быть очень осторожны при использовании
; вероятно, это не то, что вы хотите.

Вместо этого, вы можете установить , чтобы и переопределить некоторые или все регистраторы по умолчанию; или вы можете установить , чтобы
и ручка каротаж конфиг себя .’disable_existing_loggers’:Truedisable_existing_loggersFalseLOGGING_CONFIGNone

Ведение журнала настраивается как часть общей setup()функции Django . Таким образом, вы можете быть уверены, что регистраторы всегда готовы к использованию в коде вашего проекта.

Примеры

Полная документация по формату dictConfig
– лучший источник информации о словарях конфигурации журналирования. Однако, чтобы дать вам представление о том, что возможно, вот несколько примеров.

Для начала вот небольшая конфигурация, которая позволит вам выводить все сообщения журнала на консоль:

Это настраивает родительский rootрегистратор для отправки сообщений с
WARNINGуровнем и выше обработчику консоли. Установив уровень на
INFOили, DEBUGвы можете отображать больше сообщений. Это может быть полезно во время разработки.

Затем мы можем добавить более детальное ведение журнала. Вот пример того, как заставить систему журналирования печатать больше сообщений только из django с именем logger:

importosLOGGING={'version':1,'disable_existing_loggers':False,'handlers':{'console':{'class':'logging.StreamHandler',},},'root':{'handlers':['console'],'level':'WARNING',},'loggers':{'django':{'handlers':['console'],'level':os.getenv('DJANGO_LOG_LEVEL','INFO'),'propagate':False,},},}

По умолчанию эта конфигурация отправляет сообщения от djangoрегистратора уровня
INFOили выше на консоль. Это тот же уровень, что и конфигурация ведения журнала по умолчанию в Django, за исключением того, что конфигурация по умолчанию отображает записи журнала только тогда, когда
DEBUG=True. Django не регистрирует много INFOсообщений такого уровня. Однако с помощью этой конфигурации вы также можете установить переменную среды
DJANGO_LOG_LEVEL=DEBUGдля просмотра всех журналов отладки Django, которые очень подробны, поскольку включают все запросы к базе данных.

Вам не нужно входить в консоль. Вот конфигурация, которая записывает все журналы из django с именем logger в локальный файл:

LOGGING={'version':1,'disable_existing_loggers':False,'handlers':{'file':{'level':'DEBUG','class':'logging.FileHandler','filename':'/path/to/django/debug.log',},},'loggers':{'django':{'handlers':['file'],'level':'DEBUG','propagate':True,},},}

Если вы используете этот пример, обязательно измените 'filename'путь на место, доступное для записи пользователю, запускающему приложение Django.

Наконец, вот пример довольно сложной настройки ведения журнала:

LOGGING={'version':1,'disable_existing_loggers':False,'formatters':{'verbose':{'format':'{levelname}{asctime}{module}{process:d}{thread:d}{message}','style':'{',},'simple':{'format':'{levelname}{message}','style':'{',},},'filters':{'special':{'()':'project.logging.SpecialFilter','foo':'bar',},'require_debug_true':{'()':'django.utils.log.RequireDebugTrue',},},'handlers':{'console':{'level':'INFO','filters':['require_debug_true'],'class':'logging.StreamHandler','formatter':'simple'},'mail_admins':{'level':'ERROR','class':'django.utils.log.AdminEmailHandler','filters':['special']}},'loggers':{'django':{'handlers':['console'],'propagate':True,},'django.request':{'handlers':['mail_admins'],'level':'ERROR','propagate':False,},'myproject.custom':{'handlers':['console','mail_admins'],'level':'INFO','filters':['special']}}}

Эта конфигурация ведения журнала выполняет следующие функции:

  • Определяет конфигурацию как имеющуюся в формате «dictConfig версии 1». В настоящее время это единственная версия формата dictConfig.

  • Определяет два средства форматирования:

    • simple, который выводит имя уровня журнала (например, DEBUG) и сообщение журнала.

      formatСтрока является нормальным Python форматирования строки описания деталей , которые будут выводиться на каждой лесозаготовительной линии. Полный список деталей, которые можно вывести, можно найти в Объектах средства форматирования .

    • verbose, который выводит имя уровня журнала, сообщение журнала, а также время, процесс, поток и модуль, которые генерируют сообщение журнала.

  • Определяет два фильтра:

  • Определяет два обработчика:

  • Настраивает три регистратора:

    • django, который передает все сообщения consoleобработчику.
    • django.request, который передает все ERRORсообщения mail_adminsобработчику. Кроме того, этот регистратор помечен, чтобы не распространять сообщения. Это означает, что сообщения журнала, записанные в django.request, не будут обрабатываться djangoрегистратором.
    • myproject.custom, который передает все сообщения на уровне INFO
      или выше, которые также передают specialфильтр двум обработчикам – consoleи mail_admins. Это означает, что INFOсообщения всех уровней (или более высоких) будут выводиться на консоль; ERRORи CRITICAL
      сообщения также будут выводиться через электронную почту.

Отключение конфигурации ведения журнала

Если вы вообще не хотите настраивать ведение журнала (или хотите настроить ведение журнала вручную, используя собственный подход), вы можете установить
LOGGING_CONFIGзначение None. Это отключит процесс настройки журнала Django по умолчанию .

Установка LOGGING_CONFIGдля Noneтолько означает , что автоматический процесс конфигурации отключен, не войдя себя. Если вы отключите процесс настройки, Django по-прежнему будет выполнять вызовы журналирования, возвращаясь к любому определенному поведению журналирования по умолчанию.

Вот пример, который отключает конфигурацию ведения журнала Django, а затем вручную настраивает ведение журнала:

Обратите внимание, что процесс конфигурации по умолчанию вызывается только
LOGGING_CONFIGпосле полной загрузки параметров. Напротив, ручная настройка ведения журнала в файле настроек немедленно загрузит конфигурацию ведения журнала. Таким образом, ваша конфигурация ведения журнала должна отображаться после любых настроек, от которых она зависит.

Немного бесполезного, но красивого

На этапе установки, в первой части статьи мы прикрутили к graylog-у базу geoip. 

Теперь посмотрим, как её использовать, а также выведем карту мира, визуально демонстрирующую, откуда к нам на сайт приходят посетители.

Создаём data adapter

System  →  Lookup Tables  →  кнопка Data Adapters  →  Create data adapter:

Data adapter type  →  Geo IP – MaxMindTM Databases

Title:  GeoIP

Description:   GeoIP Lookup Table

Name:  geoip

File Path:   /etc/graylog/server/GeoLite2-City.mmdb

Database type:  City database

Остальное по умолчанию.

Для завершения нажимаем кнопку Create adapter.

Когда результаты кешируются – всё становится лучше

Создаём Caches:

System  →  Lookup Tables  →  кнопка Caches  →  кнопка Create cache

Cache Type:  Node-local, in-memory cache

Title:  GeoIP

Description:  GeoIP Cache

Name:  geoip

Остальное можно оставить по умолчанию.

Нажимаем кнопку Create Cache:

Создаём Lookup table:

System  →  Lookup Tables  →  Lookup Tables (активна по умолчанию)  →  Create Lookup Table

Title: GeoIP

Description:  GeoIP Lookup

Name: geoip

Data Adapter:  GeoIP (geoip)

Cache:  GeoIP (geoip)

Нажимаем кнопку Create Lookup Table:

Создаём Pipeline (пайплайны позволяют обрабатывать сообщения из потоков):

Сначала правило:

System  →  Pipelines  →  кнопка Manage rules  →  кнопка Create Rule

Description: Incoming connections

Rule source:

rule "GeoIP lookup: remote_addr"
when
  has_field("remote_addr")
then
  let geo = lookup("geoip", to_string($message.remote_addr));
  set_field("remote_addr_geo_location", geo["coordinates"]);
  set_field("remote_addr_geo_country", geo["country"].iso_code);
  set_field("remote_addr_geo_city", geo["city"].names.en);
end

Нажимаем кнопку Save&Close.

Теперь пайплайн:

System  →  Pipelines  →  кнопка Manage pipelines  →  кнопка Add new pipeline

Title:  Web

Description:  Incoming connections

Наблюдаем сообщение, что только что созданный пайплайн не подключен ни к одному потоку: 

Нажимаем кнопку Edit connections, подключаем:

А также в Stage 0 нажимаем Edit и добавляем к нему правило:

Идём в Streams  →  Sidecars, смотрим новые сообщения…

Проблема – ничего не происходит 🙁  Никаких гео-тегов не видно :((

Проблема возникает из-за порядка обработки правил.

Идём в System  →  Configurations

По умолчанию так:

1    AWS Instance Name Lookup

2    GeoIP Resolver

3    Pipeline Processor

4    Message Filter Chain

А нам нужно так:

1    AWS Instance Name Lookup

2    GeoIP Resolver

3    Message Filter Chain

4    Pipeline Processor

Нажимаем кнопку Update, перетаскиванием располагаем правила в правильном порядке:

Снова идём в Streams  →  Sidecars, смотрим новые сообщения и видим в них искомые геоданные:

Теперь будем смотреть красивую карту:

В Streams  →  Sidecars  добавляем Aggregation:

Нажимаем Edit:

Visualization type: World Map

Rows: remote_addr_geo_location

Сохраняем:  Save

Теперь можно визуально оценить откуда к нам на сайт приходят посетители:

Примеры ¶

Полная документация по формату dictConfig
– лучший источник информации о словарях конфигурации журналирования. Однако, чтобы дать вам представление о том, что возможно, вот несколько примеров.

Для начала вот небольшая конфигурация, которая позволит вам выводить все сообщения журнала на консоль:

Это настраивает родительский rootрегистратор для отправки сообщений с
WARNINGуровнем и выше обработчику консоли.

Установив уровень на
INFOили, DEBUGвы можете отображать больше сообщений. Это может быть полезно во время разработки.

Затем мы можем добавить более детальное ведение журнала. Вот пример того, как заставить систему журналирования печатать больше сообщений только из django с именем logger:

importosLOGGING={'version':1,'disable_existing_loggers':False,'handlers':{'console':{'class':'logging.StreamHandler',},},'root':{'handlers':['console'],'level':'WARNING',},'loggers':{'django':{'handlers':['console'],'level':os.getenv('DJANGO_LOG_LEVEL','INFO'),'propagate':False,},},}

По умолчанию эта конфигурация отправляет сообщения от djangoрегистратора уровня
INFOили выше на консоль.

Это тот же уровень, что и конфигурация ведения журнала по умолчанию в Django, за исключением того, что конфигурация по умолчанию отображает записи журнала только тогда, когда
DEBUG=True.

Django не регистрирует много INFOсообщений такого уровня. Однако с помощью этой конфигурации вы также можете установить переменную среды
DJANGO_LOG_LEVEL=DEBUGдля просмотра всех журналов отладки Django, которые очень подробны, поскольку включают все запросы к базе данных.

Вам не нужно входить в консоль. Вот конфигурация, которая записывает все журналы из django с именем logger в локальный файл:

LOGGING={'version':1,'disable_existing_loggers':False,'handlers':{'file':{'level':'DEBUG','class':'logging.FileHandler','filename':'/path/to/django/debug.log',},},'loggers':{'django':{'handlers':['file'],'level':'DEBUG','propagate':True,},},}

Если вы используете этот пример, обязательно измените ‘filename’путь на место, доступное для записи пользователю, запускающему приложение Django.

Наконец, вот пример довольно сложной настройки ведения журнала:

LOGGING={'version':1,'disable_existing_loggers':False,'formatters':{'verbose':{'format':'{levelname}{asctime}{module}{process:d}{thread:d}{message}','style':'{',},'simple':{'format':'{levelname}{message}','style':'{',},},'filters':{'special':{'()':'project.logging.SpecialFilter','foo':'bar',},'require_debug_true':{'()':'django.utils.log.RequireDebugTrue',},},'handlers':{'console':{'level':'INFO','filters':['require_debug_true'],'class':'logging.StreamHandler','formatter':'simple'},'mail_admins':{'level':'ERROR','class':'django.utils.log.AdminEmailHandler','filters':['special']}},'loggers':{'django':{'handlers':['console'],'propagate':True,},'django.request':{'handlers':['mail_admins'],'level':'ERROR','propagate':False,},'myproject.custom':{'handlers':['console','mail_admins'],'level':'INFO','filters':['special']}}}

Эта конфигурация ведения журнала выполняет следующие функции:

Расширения журналирования django ¶

Django предоставляет ряд утилит для удовлетворения уникальных требований ведения журнала в среде веб-сервера.

Логгеры

Django предоставляет несколько встроенных регистраторов.

django.db.backends

Сообщения, относящиеся к взаимодействию кода с базой данных. Например, каждый оператор SQL уровня приложения, выполняемый запросом, регистрируется на
DEBUGуровне этого регистратора.

Сообщения в этот регистратор имеют следующий дополнительный контекст:

По соображениям производительности ведение журнала SQL включается, только если для
settings.DEBUGнего установлено значение True, независимо от уровня ведения журнала или установленных обработчиков.

Эта регистрация не включает в себя инициализацию рамки уровня (например
) или запросы управления транзакциями (например ,
, и ). Включите ведение журнала запросов в своей базе данных, если вы хотите просмотреть все запросы к базе данных.SETTIMEZONEBEGINCOMMITROLLBACK

django.security.*

Регистраторы безопасности будут получать сообщения о любых
SuspiciousOperationошибках, связанных с безопасностью. Для каждого подтипа ошибки безопасности, включая все ошибки, есть подзаголовок
SuspiciousOperation. Уровень события журнала зависит от того, где обрабатывается исключение. Большинство событий регистрируется как предупреждение, тогда как любое сообщение SuspiciousOperation, достигнутое обработчиком WSGI, регистрируется как ошибка. Например, когда HTTP- Hostзаголовок включен в запрос от клиента, который не соответствует ALLOWED_HOSTS, Django вернет ответ 400, а сообщение об ошибке будет записано в
django.security.DisallowedHostрегистратор.

Эти события djangoжурнала по умолчанию будут поступать в средство ведения журнала, которое присылает сообщения об ошибках администраторам DEBUG=False. Запросы, приводящие к ответу 400 из-за a SuspiciousOperation, не будут регистрироваться в django.request
регистраторе, а только в django.securityрегистраторе.

Чтобы отключить определенный тип SuspiciousOperation, вы можете переопределить этот конкретный регистратор, следуя этому примеру:

Другие django.securityрегистраторы, не основанные на SuspiciousOperation:

Обработчики

Django предоставляет один обработчик журналов в дополнение к тем, которые предоставляются модулем ведения журналов Python.

classAdminEmailHandler ( include_html = False , email_backend = None , reporter_class = None )

Этот обработчик отправляет электронное письмо на сайт ADMINSдля каждого полученного сообщения журнала.

Если запись журнала содержит requestатрибут, полная информация о запросе будет включена в электронное письмо. Тема электронного письма будет включать фразу «внутренний IP-адрес», если в INTERNAL_IPSнастройках указан IP-адрес клиента
; в противном случае он будет включать «ВНЕШНИЙ IP-адрес».

Если запись журнала содержит информацию о трассировке стека, эта трассировка стека будет включена в электронное письмо.

include_htmlАргумент AdminEmailHandlerиспользуется для управления , включает ли отслеживающий электронной почты вложения HTML , содержащий полное содержимое веб – страницы отладки , который был бы выработан , если бы DEBUGбыли True. Чтобы установить это значение в вашей конфигурации, включите его в определение обработчика
django.utils.log.AdminEmailHandler, например:

Обратите внимание, что эта HTML-версия электронного письма содержит полную трассировку с именами и значениями локальных переменных на каждом уровне стека, а также значениями ваших настроек Django. Эта информация потенциально очень конфиденциальна, и вы можете не захотеть пересылать ее по электронной почте. Подумайте об использовании чего-то вроде Sentry, чтобы получить лучшее из обоих миров – богатую информацию о полных трассировках плюс безопасность, чтобы не отправлять информацию по электронной почте. Вы также можете явно указать определенную конфиденциальную информацию, которая будет отфильтрована из отчетов об ошибках – подробнее о
фильтрации отчетов об ошибках .

Установив email_backendаргумент AdminEmailHandler,
сервер электронной почты , который используется обработчиком, может быть переопределен, например:

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

reporter_classАргумент AdminEmailHandlerпозволяет обеспечивая django.views.debug.ExceptionReporterподкласс , чтобы настроить текст обратного прослеживания , переданный в теле сообщения электронной почты. Вы указываете путь импорта строки к классу, который хотите использовать, например:

send_mail( тема , сообщение , * аргументы , ** kwargs )

Отправляет электронные письма администраторам. Чтобы настроить это поведение, вы можете создать подкласс AdminEmailHandlerкласса и переопределить этот метод.

Фильтры

Django предоставляет некоторые фильтры журналов в дополнение к тем, которые предоставляются модулем ведения журналов Python.

классCallbackFilter ( обратный вызов )

Этот фильтр принимает функцию обратного вызова (которая должна принимать единственный аргумент – запись, которая должна быть записана в журнал) и вызывает ее для каждой записи, проходящей через фильтр. Обработка этой записи не будет продолжена, если обратный вызов вернет False.

Например, чтобы отфильтровать UnreadablePostError
(вызывается, когда пользователь отменяет загрузку) из электронных писем администратора, вы должны создать функцию фильтра:

а затем добавьте его в конфигурацию ведения журнала:

класс RequireDebugFalse

Этот фильтр будет передавать записи, только если settings.DEBUG имеет значение False.

Этот фильтр используется в LOGGING
конфигурации по умолчанию следующим образом, чтобы гарантировать, что AdminEmailHandlerсообщения об ошибках будут отправляться администраторам только DEBUGв следующих случаях False:

класс RequireDebugTrue

Этот фильтр похож на RequireDebugFalse, за исключением того, что записи передаются только тогда, когда DEBUGесть True.

Состав игроков ¶

Конфигурация ведения журнала Python состоит из четырех частей:

Логгеры

Регистратор – это точка входа в систему регистрации. Каждый регистратор представляет собой именованную корзину, в которую можно записывать сообщения для обработки.

Регистратор настроен на уровень ведения журнала . Этот уровень журнала описывает серьезность сообщений, обрабатываемых регистратором. Python определяет следующие уровни журнала:

Каждое сообщение, записываемое в регистратор, является записью журнала . Каждая запись журнала также имеет уровень журнала, указывающий серьезность этого конкретного сообщения. Запись журнала также может содержать полезные метаданные, описывающие регистрируемое событие. Это может включать такие детали, как трассировка стека или код ошибки.

Когда сообщение отправляется регистратору, уровень журнала сообщения сравнивается с уровнем журнала регистратора. Если уровень журнала сообщения соответствует уровню журнала самого регистратора или превышает его, сообщение будет подвергнуто дальнейшей обработке. В противном случае сообщение будет проигнорировано.

Как только регистратор определил, что сообщение необходимо обработать, оно передается обработчику .

Обработчики

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

Как и регистраторы, обработчики также имеют уровень ведения журнала. Если уровень журнала записи журнала не соответствует или превышает уровень обработчика, обработчик проигнорирует сообщение.

У регистратора может быть несколько обработчиков, и каждый обработчик может иметь свой уровень журнала. Таким образом, можно предоставить различные формы уведомления в зависимости от важности сообщения. Например, вы можете установить один обработчик, который пересылает ERRORи
CRITICALсообщения службе подкачки, в то время как второй обработчик регистрирует все сообщения (включая ERRORи CRITICALсообщения) в файле для последующего анализа.

Фильтры

Фильтр используется для обеспечения дополнительного контроля над тем, какие записи журнала передаются от регистратора к обработчику.

По умолчанию обрабатывается любое сообщение журнала, соответствующее требованиям уровня журнала. Однако, установив фильтр, вы можете добавить дополнительные критерии к процессу ведения журнала. Например, вы можете установить фильтр, который разрешает ERRORотправку сообщений только из определенного источника.

Фильтры также можно использовать для изменения записи журнала перед отправкой. Например, вы можете написать фильтр, который понижает рейтинг
ERRORзаписей журнала до WARNINGзаписей, если выполняется определенный набор критериев.

Фильтры могут быть установлены на регистраторах или на обработчиках; несколько фильтров можно использовать в цепочке для выполнения нескольких действий фильтрации.

Формат сообщений и legacy

TLDR: всё плохо

Syslog появился в 80-х, и быстро стал стандартом логирования для Unix-like систем и сетевого оборудования. Стандарта не было, все писали по принципу совместимости с существующим софтом. В 2001 IETF описал текущее положение вещей в RFC 3164 (статус “informational”). Т. к. реализации очень отличаются, то в частности в этом документе сказано “содержание любого IP пакета отправленного на UDP порт 514 должно рассматриваться как сообщение syslog”. Потом попробовали стандартизировать формат в RFC 3195, но документ получился неудачным, для него в данный момент нету ни одной живой реализации. В 2009 приняли RFC 5424, определяющий структурированные сообщения, но этим редко кто пользуется.

Вот тут можно прочитать, что обо всём этом думает автор rsyslog Рейнер Герхард (Rainer Gerhards). Фактически, по-прежнему все реализуют syslog как попало, и задача интерпретировать всё это разнообразие ложиться на syslog-сервер. Например, в rsyslog включен специальный модуль для разбора формата, используемого CISCO IOS, ну и для самых плохих случаев начиная с пятой версии можно определять собственные парсеры.

Сообщения syslog при передаче по сети выглядят примерно так:

  • PRI — Priority. Вычисляется как facility * 8 severity.
  • TIMESTAMP — время, обычно в формате “Feb 6 18:45:01”. Согласно RFC 3164, может записываться в формате времени ISO 8601: “2021-02-06T18:45:01.519832 03:00” с большей точностью и с учётом используемой временной зоны.
  • HOST — имя хоста, сгенерировавшего сообщение
  • TAG — содержит имя программы, сгенерировавшей сообщение. Не более 32 алфавитно-цифровых символов, хотя по факту многие реализации позволяют больше. Любой не-алфавитноцифровой символ заканчивает TAG и начинает MSG, обычно используется двоеточие. Иногда в квадратных скобках содержит номер сгенерировавшего сообщение процесса. Т. к. [ ] — не алфавитно-цифровые символы, то номер процесса вместе с ними должен считаться частью сообщения. Но обычно все реализации считают это частью тега, считая сообщением всё после символов “: “
  • MSG — сообщение. Из-за неопределённости с тем, где же кончается тег и начинается сообщение, в начало может добавляться пробел. Не может содержать символов перевода строки: они являются разделителями фреймов, и начнут новое сообщение. Способы всё же переслать мгногострочное сообщение:
Оцените статью
Huawei Devices
Добавить комментарий