Skip to main content

Polog - ультимативный логгер для баз данных.

Project description

Polog - ультимативный логгер для баз данных

Используйте преимущества базы данных для логгирования в ваших проектах! Легко ищите нужные вам логи, составляйте статистику и управляйте ими при помощи SQL.

Данный пакет максимально упростит миграцию ваших логов в базу данных. Вот список некоторых преимуществ логгера Polog:

  • Автоматическое логгирование. Просто повесьте декоратор на вашу функцию или класс, и каждый их вызов будет автоматически логгироваться в базу данных (или не каждый - это легко настроить)!
  • Высокая производительность. Непосредственно сама запись в базу делается из отдельных потоков и не блокирует основной поток исполнения вашей программы.
  • Поддержка асинхронных функций. Декораторы для автоматического логгирования работают как на синхронных, так и на асинхронных функциях.
  • Самый простой синтаксис. Сделать логгирование еще проще уже вряд ли возможно. Вы можете залоггировать целый класс всего одной строчкой кода. Имена функций короткие, насколько это позволяет здравый смысл.
  • Удобное профилирование. В базу автоматически записывается время работы ваших функций. Так вы можете накопить статистику производительности вашего кода и легко ее анализировать.

Быстрый старт

Просто импортируйте декоратор @flog() и примените его к вашей функции. Никаких настроек, ничего лишнего - все уже работает.

from polog.flog import flog


@flog()
def sum(a, b):
  return a + b

print(sum(2, 2))

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

from polog.flog import flog


@flog()
def division(a, b):
  return a / b

print(division(2, 0))

Произошла ошибка, мы делим число на 0. Что на этот раз записано в базу? Очевидно, что результат работы функции в базу записан не будет, т.к. она не успела ничего вернуть. Зато там появилась подробная информация об ошибке: название вызванного исключения, текст его сообщения, и главное, трейсбек. Кроме того, появится отметка о неуспешности выполненной операции - ко всем автоматическим логам такие метки проставляются автоматически, чтобы вы могли легко выбирать из базы данных только успешные или только неуспешные операции и как-то анализировать результат.

from polog.flog import flog


@flog()
def division(a, b):
  return a / b

@flog()
def operation(a, b):
  return division(a, b)

print(operation(2, 0))

Чего примечательного в этом примере кода? В данном случае ошибка происходит в функции division(), а затем, поднимаясь по стеку вызовов, она проходит через функцию operation(). Однако логгер записал в базу данных сообщение об ошибке только один раз! Встретив исключение в первый раз, он пишет его в базу и подменяет другим, специальным, которое игнорирует в дальнейшем. В результате ваша база данных не засоряется бесконечным дублированием информации об ошибках.

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

from polog.clog import clog


@clog()
class OneOperation(object):
  def division(self, a, b):
    return a / b

  def operation(self, a, b):
    return self.division(a, b)

print(OneOperation().operation(2, 0))

Что, если мы хотим залоггировать целый класс? Обязательно ли проходиться по всем его методам и на каждый вешать декоратор @flog()? Нет! Для классов существует декоратор @clog(). Что он делает? Он за вас проходится по методам класса и вешает на каждый из них декоратор @flog(). Если вы не хотите логгировать ВСЕ методы класса, передайте в @clog() имена методов, которые вам нужно залоггировать, например: @clog('division').

from polog.log import log


log(message="All right!")
log(message="It's bad.", success=False, exception=ValueError("Example of an exception."))

Если вам все же не хватило автоматического логгирования, вы можете писать логи вручную, вызывая функцию log() из своего кода.

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

Подробности

Начнем с общей информации о логгере. Запись в базу данных происходит из отдельных потоков. Ваша программа "выплевывает" логи в очередь, откуда их считывают воркеры в отдельных потоках. Непосредственно запись в БД происходит в момент, когда ваша программа уже продолжает делать что-то другое. Количество потоков, которые пишут в БД, можно настроить (подробнее об этом ниже), по умолчанию оно равно 2-м.

Таблица, в которую происходит запись, выглядит так:

id function module message exception_type exception traceback input_variables result success time time_of_work service auto level
int str str str str str str str str bool datetime float str bool int

