Как Sentry агрегирует ошибки?
Я использую Sentry (в проекте django), и я хотел бы знать, как я могу правильно собрать ошибки. Я регистрирую определенные действия пользователя как ошибки, поэтому нет основного системного исключения, и я использую атрибут culprit
для установки дружественного имени ошибки. Сообщение шаблонизировано и содержит общее сообщение ( "Пользователь" x "не смог выполнить действие, потому что" y "), но никогда не бывает точно таким же (разные пользователи, разные условия).
Sentry явно использует некоторый набор атрибутов под капотом, чтобы определить, следует ли агрегировать ошибки как одно и то же исключение, но, несмотря на просмотр кода, я не могу понять, как это сделать.
Может ли кто-нибудь сократить мои потребности в копании дальше в код и рассказать мне, какие свойства мне нужно установить, чтобы управлять агрегацией, как мне бы хотелось?
[ОБНОВЛЕНИЕ 1: группировка событий]
Эта строка появляется в sendry.models.Group:
class Group(MessageBase):
"""
Aggregated message which summarizes a set of Events.
"""
...
class Meta:
unique_together = (('project', 'logger', 'culprit', 'checksum'),)
...
Что имеет смысл - проект, регистратор и виновник, который я устанавливаю на данный момент - проблема checksum
. Я буду исследовать далее, однако "контрольная сумма" предполагает, что бинарная эквивалентность, которая никогда не сработает - должна быть возможность группировать экземпляры одного и того же исключения с атрибутами differenct?
[ОБНОВЛЕНИЕ 2: контрольные суммы событий]
Контрольная сумма события исходит из метода sentry.manager.get_checksum_from_event
:
def get_checksum_from_event(event):
for interface in event.interfaces.itervalues():
result = interface.get_hash()
if result:
hash = hashlib.md5()
for r in result:
hash.update(to_string(r))
return hash.hexdigest()
return hashlib.md5(to_string(event.message)).hexdigest()
Следующая остановка - откуда происходит событие interfaces
?
[ОБНОВЛЕНИЕ 3: интерфейсы событий]
Я разработал, что interfaces ссылается на стандартный механизм описания данных, передаваемых в часовые события, и что я использую стандартные sentry.interfaces.Message
и sentry.interfaces.User
.
Оба они будут содержать разные данные в зависимости от экземпляра исключения - и поэтому контрольная сумма никогда не будет соответствовать. Есть ли способ, который я могу исключить из расчета контрольной суммы? (Или, по крайней мере, значение интерфейса User
, поскольку это должно быть другим - значение интерфейса Message
, которое я мог бы стандартизировать.)
[ОБНОВЛЕНИЕ 4: решение]
Вот две функции get_hash
для интерфейсов Message
и User
соответственно:
# sentry.interfaces.Message
def get_hash(self):
return [self.message]
# sentry.interfaces.User
def get_hash(self):
return []
Посмотрев на эти два, только интерфейс Message.get_hash
вернет значение, которое подхвачено методом get_checksum_for_event
, и, таким образом, это тот, который будет возвращен (хэширован и т.д.). Чистый эффект этого что контрольная сумма оценивается только по одному сообщению, что теоретически означает, что я могу стандартизировать сообщение и сохранить уникальное определение пользователя.
Я ответил на свой собственный вопрос здесь, но, надеюсь, мое исследование полезно другим, имеющим ту же проблему. (В стороне, я также представил запрос на тягу к документации Sentry как часть этого; -))
(Обратите внимание на тех, кто использует/расширяя Sentry с пользовательскими интерфейсами - если вы хотите, чтобы ваш интерфейс не использовался для группировки исключений, верните пустой список.)
Ответы
Ответ 1
Посмотрите мое последнее обновление в самом вопросе. События объединяются в комбинации свойств "project", "logger", "culprit" и "checksum". Первые три из них относительно просты в управлении - четвертая, контрольная сумма - это функция типа данных, отправленных как часть события.
Sentry использует концепцию "интерфейсов" для управления структурой передаваемых данных, и каждый интерфейс поставляется с реализацией get_hash
, которая используется для возврата хэш-значения для переданных данных. Sentry поставляется с количество стандартных интерфейсов ( "Message", "User", "HTTP", "Stacktrace", "Query", "Exception" ), и каждый из них имеет собственную реализацию get_hash
. По умолчанию (унаследованный от базового класса интерфейса) есть пустой список, который не влияет на контрольную сумму.
В отсутствие каких-либо допустимых интерфейсов сообщение о событиях хэшируется и возвращается в качестве контрольной суммы, что означает, что сообщение должно быть уникальным для сгруппированного события.
Ответ 2
У меня была общая проблема с Exceptions. В настоящее время наша система захватывает только исключения, и я был смущен, почему некоторые из них слились в одну ошибку, другие - нет.
С вашей информацией выше я добавил методы "get_hash" и попытался найти различия, "поднимающие" мои ошибки. То, что я узнал, состоит в том, что сгруппированные ошибки исходили из самонаписанного типа исключения, у которого есть пустое значение Exception.message.
вывод get_hash:
[<class 'StorageException'>, StorageException()]
и множественные ошибки возникли из класса исключения, у которого есть заполненное значение сообщения (механизм шаблонов jinja)
[<class 'jinja2.exceptions.UndefinedError'>, UndefinedError('dict object has no attribute LISTza_*XYZ*',)]
Различные сообщения об исключении запускают разные отчеты, в моем случае слияние было вызвано отсутствием значения Exception.message.
Реализация:
class StorageException(Exception):
def __init__(self, value):
Exception.__init__(self)
self.value = value