Cбор логов с rsyslog, именами файлов в тегах, многострочными сообщениями и отказоустойчивостью

Управление логгированием

$ sudo journalctl --vacuum-size=1G
$ sudo journalctl --vacuum-time=1years

Настройка ротации логов в конфигурационном файле

  • SystemMaxUse= максимальный объём, который логи могут занимать на диске;
  • SystemKeepFree= объём свободного места, которое должно оставаться на диске после сохранения логов;
  • SystemMaxFileSize= объём файла лога, по достижении которого он должен быть удален с диска;
  • RuntimeMaxUse= максимальный объём, который логи могут занимать в файловой системе /run;
  • RuntimeKeepFree= объём свободного места, которое должно оставаться в файловой системе /run после сохранения логов;
  • RuntimeMaxFileSize= объём файла лога, по достижении которого он должен быть удален из файловой системы /run.

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

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

— Priority. Вычисляется как .Facility (категория) принимает значения от 0 до 23, им соответствуют различные категории системных служб: 0 — kernel, 2 — mail, 7 — news. Последние 8 — от local0 до local7 — определены для служб, не попадающих в предопределённые категории

.
Severity (важность) принимает значения от 0 (emergency, самая высокая) до 7 (debug, самая низкая). .

— время, обычно в формате «Feb 6 18:45:01»

Согласно RFC 3164, может записываться в формате времени ISO 8601: «2017-02-06T18:45:01.519832+03:00» с большей точностью и с учётом используемой временной зоны.

— имя хоста, сгенерировавшего сообщение

— содержит имя программы, сгенерировавшей сообщение. Не более 32 алфавитно-цифровых символов, хотя по факту многие реализации позволяют больше. Любой не-алфавитноцифровой символ заканчивает TAG и начинает MSG, обычно используется двоеточие. Иногда в квадратных скобках содержит номер сгенерировавшего сообщение процесса. Т. к. — не алфавитно-цифровые символы, то номер процесса вместе с ними должен считаться частью сообщения. Но обычно все реализации считают это частью тега, считая сообщением всё после символов «: «

— сообщение. Из-за неопределённости с тем, где же кончается тег и начинается сообщение, в начало может добавляться пробел. Не может содержать символов перевода строки: они являются разделителями фреймов, и начнут новое сообщение. Способы всё же переслать мгногострочное сообщение:экранирование. Получим на стороне приёмника текст с вместо переводов строки
использование octet-counted TCP Framing, как определено в RFC 5425 для TLS-enabled syslog. Нестандарт, только некоторые реализации.

Альтернатива протоколу syslog: RELP

Если сообщения пересылаются между хостами, использующими rsyslog, можно вместо plain TCP sysog использовать RELP — Reliable Event Logging Protocol. Был создан для rsyslog, сейчас поддерживается и некоторыми другими системами. В частности, его понимают Logstash и Graylog. Для транспорта использует TCP. Может опционально шифровать сообщения с помощью TLS. Надёжнее plain TCP syslog, не теряет сообщения при разрыве соединения. Решает проблему с многострочными сообщениями.

Выбор софта

Зачем вообще нужен syslog-сервер, когда есть elastic beats, logstash, systemd-journal-remote и ещё много новых блестящих технологий?

  • Это стандарт для ведения логов в POSIX-совместимых системах.
    Некоторый софт, например haproxy, использует только его. То есть совсем избавится от syslog вам всё равно не удастся
  • Его использует сетевое железо
  • Сложнее в настройке, но богаче по возможностям, чем альтернативные решения.
    Например, Elastic Filebeat до сих пор не умеет inotify.
  • Нетребователен к памяти. Возможно использование на embedded системах после небольшого тюнинга.
  • Позволяет изменять сообщение перед сохранением/пересылкой.
    Странная задача, но иногда требуется. Например, PCI DSS в разделе 3.4 требует маскировать или шифровать номера карт, если они сохраняются на диск. Тонкость в том, что если кто-то ввёл номер карты в строку поиска или в форму обратной связи, то как только вы сохранили запрос в лог, вы нарушаете стандарт.