Рассмотрим предназначение столбцов в таблице подробнее:

  • id. Главное, что вы должны знать про столбец id - порядок распределения в нем значений не обязан совпадать с реальным порядком следования операций. Запись в базу данных производится из нескольких потоков, асинхронно. Чтобы получить операции в порядке их реального следования, сортируйте таблицу по полю time.
  • function: название функции, действие в которой мы логгируем. При автоматическом логгировании (которое происходит через декораторы), название функции извлекается из атрибута __name__ объекта функции. При ручном логгировании вы можете передать в логгер как сам объект функции, чтобы из нее автоматически извлекся атрибут __name__, так и строку с названием функции. Рекомендуется предпочесть первый вариант, т.к. это снижает вероятность опечаток.
  • module: название модуля, в котором произошло событие. Автоматически извлекается из атрибута __module__ объекта функции.
  • message: произвольный текст, который вы можете приписать к каждому логу.
  • exception_type: тип исключения. Автоматические логи заполняют эту колонку самостоятельно, вручную - вам нужно передать в логгер объект исключения.
  • exception: сообщение, с которым вызывается исключение.
  • traceback: json со списком строк трейсбека. При ручном логгировании данное поле недоступно.
  • input_variables: входные аргументы логгируемой функции. Автоматически логгируются в формате json. Стандартные для json типы данных указываются напрямую, остальные преобразуются в строку. Чтобы вы могли отличить преобразованный в строку объект от собственно строки, к каждой переменной указывается ее оригинальный тип данных из кода python. Для генерации подобных json'ов при ручном логгировании рекомендуется использовать функцию polog.utils.json_vars(), куда можно передавать любый аргументы (позиционные и именные) и получать в результате стандартно оформленный json.
  • result: то, что вернула задекорированная логгером функция. Вы не можете заполнить это поле при ручном логгировании.
  • success: метка успешного завершения операции. При автоматическом логгировании проставляется в значение True, если в задекорированной функции не произошло исключений. При ручном логгировании вы можете проставить метку самостоятельно, либо она заполнится автоматически, если передадите в функцию log объект исключения (False).
  • time: объект datetime, соответствующий дате и времени начала операции. Заполняется всегда автоматически, в том числе при ручном логгировании.
  • time_of_work: время работы задекорированной логгером функции, в секундах. Проставляется автоматически. При ручном логгировании вы не можете указать этот параметр.
  • service: название или идентификатор сервиса, из которого пишутся логи. Идея в том, что в одну базу и в одну таблицу у вас могут писать несколько разных сервисов, а вы можете легко отфильтровывать только те из них, которые вас интересуют в момент чтения логов. Имя сервиса по умолчанию - 'base'. Изменить его вы можете через Config.settings(service_name='<YOUR SERVICE NAME>'), об этом подробнее будет ниже.
  • auto: метка, автоматический лог или ручной. Проставляется автоматически, вы не можете этим управлять.
  • level: уровень важности лога. Подробнее об уровнях будет ниже. На данном этапе вам только нужно знать, что по умолчанию при автоматическом логгировании уровень обычного события - 1, а уровень исключения - 2. Поэтому вам достаточно установить общий уровень логгирования, равный 2-м, чтобы в базу не попадало ничего, кроме ошибок.

Уровни логгирования

Как было сказано выше, по умолчанию автоматические логи имеют 2 уровня: 1 и 2. 1 - это рядовое событие, 2 - исключение. Однако это легко поменять.

В декораторах вы можете указать желаемый уровень логгирования:

from polog.flog import flog


@flog(level=5)
def sum(a, b):
  return a + b

print(sum(2, 2))
# В базу упадет лог с меткой 5 уровня.

Это доступно как для @flog(), так и для @clog(), работает одинаково.

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

from polog.config import Config
from polog.flog import flog


# Присваиваем уровню 5 имя 'ERROR', а уровню 1 - 'ALL'.
Config.levels(ERROR=5, ALL=1)

# Используем присвоенное имя вместо номера уровня.
@flog(level='ERROR')
def sum(a, b):
  return a + b

print(sum(2, 2))
# В базу упадет лог с меткой 5 уровня.

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

Если вы привыкли пользоваться стандартным модулем logging, вы можете присвоить уровням логгирования стандартные имена оттуда:

from polog.config import Config
from polog.flog import flog


# Имена уровням логгирования проставляются автоматически, в соответствии со стандартной схемой.
Config.standart_levels()

@flog(level='ERROR')
def sum(a, b):
  return a + b

print(sum(2, 2))
# В базу упадет лог с меткой 40 уровня.

Также вы можете установить текущий уровень логгирования.

from polog.config import Config
from polog.flog import flog


# Имена уровням логгирования проставляются автоматически, в соответствии со стандартной схемой.
Config.standart_levels()

# Устанавливаем текущий уровень логгирования - 'CRITICAL'.
Config.settings(level='CRITICAL')

@flog(level='ERROR')
def sum(a, b):
  return a + b

print(sum(2, 2))
# Запись в базу произведена не будет, т. к. уровень сообщения 'ERROR' ниже текущего уровня логгирования 'CRITICAL'.

Все события уровнем ниже в базу не пишутся. По умолчанию уровень равен 1.

Используя декораторы, для ошибок вы можете установить отдельный уровень логгирования:

# Работает одинаково в декораторе функций и декораторе классов.
@flog(level='DEBUG', error_level='ERROR')
@clog(level='DEBUG', error_level='ERROR')

Также вы можете установить уровень логгирования для ошибок глобально через настройки:

from polog.config import Config


Config.settings(error_level='CRITICAL')

