====== Добавление ошибок в проект ======
Аутентификация к API запросу происходит через токен проекта.
Его можно найти в интерфейсе редактирования проекта в поле **Токен**.
API_URL = https://err-scout.ru/api
====== Рекомендуемый способ передачи ошибок ======
В этом разделе представлен рекомендуемый способ автоматической отправки ошибок в API `https://err-scout.ru/api` для внешних Python-приложений.
Используется декоратор `@detailed_exception_logger`, который автоматически:
* перехватывает исключения,
* извлекает стек вызовов и контекст исполнения,
* подготавливает структуру ошибки по формату `Error.create`,
* и отправляет её в API с авторизацией через токен проекта.
При возникновении исключения, декоратор автоматически формирует объект `error_data`, содержащий:
* **error_type** — тип исключения
* **error_message** — текст ошибки
* **timestamp** — время в формате ISO 8601
* **environment** — окружение (по умолчанию: `local`)
* **release** — версия приложения (по умолчанию: `unversioned`)
* **platform** — ОС
* **language_version** — версия Python
* **user_id** — идентификатор пользователя (можно настроить)
* **user_email** — email пользователя (опционально)
* **request_data** — информация о контексте вызова
* **stack_trace** — список фреймов с переменными
* **tags** — теги в виде пар ключ/значение
===== Пример использования =====
@detailed_exception_logger
def divide(a, b):
return a / b
divide(10, 0)
===== Код декоратора =====
import json
import traceback
import sys
import os
import inspect
from datetime import datetime
from functools import wraps
import requests
from config import API_URL, AUTH_TOKEN, ENVIRONMENT, RELEASE
def send_error_to_api(error_data: dict):
headers = {
'Content-Type': 'application/json',
'Authorization': AUTH_TOKEN
}
payload = {
"jsonrpc": "2.0",
"method": "Error.create",
"params": error_data,
"id": 1
}
response = requests.post(API_URL, json=payload, headers=headers)
response.raise_for_status()
return response.json()
def detailed_exception_logger(func):
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
exc_type, exc_value, exc_tb = sys.exc_info()
timestamp = datetime.utcnow().isoformat()
stack_trace = []
tb = exc_tb
index = 0
while tb:
frame = tb.tb_frame
lineno = tb.tb_lineno
filename = frame.f_code.co_filename
func_name = frame.f_code.co_name
try:
with open(filename, 'r', encoding='utf-8') as f:
lines = f.readlines()
code_line = lines[lineno - 1].strip() if lineno <= len(lines) else ""
except Exception:
code_line = ""
stack_trace.append({
"file": filename,
"line": lineno,
"function": func_name,
"code": code_line,
"index": index
})
tb = tb.tb_next
index += 1
error_data = {
"error_type": exc_type.__name__,
"error_message": str(exc_value),
"timestamp": timestamp,
"environment": ENVIRONMENT,
"release": RELEASE,
"platform": sys.platform,
"language_version": sys.version.split()[0],
"user_id": "auto-reporter",
"user_email": "",
"request_data": {
"function": func.__name__,
"args": [repr(a) for a in args],
"kwargs": {k: repr(v) for k, v in kwargs.items()}
},
"stack_trace": stack_trace,
"tags": [
{"key": "module", "value": func.__module__},
{"key": "pid", "value": str(os.getpid())},
{"key": "cwd", "value": os.getcwd()}
]
}
try:
send_error_to_api(error_data)
except Exception as api_error:
print(f"[error-scout] Failed to send error: {api_error}")
print(json.dumps(error_data, indent=2))
raise
return wrapper
===== Дополнительно =====
* При необходимости можно расширить `request_data` или `tags` дополнительными параметрами