Наблюдение: пользователи пытаются ввести номер карты в любое поле ввода на странице, и норовят сообщить его саппорту вместе с CVV.

Tail-log backups

The second implication of needing to copy the pages into the log backup file has to do with database recovery in the case of a disaster.

If I have a database in full recovery model, I’m taking regular log backups, and some disaster damages the data file severely enough that the database becomes unavailable (say a complete drive failure), I can take what’s called a tail-log backup (BACKUP LOG … WITH NO_TRUNCATE) if the log file is still available even though the data file is completely unavailable. With the existing backups (full, perhaps differentil and then log) and the tail log backup, I can restore the database up to the exact point that it failed. I can do that because the log contains sufficient information to completely recreate all of the committed transactions.

However, in bulk-logged recovery that may not be true. If there are any minimally logged operations, then the log does not contain sufficient information to completely recreate all of the committed transactions and the actual data pages are required to be available when the log backup is taken. If the data file is not available, then that log backup cannot copy the data pages it needs to restore a consistent database.

Let’s see how this works:

CREATE DATABASE BulkLoggedRecovery
GO
ALTER DATABASE BulkLoggedRecovery SET RECOVERY BULK_LOGGED
GO
BACKUP DATABASE BulkLoggedRecovery TO DISK = 'D:\Develop\Databases\Backups\BulkLoggedRecovery.bak'
GO
USE BulkLoggedRecovery
go
SELECT TOP (200) REPLICATE('a',2000) AS SomeCol
 INTO SomeTable
 FROM sys.columns AS c;
GO
SHUTDOWN WITH NOWAIT
GO

The shutdown is so that I can go and do nasty things to the data file for that DB without having to resort to things like pulling USB drives out.

With the SQL Server service shut down, I’m going to go to the data folder and delete the mdf file for the BulkLogged database, then restart SQL. It’s not a complete simulation of a drive failure, but its close enough for the purposes of this demo.

When SQL restarts, the database is not available. No surprise, its primary data file is missing. The state is Recovery_Pending, meaning that SQL couldn’t open the database to run crash recovery on it.

Let’s see what happens if I try to take a tail-log backup (note that no_truncate implies copy_only and continue_after_error):

BACKUP LOG BulkLoggedRecovery TO DISK = D:\Develop\Databases\Backups\BulkLoggedRecovery_tail.trn'
WITH NO_TRUNCATE

Well, it said that it succeeded (and despite the warning, there were no errors in the error log). Now, let’s try and restore this DB:

RESTORE DATABASE BulkLoggedRecovery FROM DISK = 'D:\Develop\Databases\Backups\BulkLoggedRecovery.bak' WITH NORECOVERY
RESTORE LOG BulkLoggedRecovery FROM DISK = 'D:\Develop\Databases\Backups\BulkLoggedRecovery_tail.trn' WITH RECOVERY

Well that didn’t work. Let’s see about restoring the log with continue_after_error:

RESTORE DATABASE BulkLoggedRecovery FROM DISK = 'D:\Develop\Databases\Backups\BulkLoggedRecovery.bak' WITH NORECOVERY
RESTORE LOG BulkLoggedRecovery FROM DISK = 'D:\Develop\Databases\Backups\BulkLoggedRecovery_tail.trn' WITH RECOVERY, CONTINUE_AFTER_ERROR

