refactor(log): refactor the whole project
format: {time:YYYY-MM-DD HH:mm:ss} [{level}] | {name} | {message}
{name} is:
- Uvicorn: log from uvicorn server (#228B22)
- Service: log from class of `app.service` (blue)
- Fetcher: log from fetchers (magenta)
- Task: log from `app.tasks` (#FFD700)
- System: log from `system_logger` (red)
- Normal: log from `log(name)` (#FFC1C1)
- Default: the module name of caller
if you are writing services or tasks, you can just call `logger.`, we will pack it with name `Service` or `Task`
if you want to print fetcher logs, system-related logs, or normal logs, use `logger = (fetcher_logger / system_logger / log)(name)`
This commit is contained in:
@@ -166,7 +166,7 @@ class BeatmapsetUpdateService:
|
||||
except Exception as e:
|
||||
logger.error(f"failed to add missing beatmapset {missing}: {e}")
|
||||
if total > 0:
|
||||
logger.info(f"added {total} missing beatmapset")
|
||||
logger.opt(colors=True).info(f"added {total} missing beatmapset")
|
||||
self._adding_missing = False
|
||||
|
||||
async def add(self, beatmapset: BeatmapsetResp):
|
||||
@@ -206,12 +206,14 @@ class BeatmapsetUpdateService:
|
||||
processing = ProcessingBeatmapset(beatmapset, sync_record)
|
||||
next_time_delta = processing.calculate_next_sync_time()
|
||||
if not next_time_delta:
|
||||
logger.info(f"[{beatmapset.id}] beatmapset has transformed to ranked or loved, removing from sync list")
|
||||
logger.opt(colors=True).info(
|
||||
f"<g>[{beatmapset.id}]</g> beatmapset has transformed to ranked or loved, removing from sync list"
|
||||
)
|
||||
await session.delete(sync_record)
|
||||
await session.commit()
|
||||
return
|
||||
sync_record.next_sync_time = utcnow() + next_time_delta
|
||||
logger.info(f"[{beatmapset.id}] next sync at {sync_record.next_sync_time}")
|
||||
logger.opt(colors=True).info(f"<g>[{beatmapset.id}]</g> next sync at {sync_record.next_sync_time}")
|
||||
await session.commit()
|
||||
|
||||
async def _update_beatmaps(self):
|
||||
@@ -224,17 +226,19 @@ class BeatmapsetUpdateService:
|
||||
.order_by(col(BeatmapSync.next_sync_time).desc())
|
||||
)
|
||||
for record in records:
|
||||
logger.info(f"[{record.beatmapset_id}] syncing...")
|
||||
logger.opt(colors=True).info(f"<g>[{record.beatmapset_id}]</g> syncing...")
|
||||
try:
|
||||
beatmapset = await self.fetcher.get_beatmapset(record.beatmapset_id)
|
||||
except Exception as e:
|
||||
if isinstance(e, HTTPError):
|
||||
logger.warning(
|
||||
f"[{record.beatmapset_id}] "
|
||||
logger.opt(colors=True).warning(
|
||||
f"<g>[{record.beatmapset_id}]</g> "
|
||||
f"failed to fetch beatmapset: [{e.__class__.__name__}] {e}, retrying later"
|
||||
)
|
||||
else:
|
||||
logger.exception(f"[{record.beatmapset_id}] unexpected error: {e}, retrying later")
|
||||
logger.opt(colors=True).exception(
|
||||
f"<g>[{record.beatmapset_id}]</g> unexpected error: {e}, retrying later"
|
||||
)
|
||||
record.next_sync_time = utcnow() + timedelta(seconds=MIN_DELTA)
|
||||
continue
|
||||
processing = ProcessingBeatmapset(beatmapset, record)
|
||||
@@ -266,15 +270,16 @@ class BeatmapsetUpdateService:
|
||||
|
||||
next_time_delta = processing.calculate_next_sync_time()
|
||||
if not next_time_delta:
|
||||
logger.info(
|
||||
f"[{record.beatmapset_id}] beatmapset "
|
||||
"has transformed to ranked or loved,"
|
||||
" removing from sync list"
|
||||
logger.opt(colors=True).info(
|
||||
f"<yellow>[{beatmapset.id}]</yellow> beatmapset has transformed to ranked or loved,"
|
||||
f" removing from sync list"
|
||||
)
|
||||
await session.delete(record)
|
||||
else:
|
||||
record.next_sync_time = utcnow() + next_time_delta
|
||||
logger.info(f"[{record.beatmapset_id}] next sync at {record.next_sync_time}")
|
||||
logger.opt(colors=True).info(
|
||||
f"<g>[{record.beatmapset_id}]</g> next sync at {record.next_sync_time}"
|
||||
)
|
||||
await session.commit()
|
||||
|
||||
async def _process_changed_beatmapset(self, beatmapset: BeatmapsetResp):
|
||||
@@ -304,7 +309,7 @@ class BeatmapsetUpdateService:
|
||||
await score.ranked_score.delete(session)
|
||||
total += 1
|
||||
if total > 0:
|
||||
logger.info(f"[beatmap: {beatmap_id}] processed {total} old scores")
|
||||
logger.opt(colors=True).info(f"<g>[beatmap: {beatmap_id}]</g> processed {total} old scores")
|
||||
await session.commit()
|
||||
|
||||
for change in changed:
|
||||
@@ -312,17 +317,24 @@ class BeatmapsetUpdateService:
|
||||
try:
|
||||
beatmap = await self.fetcher.get_beatmap(change.beatmap_id)
|
||||
except Exception as e:
|
||||
logger.error(f"[beatmap: {change.beatmap_id}] failed to fetch added beatmap: {e}, skipping")
|
||||
logger.opt(colors=True).error(
|
||||
f"<g>[beatmap: {change.beatmap_id}]</g> failed to fetch added beatmap: {e}, skipping"
|
||||
)
|
||||
continue
|
||||
logger.info(f"[{beatmap.beatmapset_id}] adding beatmap {beatmap.id}")
|
||||
logger.opt(colors=True).info(f"[{beatmap.beatmapset_id}] adding beatmap {beatmap.id}")
|
||||
await Beatmap.from_resp_no_save(session, beatmap)
|
||||
else:
|
||||
try:
|
||||
beatmap = await self.fetcher.get_beatmap(change.beatmap_id)
|
||||
except Exception as e:
|
||||
logger.error(f"[beatmap: {change.beatmap_id}] failed to fetch changed beatmap: {e}, skipping")
|
||||
logger.opt(colors=True).error(
|
||||
f"<g>[beatmap: {change.beatmap_id}]</g> failed to fetch changed beatmap: {e}, skipping"
|
||||
)
|
||||
continue
|
||||
logger.info(f"[{beatmap.beatmapset_id}] processing beatmap {beatmap.id} change {change.type}")
|
||||
logger.opt(colors=True).info(
|
||||
f"<g>[{beatmap.beatmapset_id}]</g> processing beatmap <blue>{beatmap.id}</blue> "
|
||||
f"change <cyan>{change.type}</cyan>"
|
||||
)
|
||||
new_db_beatmap = await Beatmap.from_resp_no_save(session, beatmap)
|
||||
existing_beatmap = await session.get(Beatmap, change.beatmap_id)
|
||||
if existing_beatmap:
|
||||
|
||||
@@ -46,13 +46,13 @@ class DatabaseCleanupService:
|
||||
await db.commit()
|
||||
|
||||
if deleted_count > 0:
|
||||
logger.debug(f"[Cleanup Service] Cleaned up {deleted_count} expired email verification codes")
|
||||
logger.debug(f"Cleaned up {deleted_count} expired email verification codes")
|
||||
|
||||
return deleted_count
|
||||
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"[Cleanup Service] Error cleaning expired verification codes: {e!s}")
|
||||
logger.error(f"Error cleaning expired verification codes: {e!s}")
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
@@ -85,13 +85,13 @@ class DatabaseCleanupService:
|
||||
await db.commit()
|
||||
|
||||
if deleted_count > 0:
|
||||
logger.debug(f"[Cleanup Service] Cleaned up {deleted_count} expired login sessions")
|
||||
logger.debug(f"Cleaned up {deleted_count} expired login sessions")
|
||||
|
||||
return deleted_count
|
||||
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"[Cleanup Service] Error cleaning expired login sessions: {e!s}")
|
||||
logger.error(f"Error cleaning expired login sessions: {e!s}")
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
@@ -126,15 +126,13 @@ class DatabaseCleanupService:
|
||||
await db.commit()
|
||||
|
||||
if deleted_count > 0:
|
||||
logger.debug(
|
||||
f"[Cleanup Service] Cleaned up {deleted_count} used verification codes older than {days_old} days"
|
||||
)
|
||||
logger.debug(f"Cleaned up {deleted_count} used verification codes older than {days_old} days")
|
||||
|
||||
return deleted_count
|
||||
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"[Cleanup Service] Error cleaning old used verification codes: {e!s}")
|
||||
logger.error(f"Error cleaning old used verification codes: {e!s}")
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
@@ -169,16 +167,13 @@ class DatabaseCleanupService:
|
||||
await db.commit()
|
||||
|
||||
if deleted_count > 0:
|
||||
logger.debug(
|
||||
f"[Cleanup Service] Cleaned up {deleted_count} unverified "
|
||||
f"login sessions older than {hours_old} hour(s)"
|
||||
)
|
||||
logger.debug(f"Cleaned up {deleted_count} unverified login sessions older than {hours_old} hour(s)")
|
||||
|
||||
return deleted_count
|
||||
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"[Cleanup Service] Error cleaning unverified login sessions: {e!s}")
|
||||
logger.error(f"Error cleaning unverified login sessions: {e!s}")
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
@@ -206,13 +201,13 @@ class DatabaseCleanupService:
|
||||
await db.commit()
|
||||
|
||||
if deleted_count > 0:
|
||||
logger.debug(f"[Cleanup Service] Cleaned up {deleted_count} outdated verified sessions")
|
||||
logger.debug(f"Cleaned up {deleted_count} outdated verified sessions")
|
||||
|
||||
return deleted_count
|
||||
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"[Cleanup Service] Error cleaning outdated verified sessions: {e!s}")
|
||||
logger.error(f"Error cleaning outdated verified sessions: {e!s}")
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
@@ -243,13 +238,13 @@ class DatabaseCleanupService:
|
||||
await db.commit()
|
||||
|
||||
if deleted_count > 0:
|
||||
logger.debug(f"[Cleanup Service] Cleaned up {deleted_count} expired trusted devices")
|
||||
logger.debug(f"Cleaned up {deleted_count} expired trusted devices")
|
||||
|
||||
return deleted_count
|
||||
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"[Cleanup Service] Error cleaning expired trusted devices: {e!s}")
|
||||
logger.error(f"Error cleaning expired trusted devices: {e!s}")
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
@@ -278,13 +273,13 @@ class DatabaseCleanupService:
|
||||
await db.commit()
|
||||
|
||||
if deleted_count > 0:
|
||||
logger.debug(f"[Cleanup Service] Cleaned up {deleted_count} expired OAuth tokens")
|
||||
logger.debug(f"Cleaned up {deleted_count} expired OAuth tokens")
|
||||
|
||||
return deleted_count
|
||||
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"[Cleanup Service] Error cleaning expired OAuth tokens: {e!s}")
|
||||
logger.error(f"Error cleaning expired OAuth tokens: {e!s}")
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
@@ -323,9 +318,7 @@ class DatabaseCleanupService:
|
||||
|
||||
total_cleaned = sum(results.values())
|
||||
if total_cleaned > 0:
|
||||
logger.debug(
|
||||
f"[Cleanup Service] Full cleanup completed, total cleaned: {total_cleaned} records - {results}"
|
||||
)
|
||||
logger.debug(f"Full cleanup completed, total cleaned: {total_cleaned} records - {results}")
|
||||
|
||||
return results
|
||||
|
||||
@@ -421,7 +414,7 @@ class DatabaseCleanupService:
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[Cleanup Service] Error getting cleanup statistics: {e!s}")
|
||||
logger.error(f"Error getting cleanup statistics: {e!s}")
|
||||
return {
|
||||
"expired_verification_codes": 0,
|
||||
"expired_login_sessions": 0,
|
||||
|
||||
@@ -221,11 +221,6 @@ class EmailQueue:
|
||||
是否发送成功
|
||||
"""
|
||||
try:
|
||||
# 如果邮件发送功能被禁用,则只记录日志
|
||||
if not getattr(settings, "enable_email_sending", True):
|
||||
logger.info(f"[Mock Email] Would send to {email_data.get('to_email')}: {email_data.get('subject')}")
|
||||
return True
|
||||
|
||||
# 创建邮件
|
||||
msg = MIMEMultipart("alternative")
|
||||
msg["From"] = f"{self.from_name} <{self.from_email}>"
|
||||
|
||||
@@ -147,11 +147,11 @@ class EmailService:
|
||||
|
||||
server.send_message(msg)
|
||||
|
||||
logger.info(f"[Email Verification] Successfully sent verification code to {email}")
|
||||
logger.info(f"Successfully sent verification code to {email}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[Email Verification] Failed to send email: {e}")
|
||||
logger.error(f"Failed to send email: {e}")
|
||||
return False
|
||||
|
||||
|
||||
|
||||
@@ -105,13 +105,13 @@ class PasswordResetService:
|
||||
email_sent = await self.send_password_reset_email(email=email, code=reset_code, username=user.username)
|
||||
|
||||
if email_sent:
|
||||
logger.info(f"[Password Reset] Sent reset code to user {user.id} ({email})")
|
||||
logger.info(f"Sent reset code to user {user.id} ({email})")
|
||||
return True, "密码重置邮件已发送,请查收邮箱"
|
||||
else:
|
||||
# 邮件发送失败,清理Redis中的数据
|
||||
await redis.delete(reset_code_key)
|
||||
await redis.delete(rate_limit_key)
|
||||
logger.warning(f"[Password Reset] Email sending failed, cleaned up Redis data for {email}")
|
||||
logger.warning(f"Email sending failed, cleaned up Redis data for {email}")
|
||||
return False, "邮件发送失败,请稍后重试"
|
||||
|
||||
except Exception:
|
||||
@@ -121,7 +121,7 @@ class PasswordResetService:
|
||||
await redis.delete(rate_limit_key)
|
||||
except Exception:
|
||||
pass
|
||||
logger.exception("[Password Reset] Redis operation failed")
|
||||
logger.exception("Redis operation failed")
|
||||
return False, "服务暂时不可用,请稍后重试"
|
||||
|
||||
async def send_password_reset_email(self, email: str, code: str, username: str) -> bool:
|
||||
@@ -269,11 +269,11 @@ class PasswordResetService:
|
||||
metadata=metadata,
|
||||
)
|
||||
|
||||
logger.info(f"[Password Reset] Enqueued reset code email to {email}")
|
||||
logger.info(f"Enqueued reset code email to {email}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[Password Reset] Failed to enqueue email: {e}")
|
||||
logger.error(f"Failed to enqueue email: {e}")
|
||||
return False
|
||||
|
||||
async def reset_password(
|
||||
@@ -366,7 +366,7 @@ class PasswordResetService:
|
||||
await redis.setex(reset_code_key, 300, json.dumps(reset_data)) # 保留5分钟用于日志记录
|
||||
|
||||
logger.info(
|
||||
f"[Password Reset] User {user_id} ({email}) successfully reset password from IP {ip_address},"
|
||||
f"User {user_id} ({email}) successfully reset password from IP {ip_address},"
|
||||
f" invalidated {tokens_deleted} tokens"
|
||||
)
|
||||
return True, "密码重置成功,所有设备已被登出"
|
||||
@@ -374,7 +374,7 @@ class PasswordResetService:
|
||||
except Exception as e:
|
||||
# 不要在异常处理中访问user.id,可能触发数据库操作
|
||||
user_id = reset_data.get("user_id", "未知")
|
||||
logger.error(f"[Password Reset] Failed to reset password for user {user_id}: {e}")
|
||||
logger.error(f"Failed to reset password for user {user_id}: {e}")
|
||||
await session.rollback()
|
||||
|
||||
# 数据库回滚时,需要恢复Redis中的验证码状态
|
||||
@@ -401,14 +401,14 @@ class PasswordResetService:
|
||||
remaining_ttl,
|
||||
json.dumps(original_reset_data),
|
||||
)
|
||||
logger.info(f"[Password Reset] Restored Redis state after database rollback for {email}")
|
||||
logger.info(f"Restored Redis state after database rollback for {email}")
|
||||
else:
|
||||
# 如果已经过期,直接删除
|
||||
await redis.delete(reset_code_key)
|
||||
logger.info(f"[Password Reset] Removed expired reset code after database rollback for {email}")
|
||||
logger.info(f"Removed expired reset code after database rollback for {email}")
|
||||
|
||||
except Exception as redis_error:
|
||||
logger.error(f"[Password Reset] Failed to restore Redis state after rollback: {redis_error}")
|
||||
logger.error(f"Failed to restore Redis state after rollback: {redis_error}")
|
||||
|
||||
return False, "密码重置失败,请稍后重试"
|
||||
|
||||
@@ -428,7 +428,7 @@ class PasswordResetService:
|
||||
ttl = await redis.ttl(rate_limit_key)
|
||||
return 1 if ttl > 0 else 0
|
||||
except Exception as e:
|
||||
logger.error(f"[Password Reset] Failed to get attempts count: {e}")
|
||||
logger.error(f"Failed to get attempts count: {e}")
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from app.log import logger
|
||||
from app.log import log
|
||||
from app.models.notification import NotificationDetails
|
||||
|
||||
from .base import RedisSubscriber
|
||||
@@ -17,6 +17,8 @@ JOIN_CHANNEL = "chat:room:joined"
|
||||
EXIT_CHANNEL = "chat:room:left"
|
||||
ON_NOTIFICATION = "chat:notification"
|
||||
|
||||
logger = log("Chat")
|
||||
|
||||
|
||||
class ChatSubscriber(RedisSubscriber):
|
||||
def __init__(self):
|
||||
@@ -49,7 +51,7 @@ class ChatSubscriber(RedisSubscriber):
|
||||
try:
|
||||
detail = TypeAdapter(NotificationDetails).validate_json(s)
|
||||
except ValueError:
|
||||
logger.exception("")
|
||||
logger.exception("Failed to parse notification detail")
|
||||
return
|
||||
except Exception:
|
||||
logger.exception("Failed to parse notification detail")
|
||||
|
||||
@@ -180,7 +180,7 @@ This email was sent automatically, please do not reply.
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[Email Verification] Failed to enqueue email: {e}")
|
||||
logger.error(f"Failed to enqueue email: {e}")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
@@ -237,7 +237,7 @@ This email was sent automatically, please do not reply.
|
||||
str(verification.id) if verification.id else "0",
|
||||
)
|
||||
|
||||
logger.info(f"[Email Verification] Created verification code for user {user_id}: {code}")
|
||||
logger.info(f"Created verification code for user {user_id}: {code}")
|
||||
return verification, code
|
||||
|
||||
@staticmethod
|
||||
@@ -254,11 +254,11 @@ This email was sent automatically, please do not reply.
|
||||
try:
|
||||
# 检查是否启用邮件验证功能
|
||||
if not settings.enable_email_verification:
|
||||
logger.debug(f"[Email Verification] Email verification is disabled, skipping for user {user_id}")
|
||||
logger.debug(f"Email verification is disabled, skipping for user {user_id}")
|
||||
return True # 返回成功,但不执行验证流程
|
||||
|
||||
# 检测客户端信息
|
||||
logger.info(f"[Email Verification] Detected client for user {user_id}: {user_agent}")
|
||||
logger.info(f"Detected client for user {user_id}: {user_agent}")
|
||||
|
||||
# 创建验证记录
|
||||
(
|
||||
@@ -272,16 +272,14 @@ This email was sent automatically, please do not reply.
|
||||
success = await EmailVerificationService.send_verification_email_via_queue(email, code, username, user_id)
|
||||
|
||||
if success:
|
||||
logger.info(
|
||||
f"[Email Verification] Successfully enqueued verification email to {email} (user: {username})"
|
||||
)
|
||||
logger.info(f"Successfully enqueued verification email to {email} (user: {username})")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"[Email Verification] Failed to enqueue verification email: {email} (user: {username})")
|
||||
logger.error(f"Failed to enqueue verification email: {email} (user: {username})")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[Email Verification] Exception during sending verification email: {e}")
|
||||
logger.error(f"Exception during sending verification email: {e}")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
@@ -299,7 +297,7 @@ This email was sent automatically, please do not reply.
|
||||
try:
|
||||
# 检查是否启用邮件验证功能
|
||||
if not settings.enable_email_verification:
|
||||
logger.debug(f"[Email Verification] Email verification is disabled, auto-approving for user {user_id}")
|
||||
logger.debug(f"Email verification is disabled, auto-approving for user {user_id}")
|
||||
return True, "验证成功(邮件验证功能已禁用)"
|
||||
|
||||
# 先从 Redis 检查
|
||||
@@ -331,11 +329,11 @@ This email was sent automatically, please do not reply.
|
||||
# 删除 Redis 记录
|
||||
await redis.delete(f"email_verification:{user_id}:{code}")
|
||||
|
||||
logger.info(f"[Email Verification] User {user_id} verification code verified successfully")
|
||||
logger.info(f"User {user_id} verification code verified successfully")
|
||||
return True, "验证成功"
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[Email Verification] Exception during verification code validation: {e}")
|
||||
logger.error(f"Exception during verification code validation: {e}")
|
||||
return False, "验证过程中发生错误"
|
||||
|
||||
@staticmethod
|
||||
@@ -354,7 +352,7 @@ This email was sent automatically, please do not reply.
|
||||
_ = user_agent
|
||||
# 检查是否启用邮件验证功能
|
||||
if not settings.enable_email_verification:
|
||||
logger.debug(f"[Email Verification] Email verification is disabled, skipping resend for user {user_id}")
|
||||
logger.debug(f"Email verification is disabled, skipping resend for user {user_id}")
|
||||
return True, "验证码已发送(邮件验证功能已禁用)"
|
||||
|
||||
# 检查重发频率限制(60秒内只能发送一次)
|
||||
@@ -376,7 +374,7 @@ This email was sent automatically, please do not reply.
|
||||
return False, "重新发送失败,请稍后再试"
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[Email Verification] Exception during resending verification code: {e}")
|
||||
logger.error(f"Exception during resending verification code: {e}")
|
||||
return False, "重新发送过程中发生错误"
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user