Сделав это 1 раз, вы можете больше не указывать уровень логгирования локально в каждом декораторе. Но иногда вам это может быть полезным. Уровень, указанный в декораторе, обладает более высоким приоритетом, чем глобальный. Поэтому вы можете, к примеру, для какого-то особо важного класса указать более высокий уроень логгирования. Или наоборот, понизить его, если не хотите в данный момент записывать логи из конкретной функции или класса.

Общие настройки

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

from polog.config import Config

Класс Config предоставляет несколько методов.

  • settings(): общие настройки логгирования.

Принимает следующие именованные параметры:

pool_size (int) - количество потоков-воркеров, которые пишут в базу данных. По умолчанию оно равно 2-м. Вы можете увеличить это число, если ваша программа пишет в базу достаточно интенсивно.

service_name (str) - имя сервиса. Указывается в каждой записи в базу. По умолчанию 'base'.

level (int) - общий уровень логгирования. Подробнее в разделе "Уровни логгирования" (выше).

errors_level (int) - уровень логгирования для ошибок. Также см. в "Уровни логгирования".

original_exceptions (bool) - режим подмены оригинальных исключений. По умолчанию False. True означает, что все исключения остаются как были и никак не видоизменяются логгером. Это может приводить к дублированию информации об одной ошибке в базе данных, т. к. исключение, поднимаясь по стеку вызовов функций, может пройти через несколько задекорированных логгером функций. В режиме False все исключения логгируются 1 раз, после чего оригинальное исключение подменяется на polog.errors.LoggedError, которое не логгируется никогда.

  • levels(): присвоение имен уровням логгирования, см. подробнее в разделе "Уровни логгирования".

  • standart_levels(): присвоение стандартных имен уровням логгирования, см. подробнее в разделе "Уровни логгирования".

  • db(): указание базы данных, куда будут писаться логи.

Для управления базой данных Polog использует Pony ORM - самую быструю и удобную ORM из доступных на python. В метод db() вы можете передать данные для подключения к базе данных в том же формате, в каком это делается в методе bind() самой ORM.

Pony поддерживает: SQLite, PostgreSQL, MySQL, Oracle, CockroachDB.

Если вы не укажете никакую базу данных, по умолчанию логи будут писаться в базу SQLite, которая будет автоматически создана в папке с проектом в файле с названием logs.db.

Декоратор @flog()

Декоратор @flog() используется для автоматического логгирования вызовов функций. Поддерживает как обычные функции, так и корутины.

Напомним, он импортируется так:

from polog.flog import flog

Используйте параметр message для добавления произвольного текста к каждому логу.

@flog(message='This function is very important!!!')
def very_important_function():
  ...

Про управление уровнями логгирования через аргументы к данному декоратору читайте в разделе "Уровни логгирования" (выше).

@clog() - декоратор класса

По традиции, вспомним, откуда он импортируется:

from polog.clog import clog

Принимает все те же аргументы, что и @flog(). Автоматически навешивает декоратор @flog() на все методы задекорированного класса.

Игнорирует дандер-методы (методы, чьи названия начинаются с "__").

Если не хотите логировать все методы класса, можете перечислить нужные в качестве неименованных аргументов:

@сlog('important_method', message='This class is also very important!!!')
class VeryImportantClass():
  def important_method(self):
    ...
  def not_important_method(self):
    ...
  ...

"Ручное" логгирование через log()

Отдельные важные события в вашем коде вы можете регистрировать вручную.

Импортируйте функцию log():

from polog.log import log

И используйте ее в вашем коде:

log(message='Very important message!!!')

Уровень логгирования вы можете передать первым неименованным аргументом:

# Когда псевдонимы для уровней логгирования прописаны по стандартной схеме.
log('ERROR', message='Very important message!!!')
# Ну или просто в виде числа.
log(40, message='Very important message!!!')

Вы можете передать в log() функцию, в которой исполняется код:

def foo():
  log(function=foo)

Колонки function и module в этом случае заполнятся автоматически.

Также вы можете передать в log() экземпляр исключения:

try:
  var = 1 / 0
except ZeroDivisionError as e:
  log(exception=e)

Колонки exception и exception_type тогда тоже заполнятся автоматически. Флаг success будет установлен в значение False. Трейсбек автоматически не заполняется и задать его вручную невозможно.

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

Также вы можете передавать в log() произвольные переменные, которые считаете нужным залоггировать. Для этого нужно использовать функцию json_vars(), которая принимает любые аргументы и переводит их в стандартный json-формат:

from polog.utils.json_vars import json_vars
from polog.log import log


def bar(a, b, c, other=None):
  ...
  log(function=bar, vars=json_vars(a, b, c, other=other))
  ...

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

polog-0.0.1.tar.gz (22.4 kB view hashes)

Uploaded Source

Built Distribution

polog-0.0.1-py3-none-any.whl (18.2 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page