That worked, so let’s see about the state of the database. The table that was the target of select…into exists, so let’s see if any of the data made it into the table (remember, the page allocations were logged, the contents of the pages were not.

Not good. Let’s see what CheckDB says about the state of the DB

Not good at all. About the only sensible option here is to restore again and leave off the tail-log backup. It means that any transactions that committed between the last normal log backup and the point of failure are lost.

This is a second thing to consider carefully before choosing to run a database in bulk-logged recovery model for long or short periods.

Модуль Logging

Модуль logging в Python — это готовый к использованию, мощный модуль, предназначенный для удовлетворения потребностей как начинающих, так и корпоративных команд. Он используется большинством сторонних библиотек Python, поэтому вы можете интегрировать ваши логи с сообщениями из этих библиотек для создания единого журнала логов в вашего приложении.

Добавить logging в вашу программу на Python так же просто, как написать эту строчку:

import logging

С импортированным модулем logging вы можете использовать то, что называется «logger», для логирования сообщений, которые вы хотите видеть

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

Список уровней в порядке увеличения важности:

  • DEBUG
  • INFO
  • WARNING
  • ERROR
  • CRITICAL

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

import logging

logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')

Вывод вышеупомянутой программы будет выглядеть так:

WARNING:root:This is a warning message
ERROR:root:This is an error message
CRITICAL:root:This is a critical message

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

Обратите внимание, что сообщения debug() и info() не были отображены. Это связано с тем, что по умолчанию модуль ведения журнала регистрирует сообщения только с уровнем WARNING или выше

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

Bulk Data

bulk data

  • Все пули в игре.
  • Все деревья в игре.
  • Все монеты в игре.
  • Все сущности в игре.
  • Все меши в игре.
  • Все звуки в игре.
  • Все звуковые ресурсы, которые могут воспроизводиться.
  • Все звуки, воспроизводимые в данный момент.
  • Все эффекты (затухания, изменения тона и т.п.), применяемые к звукам.
  • Порядок хранения объектов не важен. Т.е. мы воспринимаем массив как множество объектов.
  • Каждый объект представлен как простая структура данных (POD-struct) фиксированного размера, которую можно перемещать или дублировать при помощи .

важенперерисовкикак они используютсякрасно-чёрные деревьяB-деревьяO(n)поразрядной сортировки (radix sort)

  • Добавление и удаление объектов должны быть быстрыми.
  • Данные должны быть расположены в удобном для кэширования виде, чтобы можно было быстро выполнять итерации по ним для обновления системы.
  • Она должна поддерживать механизм ссылок — необходимо наличие способа передачи информации о конкретных объектах в bulk data. В показанном выше примере fade должен иметь возможность указать, какой именно звук подвергается затуханию. В примере я записал ссылки как указатели, но их реализация зависит от того, как устроены bulk data.
  • Данные должны быть дружественными к аллокаторам — они должны использовать несколько крупных распределений памяти, а не распределять отдельные объекты в куче.

не знаете

  • Стандартная реализация из Visual Studio медленно работает в режиме Debug из-за итераторов отладки. Им стоит присвоить значение _ITERATOR_DEBUG_LEVEL=0.
  • Для создания и уничтожения объектов использует конструкторы и деструкторы, а они в некоторых случаях могут быть значительно медленнее, чем .
  • намного сложнее анализировать, чем реализация простого «растягивающегося буфера» (stretchy buffer).

Почему важно журналирование

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

Делаем ошибки в точках интеграции видимыми

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

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

Диагностика функциональных ошибок в производственой системе

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

Анализ истории событий

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

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

Инструменты мониторинга отслеживают журналы в режиме реального времени для сбора важных показателей, полезных как для бизнеса, так и для операций, а также могут быть настроены для подачи сигналов тревоги, когда эти показатели превышают определенные пороговые значения. Разработчики используют журналы для отладки и трассировки и даже для записи важных событий для сборки и тестирования в конвейерах CI / CD.

2.4. Настройка IP-адреса коммутатора

Хотя коммутаторы серий SNR-S2965 и SNR-S2985G являются оборудованием уровня 2, на них есть возможность создать VLAN интерфейс (SVI) третьего уровня (L3) с IP-адресом, который также является IP-адресом для управления коммутатором.Поддерживается три варианта назначения IP-адреса коммутатору:

  • Статический

  • BOOTP

  • DHCP

В режиме BOOTP/DHCP, коммутатор работает в роли BOOTP или DHCP клиента и получает IP-адрес динамически от BOOTP/DHCP сервера. Также коммутатор может сам выступать в роли DHCP сервера, динамически раздавая адреса подключенному оборудованию.

2.4.1. Настройка IP-адреса на коммутаторе

1. Создание VLAN интерфейса на коммутаторе2. Статическая настройка IP-адреса3. Динамическое получение IP-адреса по протоколу BOOTP4. Динамическое получение IP-адреса по протоколу DHCP

          1. Создание VLAN интерфейса на коммутаторе

Команда

Описание

interface vlan <vlan-id>no interface vlan <vlan-id>! В режиме глобальной конфигурации

Создание L3 интерфейса в Vlan <vlan-id>Удаление L3 интерфейса в Vlan <vlan-id>

            2. Статическая настройка IP-адреса

Команда

Описание

ip address <ip_address> <mask>

no ip address <ip_address> <mask>

! В режиме конфигурации Interface VLAN

Настройка статического IPv4-адреса <ip_address> с маской <mask> на Vlan интерфейсе.

secondary — ip-адрес будет добавлен на интерфейс как дополнительный (secondary)

Удаление статического ip-адреса с интерфейса

ipv6 address <ipv6-address/prefix-length>

no ipv6 address <ipv6-address/prefix-length>

! В режиме конфигурации Interface VLAN

Настройка статического IPv6-адреса <ipv6_address/prefix>

eui-64 — использовать EUI-64 для формирования IPv6 адреса

Удаление статического IPv6-адреса с интерфейса

              3. Динамическое получение IP-адреса по протоколу BOOTP

Команда

Описание

ip bootp-client enableno ip bootp-client enable! В режиме конфигурации Interface VLAN

Включить BOOTP клиент на Interface Vlan

Отключить BOOTP клиент на Interface Vlan

             4. Динамическое получение IP-адреса по протоколу DHCP

Команда

Описание

ip dhcp-client enableno ip dhcp-client enable! В режиме конфигурации Interface VLAN

Включить DHCP клиент на Interface Vlan

Отключить DHCP клиент на Interface Vlan

Копаем recovery

Проверяю разницу между boot и recovery разделами. Все идентично кроме initramfs. В initramfs раздела recovery изучаю init.rc, в котором описан лишь один сервис, который запускает . Изучаю , затем исходники оригинального recovery. Как видно, по умолчанию recovery просто отображает логотип Android. А если необходимо что-то сделать, то в штатном режиме в раздел записывается файл , который может содержать параметры запуска recovery. Если в этот файл записать то мы должны увидеть меню.

Запускаю dirtycow exploit, выставляю UID/GID, записываю файл и запускаю . Телефон перезагружается и я попадаю в меню стандартного recovery. Уже что-то. Пробую прошить ZIP файл с supersu через . Операция прерывается с ошибкой. Толком не смотрю на ошибку, а лезу в код recovery и ищу место, отвечающее за проверку цифровой подписи ZIP файла.

Выясняю, что initramfs содержит публичный ключ в формате minicrypt, которым проверяется цифровая подпись ZIP файла. Оказалось это стандартный тестовый ключ Android, и что я могу подписать этим ключём любой архив. Проверить это можно следующим образом:

Попробовал установить ZIP напрямую с sdcard, но в recovery при монтировании sdcard возникала ошибка. Изучил , оказалось что в режиме recovery sdcard монтируется как vfat:

Моя 64Gb флэшка была отформатирована в exfat. Нашел старую sdcard на 2Gb, отформатировал её как vfat, записал ZIP, вставил её в телефон. Recovery в этот раз смог примонтировать карточку и я мог просматривать её содержимое на телефоне. Однако при установке ZIP опять возникла ошибка: E:failed to set up expected mounts for install; aborting.

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

Т.е. перед тем как применить ZIP, recovery отмонтирует все разделы, но в моём случае что-то идёт не так.

Стратегия распределения

геометрическиO(n)nnO(n)O(n) / O(n) = O(1)амортизированной постояннойO(n)O(n)Затраты на запись в .O(n)O(n) / O(128) = O(n)

«Амортизированная постоянная» не очень хорошо подходит для ПО реального времени. Если у вас есть очень большой массив, допустим, в сотни миллионов элементов, то увеличение этого массива и перемещение всех элементов может вызвать заметное торможение частоты кадров. Это проблематично по той же причине, по которой проблематична в играх сборка мусора

Не важно, насколько низки средние затраты, если в некоторых кадрах затраты могут скачкообразно увеличиваться, вызывая глитчи игры.
Аналогично, эта стратегия распределения в случае больших массивов может впустую тратить много памяти. Допустим, у нас есть массив из 16 миллионов элементов и нужно записать в него ещё один

Это заставит массив увеличиться до 32 миллионов. Теперь у нас есть в массиве 16 миллионов элементов, которые мы не используем. Для платформы с низким объёмом памяти это очень много.
Наконец, перераспределение перемещает объекты в памяти, делая недействительными все указатели на объекты. Это может стать источником багов, которые сложно отследить.

  • Мы можем распределить последовательность геометрически растущих буферов: 16, 32, 64, …, но сохранять старые буферы при распределении новых. Например, первые 16 элементов хранятся в одном буфере, следующие 32 элемента в следующем, и т.д… Чтобы отслеживать все эти буферы, мы можем хранить указатели на них в отдельном .
  • Мы можем распределить последовательность буферов фиксированного размера и хранить столько элементов, сколько поместится в каждом буфере. Так как мы можем подбирать размер буферов, то можно сделать их кратными размеру страницы. Благодаря этому они могут распределять память непосредственно из виртуальной памяти и не проходить через кучу. Учтите, что использование фиксированного размера не обеспечивает в этом случае производительности O(n) для , потому что мы никогда не перемещаем старые элементы.
  • Можно использовать систему виртуальной памяти для резервирования огромного массива, достаточного для хранения максимального количества объектов, которое нам понадобится, и пользоваться только той памятью, которую мы используем.

наихудший случайnСтратегии распределения

Клиент: пересылка логов с сохранением имени файла

Сохранять имена файлов мы будем в поле . В имена хочется включить каталоги, чтобы не наблюдать одноуровневую россыпь файлов: . Если лог читается не из файла, а из переданных в syslog сообщений от программы, то она может не согласиться записывать в TAG символ , потому что это не соответствует стандарту. Поэтому мы закодируем их двойными подчеркиваниями, а на лог-сервере распарсим обратно.

Создадим шаблон для передачи логов по сети. Мы хотим передавать сообщения с тегами длиннее 32 символов (у нас длинные названия логов), и передавать более точную, чем стандартную, метку времени с указанием временной зоны. Кроме того, к названию лог-файла будет добавлена локальная переменная , позже станет понятно, зачем. Локальные переменные в RainerScript начинаются с точки. Если переменная не определена, она раскроется в пустую строку.

Теперь создадим RuleSet, который будут использоваться для передачи логов по сети. Его можно будет присоединять к Input, читающим файлы, или вызывать как функцию. Да, rsyslog позволяет вызвать один RuleSet из другого. Для использования RELP надо сначала загрузить соответствующий модуль.

Теперь создадим Input, читающий лог-файл, и присоединим к нему этот RuleSet.

Стоит обратить внимание, что для каждого считываемого файла rsyslog создаёт state-файлы в своём рабочем каталоге (задаётся директивой ). Если rsyslog не может создавать там файлы, то весь лог-файл будет заново передаваться после перезапуска rsyslog

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

Последний нужен, чтобы прекратить обрабатывать эти сообщения, иначе они попадут в общий syslog. Кстати, если приложение умеет выбирать другой unix socket для syslog, кроме стандартного (nginx и haproxy так умеют), то можно с помощью модуля imuxsock сделать для этого сокета отдельный Input и прицепить к нему нужный RuleSet, не разбирая логи из общего потока по тегам.

Чтение лог-файлов, заданных через wildcard

Интерлюдия

Программист: Не могу найти на лог-сервере логи somevendor.log за начало прошлого месяца, посмотри плиз.
Девопс: Эээ… а мы разве пишем такие логи? Предупреждать же надо. Ну в любом случае всё старше недели логротейт потёр, если мы его не сохраняли — значит уже нету.
Программист: бурно возмущается

Если приложение пишет много разных логов, и иногда появляются новые, то обновлять конфиги каждый раз неудобно. Хочется это автоматизировать. Модуль imfile умеет считывать файлы, заданные вайлдкардом, и сохранять в мета-данных сообщения путь к файлу. Правда, путь сохраняется полный, а нам нужен только последний компонент, который оттуда придётся добыть. Кстати, тут нам и пригодится переменная

Вайлдкарды поддерживаются только в режиме работы imfile (это режим по-умолчанию). Начиная с верcии 8.25.0, вайлдкарды поддерживаются как в имени файла, так и пути: /var/log/.log.

Многострочные сообщения

Для работы с лог-файлами, содержащими многострочные сообщения, модуль imfile предлагает три варианта:

  • — сообщения разделены пустой строкой
  • — новые сообщения начинаются с начала строки, продолжение сообщения идёт с отступом. Часто так выглядят стектрейсы
  • — определять начало нового сообщения по regexp (POSIX Extended)

Первые два варианта имеют проблемы в режиме работы , и при необходимости третий легко их заменяет с соответствующим regexp. Считывание многострочных логов имеет одну тонкость. Обычно признак нового сообщения находится в его начале, и мы не можем быть уверены, что программа закончила писать прошлое сообщение, пока не началось следующее. Из-за этого последнее сообщение может никогда не передаваться. Чтобы этого избежать, мы задаём , по истечении которого сообщение считается законченным и будет передано.

1.3.5. Изменение свойств базы данных

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

Для изменения свойства используется оператор SET. Команда будет выглядеть следующим образом:

ALTER DATABASE Имя_базы
SET имя_свойства

После ALTER DATABASE указывается имя базы данных, свойства которой нужно изменить, а после оператора SET нужно указать имя свойства.

Давайте посмотрим имена свойств которые нужно подставить вместо параметра имя_свойства:

  • SINGLE_USER – перевести базу данных в однопользовательский режим. Только один пользователь сможет работать с базой;
  • RESTRICTED_USER – к базе данных разрешено подключаться только пользователям, которые принадлежат роли db_owner, dbcreator или sysadmin;
  • MULTI_USER – нормальный многопользовательский режим, при котором действуют все права (используется по умолчанию);
  • OFFLINE – отключить базу данных, подключения будут невозможны. Команды должна выполняться, когда к базе данных нет активных подключений. Вы при этом должны быть подключены к базе данных master.
  • ONLINE – вернуть базу данных в активное состояние;
  • READ_ONLY — перевести базу данных в режим только для чтения, изменение данных будет невозможно;
  • READ_WRITE — вернуть базе данных полный доступ на запись и чтение;
  • CURSOR_CLOSE_ON_COMMIT ON – по завершении транзакции (принятии или откате) все открытые курсоры будут закрываться. Если ON заменить на OFF, то при нормальном завершении транзакции (принятии изменений) курсоры остаются открытыми. При откате все курсоры кроме INSENSITIVE и STATIC закрываются;
  • RECOVERY FULL – использовать полную модель восстановления;
  • BULK_LOGGED — установить модель восстановления BULK_LOGGED;
  • SIMPLE – установить простую модель восстановления.

Это основные параметры, которые можно изменить. Более подробно о моделях восстановления можно узнать из файла Doc/BackupRestore.pdf на компакт диске.

Теперь давайте посмотрим на примеры использования этих свойств:

Следующий пример разрешает доступ только одному пользователю:

ALTER DATABASE TestDatabase
SET SINGLE_USER 

Доступ только только пользователям ролей db_owner, dbcreator или sysadmin:

ALTER DATABASE TestDatabase
SET RESTRICTED_USER

Возвращаем нормальный многопользовательский режим:

ALTER DATABASE TestDatabase
SET MULTI_USER

Вывести базу данных в off-line, т.е. доступ будет запрещен всем пользователям:

ALTER DATABASE TestDatabase
SET OFFLINE

Возобновить доступ к базе данных:

ALTER DATABASE TestDatabase
SET ONLINE

Перевести базу данных в режим только для чтения, любые изменения будут отклонены:

ALTER DATABASE TestDatabase
SET READ_ONLY 

Вернуть базе данных полный доступ на запись и чтение:

ALTER DATABASE TestDatabase
SET READ_WRITE 

По завершении транзакции (принятии или откате) все открытые курсоры будут закрываться:

ALTER DATABASE TestDatabase
SET CURSOR_CLOSE_ON_COMMIT ON

Установить полную модель восстановления:

ALTER DATABASE TestDatabase
SET RECOVERY FULL

Установить модель восстановления BULK_LOGGED:

ALTER DATABASE TestDatabase
SET BULK_LOGGED 

Установить простую модель восстановления:

ALTER DATABASE TestDatabase
SET SIMPLE

И последнее, что нам предстоит узнать – это возможность изменения раскладки (кодировки) по умолчанию для базы данных. Для этого выполняется команда:

ALTER DATABASE Имя_базы
COLLATE имя_кодировки

Формат вывода

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

import logging

logging.basicConfig(format='%(process)d-%(levelname)s-%(message)s')
logging.warning('This is a Warning')
18472-WARNING-This is a Warning

format может принимать строку с атрибутами LogRecord в любом порядке. Весь список доступных атрибутов можно найти .

Вот еще один пример, где вы можете добавить информацию о дате и времени:

import logging

logging.basicConfig(format='%(asctime)s - %(message)s', level=logging.INFO)
logging.info('Admin logged in')
2018-07-11 20:12:06,288 - Admin logged in

%(asctime)s добавляет время создания LogRecord. Формат можно изменить с помощью атрибута datefmt, который использует тот же язык форматирования, что и функции форматирования в модуле datetime, например time.strftime():

import logging

logging.basicConfig(format='%(asctime)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S')
logging.warning('Admin logged out')
12-Jul-18 20:53:19 - Admin logged out

Вы можете найти больше информации о формате datetime руководстве.

Логирование переменных

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

import logging

name = 'John'

logging.error('%s raised an error', name)
ERROR:root:John raised an error

Аргументы, передаваемые методу, будут включены в сообщение в качестве переменных.

Хотя вы можете использовать любой стиль форматирования, f-строки, представленные в Python 3.6, являются лучшим способом форматирования строк, поскольку они могут помочь сделать форматирование коротким и легким для чтения:

import logging

name = 'John'

logging.error(f'{name} raised an error')
ERROR:root:John raised an error

Вывод стека

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

import logging

a = 5
b = 0

try:
  c = a / b
except Exception as e:
  logging.error("Exception occurred", exc_info=True)
ERROR:root:Exception occurred
Traceback (most recent call last):
  File "exceptions.py", line 6, in <module>
    c = a / b
ZeroDivisionError: division by zero

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

ERROR:root:Exception occurred

Совет: если вы логируете в обработчике исключений (try..except…), используйте метод logging.exception(), который регистрирует сообщение с уровнем ERROR и добавляет в сообщение информацию об исключении. Проще говоря, вызов logging.exception() похож на вызов logging.error (exc_info = True). Но поскольку этот метод всегда выводит информацию об исключении, его следует вызывать только в обработчике исключений. Взгляните на этот пример:

import logging

a = 5
b = 0
try:
  c = a / b
except Exception as e:
  logging.exception("Exception occurred")
ERROR:root:Exception occurred
Traceback (most recent call last):
  File "exceptions.py", line 6, in <module>
    c = a / b
ZeroDivisionError: division by zero

Использование logging.exception() покажет лог на уровне ERROR. Если вы не хотите этого, вы можете вызвать любой из других методов ведения журнала от debug() до critical() и передать параметр exc_info как True.

Заключение

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

Бесконтрольно подтягиваемые транзитивные зависимости библиотек, которые вы используете в своем проекте, рано или поздно принесут какие-то свои библиотеки логирования, отчего могут открыться врата прямиком в ад.

Поэтому следите за , смотрите что вы используете и не разводите -зоопарк.

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

При этом, если вы разрабатываете библиотеку, то:

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

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

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

Думайте о том что вы пишите в лог!

Заключение

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

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

Оригинальная статья:   Logging in Python

Spread the love

2
Поделились

Рейтинг
( Пока оценок нет )
Editor
Editor/ автор статьи

Давно интересуюсь темой. Мне нравится писать о том, в чём разбираюсь.

Понравилась статья? Поделиться с друзьями:
Работатека
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: