====== Добавление ошибок в проект ====== Аутентификация к 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` дополнительными параметрами