refactor(logging): use logger.exception to preserve stack traces

Unify error logging across services: replace logger.error(f"...{e}")
and logger.error(..., exc_info=True) with logger.exception() so the
stack trace is consistently captured.
This commit is contained in:
3252a8
2026-04-27 08:43:48 +03:00
parent f1113eb80a
commit d6b703debb
9 changed files with 68 additions and 71 deletions
+14 -14
View File
@@ -121,16 +121,16 @@ class CryptoPayService:
str(invoice.status), str(invoice.status),
) )
await session.commit() await session.commit()
except Exception as e_db_update: except Exception:
await session.rollback() await session.rollback()
logging.error( logging.exception(
f"Failed to update cryptopay payment record {payment_record.payment_id}: {e_db_update}", "Failed to update cryptopay payment record %s.",
exc_info=True, payment_record.payment_id,
) )
return None return None
return invoice.bot_invoice_url return invoice.bot_invoice_url
except Exception as e: except Exception:
logging.error(f"CryptoPay invoice creation failed: {e}", exc_info=True) logging.exception("CryptoPay invoice creation failed.")
return None return None
async def _invoice_paid_handler(self, update: Update, app: web.Application): async def _invoice_paid_handler(self, update: Update, app: web.Application):
@@ -145,8 +145,8 @@ class CryptoPayService:
payment_db_id = int(meta["payment_db_id"]) payment_db_id = int(meta["payment_db_id"])
sale_mode = meta.get("sale_mode") or ("traffic" if self.settings.traffic_sale_mode else "subscription") sale_mode = meta.get("sale_mode") or ("traffic" if self.settings.traffic_sale_mode else "subscription")
traffic_gb = float(meta.get("traffic_gb")) if meta.get("traffic_gb") else months traffic_gb = float(meta.get("traffic_gb")) if meta.get("traffic_gb") else months
except Exception as e: except Exception:
logging.error(f"Failed to parse CryptoPay payload: {e}") logging.exception("Failed to parse CryptoPay payload.")
return return
async_session_factory: sessionmaker = app["async_session_factory"] async_session_factory: sessionmaker = app["async_session_factory"]
@@ -184,9 +184,9 @@ class CryptoPayService:
skip_if_active_before_payment=False, skip_if_active_before_payment=False,
) )
await session.commit() await session.commit()
except Exception as e: except Exception:
await session.rollback() await session.rollback()
logging.error(f"Failed to process CryptoPay invoice: {e}", exc_info=True) logging.exception("Failed to process CryptoPay invoice.")
return return
db_user = await user_dal.get_user_by_id(session, user_id) db_user = await user_dal.get_user_by_id(session, user_id)
@@ -247,8 +247,8 @@ class CryptoPayService:
parse_mode="HTML", parse_mode="HTML",
disable_web_page_preview=True, disable_web_page_preview=True,
) )
except Exception as e: except Exception:
logging.error(f"Failed to send CryptoPay success message: {e}") logging.exception("Failed to send CryptoPay success message.")
# Send notification about payment # Send notification about payment
try: try:
@@ -263,8 +263,8 @@ class CryptoPayService:
payment_provider="crypto_pay", payment_provider="crypto_pay",
username=user.username if user else None username=user.username if user else None
) )
except Exception as e: except Exception:
logging.error(f"Failed to send crypto_pay payment notification: {e}") logging.exception("Failed to send crypto_pay payment notification.")
def _validate_webhook_signature(self, raw_body: bytes, signature: str) -> bool: def _validate_webhook_signature(self, raw_body: bytes, signature: str) -> bool:
if not self.token: if not self.token:
+9 -9
View File
@@ -137,7 +137,7 @@ class FreeKassaService:
return True, response_data return True, response_data
except Exception as exc: except Exception as exc:
logging.error("FreeKassa create_order: request failed: %s", exc, exc_info=True) logging.exception("FreeKassa create_order: request failed.")
return False, {"message": str(exc)} return False, {"message": str(exc)}
async def _get_session(self) -> ClientSession: async def _get_session(self) -> ClientSession:
@@ -196,8 +196,8 @@ class FreeKassaService:
return web.Response(status=403) return web.Response(status=403)
raw_body = await request.read() raw_body = await request.read()
except Exception as e: except Exception:
logging.error("FreeKassa webhook: failed to read request body: %s", e) logging.exception("FreeKassa webhook: failed to read request body.")
return web.Response(status=400, text="bad_request") return web.Response(status=400, text="bad_request")
payload_dict: Dict[str, Any] = {} payload_dict: Dict[str, Any] = {}
@@ -299,9 +299,9 @@ class FreeKassaService:
) )
await session.commit() await session.commit()
except Exception as e: except Exception:
await session.rollback() await session.rollback()
logging.error(f"FreeKassa webhook: failed to process payment {payment_db_id}: {e}", exc_info=True) logging.exception("FreeKassa webhook: failed to process payment %s.", payment_db_id)
return web.Response(status=500, text="processing_error") return web.Response(status=500, text="processing_error")
db_user = payment.user or await user_dal.get_user_by_id(session, payment.user_id) db_user = payment.user or await user_dal.get_user_by_id(session, payment.user_id)
@@ -385,8 +385,8 @@ class FreeKassaService:
parse_mode="HTML", parse_mode="HTML",
disable_web_page_preview=True, disable_web_page_preview=True,
) )
except Exception as e: except Exception:
logging.error(f"FreeKassa notification: failed to send message to user {payment.user_id}: {e}") logging.exception("FreeKassa notification: failed to send message to user %s.", payment.user_id)
try: try:
notification_service = NotificationService(self.bot, self.settings, self.i18n) notification_service = NotificationService(self.bot, self.settings, self.i18n)
@@ -399,8 +399,8 @@ class FreeKassaService:
payment_provider="freekassa", payment_provider="freekassa",
username=db_user.username if db_user else None, username=db_user.username if db_user else None,
) )
except Exception as e: except Exception:
logging.error(f"FreeKassa notification: failed to notify admins: {e}") logging.exception("FreeKassa notification: failed to notify admins.")
return web.Response(text="YES") return web.Response(text="YES")
+8 -8
View File
@@ -120,8 +120,8 @@ class NotificationService:
logging.error( logging.error(
f"Failed to send notification to log channel {self.settings.LOG_CHAT_ID}: {exc}" f"Failed to send notification to log channel {self.settings.LOG_CHAT_ID}: {exc}"
) )
except Exception as e: except Exception:
logging.error(f"Failed to send notification to log channel {self.settings.LOG_CHAT_ID}: {e}") logging.exception("Failed to send notification to log channel %s.", self.settings.LOG_CHAT_ID)
return return
try: try:
@@ -143,8 +143,8 @@ class NotificationService:
# Queue message for sending (groups are rate limited to 15/minute) # Queue message for sending (groups are rate limited to 15/minute)
await queue_manager.send_message(self.settings.LOG_CHAT_ID, **kwargs) await queue_manager.send_message(self.settings.LOG_CHAT_ID, **kwargs)
except Exception as e: except Exception:
logging.error(f"Failed to queue notification to log channel {self.settings.LOG_CHAT_ID}: {e}") logging.exception("Failed to queue notification to log channel %s.", self.settings.LOG_CHAT_ID)
async def _send_to_admins(self, message: str): async def _send_to_admins(self, message: str):
"""Send message to all admin users using message queue""" """Send message to all admin users using message queue"""
@@ -162,8 +162,8 @@ class NotificationService:
parse_mode="HTML", parse_mode="HTML",
disable_web_page_preview=True disable_web_page_preview=True
) )
except Exception as e: except Exception:
logging.error(f"Failed to send notification to admin {admin_id}: {e}") logging.exception("Failed to send notification to admin %s.", admin_id)
return return
for admin_id in self.settings.ADMIN_IDS: for admin_id in self.settings.ADMIN_IDS:
@@ -174,8 +174,8 @@ class NotificationService:
parse_mode="HTML", parse_mode="HTML",
disable_web_page_preview=True disable_web_page_preview=True
) )
except Exception as e: except Exception:
logging.error(f"Failed to queue notification to admin {admin_id}: {e}") logging.exception("Failed to queue notification to admin %s.", admin_id)
async def notify_new_user_registration(self, user_id: int, username: Optional[str] = None, async def notify_new_user_registration(self, user_id: int, username: Optional[str] = None,
first_name: Optional[str] = None, first_name: Optional[str] = None,
+1 -1
View File
@@ -174,7 +174,7 @@ class PanelApiService:
"message": f"Connection error: {str(e)}" "message": f"Connection error: {str(e)}"
} }
except aiohttp.ClientError as e: except aiohttp.ClientError as e:
logging.error(f"Panel API ClientError to {url_for_request}: {e}") logging.exception("Panel API ClientError to %s.", url_for_request)
return { return {
"error": True, "error": True,
"status_code": -2, "status_code": -2,
+11 -11
View File
@@ -127,7 +127,7 @@ class PlategaService:
return True, response_data return True, response_data
except Exception as exc: except Exception as exc:
logging.error("Platega create_transaction: request failed: %s", exc, exc_info=True) logging.exception("Platega create_transaction: request failed.")
return False, {"message": str(exc)} return False, {"message": str(exc)}
async def webhook_route(self, request: web.Request) -> web.Response: async def webhook_route(self, request: web.Request) -> web.Response:
@@ -136,8 +136,8 @@ class PlategaService:
try: try:
data = await request.json() data = await request.json()
except Exception as exc: except Exception:
logging.error("Platega webhook: failed to parse JSON: %s", exc) logging.exception("Platega webhook: failed to parse JSON.")
return web.Response(status=400, text="bad_request") return web.Response(status=400, text="bad_request")
header_merchant = request.headers.get("X-MerchantId") header_merchant = request.headers.get("X-MerchantId")
@@ -215,9 +215,9 @@ class PlategaService:
) )
await session.commit() await session.commit()
except Exception as exc: except Exception:
await session.rollback() await session.rollback()
logging.error("Platega webhook: failed to process payment %s: %s", transaction_id, exc, exc_info=True) logging.exception("Platega webhook: failed to process payment %s.", transaction_id)
return web.Response(status=500, text="processing_error") return web.Response(status=500, text="processing_error")
db_user = await user_dal.get_user_by_id(session, payment.user_id) db_user = await user_dal.get_user_by_id(session, payment.user_id)
@@ -296,8 +296,8 @@ class PlategaService:
parse_mode="HTML", parse_mode="HTML",
disable_web_page_preview=True, disable_web_page_preview=True,
) )
except Exception as exc: except Exception:
logging.error("Platega webhook: failed to notify user %s: %s", payment.user_id, exc) logging.exception("Platega webhook: failed to notify user %s.", payment.user_id)
try: try:
notification_service = NotificationService(self.bot, self.settings, self.i18n) notification_service = NotificationService(self.bot, self.settings, self.i18n)
@@ -310,8 +310,8 @@ class PlategaService:
payment_provider="platega", payment_provider="platega",
username=db_user.username if db_user else None, username=db_user.username if db_user else None,
) )
except Exception as exc: except Exception:
logging.error("Platega webhook: failed to notify admins: %s", exc) logging.exception("Platega webhook: failed to notify admins.")
return web.Response(text="ok") return web.Response(text="ok")
@@ -324,9 +324,9 @@ class PlategaService:
"canceled", "canceled",
) )
await session.commit() await session.commit()
except Exception as exc: except Exception:
await session.rollback() await session.rollback()
logging.error("Platega webhook: failed to cancel payment %s: %s", transaction_id, exc) logging.exception("Platega webhook: failed to cancel payment %s.", transaction_id)
return web.Response(status=500, text="processing_error") return web.Response(status=500, text="processing_error")
db_user = await user_dal.get_user_by_id(session, payment.user_id) db_user = await user_dal.get_user_by_id(session, payment.user_id)
+13 -13
View File
@@ -142,7 +142,7 @@ class SeverPayService:
return True, response_data.get("data") or response_data return True, response_data.get("data") or response_data
except Exception as exc: except Exception as exc:
logging.error("SeverPay create_payment: request failed: %s", exc, exc_info=True) logging.exception("SeverPay create_payment: request failed.")
return False, {"message": str(exc)} return False, {"message": str(exc)}
async def webhook_route(self, request: web.Request) -> web.Response: async def webhook_route(self, request: web.Request) -> web.Response:
@@ -151,8 +151,8 @@ class SeverPayService:
try: try:
payload = await request.json() payload = await request.json()
except Exception as exc: except Exception:
logging.error("SeverPay webhook: failed to parse JSON: %s", exc) logging.exception("SeverPay webhook: failed to parse JSON.")
return web.json_response({"status": False, "msg": "bad_request"}, status=400) return web.json_response({"status": False, "msg": "bad_request"}, status=400)
if not isinstance(payload, dict) or not self._validate_signature(payload): if not isinstance(payload, dict) or not self._validate_signature(payload):
@@ -223,9 +223,9 @@ class SeverPayService:
) )
await session.commit() await session.commit()
except Exception as exc: except Exception:
await session.rollback() await session.rollback()
logging.error("SeverPay webhook: failed to process payment %s: %s", provider_payment_id, exc, exc_info=True) logging.exception("SeverPay webhook: failed to process payment %s.", provider_payment_id)
return web.json_response({"status": False, "msg": "processing_error"}, status=500) return web.json_response({"status": False, "msg": "processing_error"}, status=500)
db_user = payment.user or await user_dal.get_user_by_id(session, payment.user_id) db_user = payment.user or await user_dal.get_user_by_id(session, payment.user_id)
@@ -304,8 +304,8 @@ class SeverPayService:
parse_mode="HTML", parse_mode="HTML",
disable_web_page_preview=True, disable_web_page_preview=True,
) )
except Exception as exc: except Exception:
logging.error("SeverPay webhook: failed to notify user %s: %s", payment.user_id, exc) logging.exception("SeverPay webhook: failed to notify user %s.", payment.user_id)
try: try:
notification_service = NotificationService(self.bot, self.settings, self.i18n) notification_service = NotificationService(self.bot, self.settings, self.i18n)
@@ -318,8 +318,8 @@ class SeverPayService:
payment_provider="severpay", payment_provider="severpay",
username=db_user.username if db_user else None, username=db_user.username if db_user else None,
) )
except Exception as exc: except Exception:
logging.error("SeverPay webhook: failed to notify admins: %s", exc) logging.exception("SeverPay webhook: failed to notify admins.")
return web.json_response({"status": True}) return web.json_response({"status": True})
@@ -332,9 +332,9 @@ class SeverPayService:
"failed", "failed",
) )
await session.commit() await session.commit()
except Exception as exc: except Exception:
await session.rollback() await session.rollback()
logging.error("SeverPay webhook: failed to mark payment %s as failed: %s", provider_payment_id, exc) logging.exception("SeverPay webhook: failed to mark payment %s as failed.", provider_payment_id)
return web.json_response({"status": False, "msg": "processing_error"}, status=500) return web.json_response({"status": False, "msg": "processing_error"}, status=500)
db_user = payment.user or await user_dal.get_user_by_id(session, payment.user_id) db_user = payment.user or await user_dal.get_user_by_id(session, payment.user_id)
@@ -355,9 +355,9 @@ class SeverPayService:
"pending_severpay", "pending_severpay",
) )
await session.commit() await session.commit()
except Exception as exc: except Exception:
await session.rollback() await session.rollback()
logging.error("SeverPay webhook: failed to update pending status for %s: %s", provider_payment_id, exc) logging.exception("SeverPay webhook: failed to update pending status for %s.", provider_payment_id)
return web.json_response({"status": True}) return web.json_response({"status": True})
logging.warning("SeverPay webhook: unhandled status '%s' for payment %s", status, provider_payment_id) logging.warning("SeverPay webhook: unhandled status '%s' for payment %s", status, provider_payment_id)
+1 -1
View File
@@ -55,7 +55,7 @@ class StarsService:
title=description, title=description,
description=description, description=description,
payload=payload, payload=payload,
provider_token="", provider_token="", # Required to be empty for Telegram Stars (XTR) per Telegram Bot API.
currency="XTR", currency="XTR",
prices=prices, prices=prices,
) )
+9 -12
View File
@@ -35,9 +35,8 @@ class YooKassaService:
self.configured = True self.configured = True
logging.info( logging.info(
f"YooKassa SDK configured for shop_id: {shop_id[:5]}...") f"YooKassa SDK configured for shop_id: {shop_id[:5]}...")
except Exception as e: except Exception:
logging.error(f"Failed to configure YooKassa SDK: {e}", logging.exception("Failed to configure YooKassa SDK.")
exc_info=True)
self.configured = False self.configured = False
if configured_return_url: if configured_return_url:
@@ -201,9 +200,8 @@ class YooKassaService:
response.test if hasattr(response, 'test') else None, response.test if hasattr(response, 'test') else None,
"payment_method": getattr(response, 'payment_method', None), "payment_method": getattr(response, 'payment_method', None),
} }
except Exception as e: except Exception:
logging.error(f"YooKassa payment creation failed: {e}", logging.exception("YooKassa payment creation failed.")
exc_info=True)
return None return None
async def get_payment_info( async def get_payment_info(
@@ -266,10 +264,9 @@ class YooKassaService:
f"No payment info found in YooKassa for ID: {payment_id_in_yookassa}" f"No payment info found in YooKassa for ID: {payment_id_in_yookassa}"
) )
return None return None
except Exception as e: except Exception:
logging.error( logging.exception(
f"YooKassa get payment info for {payment_id_in_yookassa} failed: {e}", "YooKassa get payment info for %s failed.", payment_id_in_yookassa)
exc_info=True)
return None return None
async def cancel_payment(self, payment_id_in_yookassa: str) -> bool: async def cancel_payment(self, payment_id_in_yookassa: str) -> bool:
@@ -280,6 +277,6 @@ class YooKassaService:
await asyncio.to_thread(YooKassaPayment.cancel, payment_id_in_yookassa) await asyncio.to_thread(YooKassaPayment.cancel, payment_id_in_yookassa)
logging.info(f"Cancelled YooKassa payment {payment_id_in_yookassa}") logging.info(f"Cancelled YooKassa payment {payment_id_in_yookassa}")
return True return True
except Exception as e: except Exception:
logging.error(f"Failed to cancel YooKassa payment {payment_id_in_yookassa}: {e}") logging.exception("Failed to cancel YooKassa payment %s.", payment_id_in_yookassa)
return False return False
+2 -2
View File
@@ -82,9 +82,9 @@ class MessageQueue:
self.total_failed += 1 self.total_failed += 1
logging.error(f"Failed to send queued message to {message.chat_id}: {exc}") logging.error(f"Failed to send queued message to {message.chat_id}: {exc}")
except Exception as e: except Exception:
self.total_failed += 1 self.total_failed += 1
logging.error(f"Failed to send queued message to {message.chat_id}: {e}") logging.exception("Failed to send queued message to %s.", message.chat_id)
finally: finally:
self.is_processing = False self.is_processing = False