[Enhance][Refactor] unranked score & warning log

- Add an option that can be used to forbid some illegal scores
- Add brief warning logs for custom exceptions
This commit is contained in:
Lost-MSth
2022-11-28 21:58:06 +08:00
parent a7a9a4ba3d
commit 426f65ea9e
9 changed files with 84 additions and 66 deletions

View File

@@ -50,6 +50,8 @@ class Config:
ALLOW_LOGIN_SAME_DEVICE = False ALLOW_LOGIN_SAME_DEVICE = False
ALLOW_BAN_MULTIDEVICE_USER_AUTO = True ALLOW_BAN_MULTIDEVICE_USER_AUTO = True
ALLOW_SCORE_WITH_NO_SONG = True
ALLOW_INFO_LOG = False ALLOW_INFO_LOG = False
ALLOW_WARNING_LOG = False ALLOW_WARNING_LOG = False

View File

@@ -20,15 +20,6 @@ def get_song_file_md5(song_id: str, file_name: str) -> str:
return get_file_md5(path) return get_file_md5(path)
def initialize_songfile():
'''初始化歌曲数据的md5信息'''
get_song_file_md5.cache_clear()
x = DownloadList()
x.url_flag = False
x.add_songs()
del x
class SonglistParser: class SonglistParser:
'''songlist文件解析器''' '''songlist文件解析器'''
@@ -189,13 +180,22 @@ class DownloadList(UserDownload):
self.downloads: list = [] self.downloads: list = []
self.urls: dict = {} self.urls: dict = {}
@classmethod
def initialize_cache(cls) -> None:
'''初始化歌曲数据缓存包括md5、文件目录遍历、解析songlist'''
SonglistParser()
x = cls()
x.url_flag = False
x.add_songs()
del x
@staticmethod @staticmethod
def clear_all_cache(): def clear_all_cache() -> None:
'''清除所有歌曲文件有关缓存''' '''清除所有歌曲文件有关缓存'''
get_song_file_md5.cache_clear() get_song_file_md5.cache_clear()
DownloadList.get_one_song_file_names.cache_clear() DownloadList.get_one_song_file_names.cache_clear()
DownloadList.get_all_song_ids.cache_clear() DownloadList.get_all_song_ids.cache_clear()
SonglistParser() SonglistParser.songs = {}
def clear_download_token(self) -> None: def clear_download_token(self) -> None:
'''清除过期下载链接''' '''清除过期下载链接'''

View File

@@ -4,11 +4,12 @@ from importlib import import_module
from json import load from json import load
from shutil import copy, copy2 from shutil import copy, copy2
from time import time from time import time
from traceback import format_exc
from core.config_manager import Config from core.config_manager import Config
from core.constant import ARCAEA_SERVER_VERSION from core.constant import ARCAEA_SERVER_VERSION
from core.course import Course from core.course import Course
from core.download import SonglistParser from core.download import DownloadList
from core.purchase import Purchase from core.purchase import Purchase
from core.sql import Connect, DatabaseMigrator, MemoryDatabase from core.sql import Connect, DatabaseMigrator, MemoryDatabase
from core.user import UserRegister from core.user import UserRegister
@@ -181,6 +182,7 @@ class LogDatabaseInit:
class FileChecker: class FileChecker:
'''文件检查及初始化类'''
def __init__(self, logger=None): def __init__(self, logger=None):
self.logger = logger self.logger = logger
@@ -206,7 +208,8 @@ class FileChecker:
LogDatabaseInit().init() LogDatabaseInit().init()
self.logger.info( self.logger.info(
f'Success to new the file {Config.SQLITE_LOG_DATABASE_PATH}') f'Success to new the file {Config.SQLITE_LOG_DATABASE_PATH}')
except: except Exception as e:
self.logger.error(format_exc())
self.logger.error( self.logger.error(
f'Failed to new the file {Config.SQLITE_LOG_DATABASE_PATH}') f'Failed to new the file {Config.SQLITE_LOG_DATABASE_PATH}')
return False return False
@@ -218,7 +221,8 @@ class FileChecker:
DatabaseInit().init() DatabaseInit().init()
self.logger.info( self.logger.info(
'Success to new the file `%s`.' % Config.SQLITE_DATABASE_PATH) 'Success to new the file `%s`.' % Config.SQLITE_DATABASE_PATH)
except: except Exception as e:
self.logger.error(format_exc())
self.logger.warning( self.logger.warning(
'Failed to new the file `%s`.' % Config.SQLITE_DATABASE_PATH) 'Failed to new the file `%s`.' % Config.SQLITE_DATABASE_PATH)
return False return False
@@ -262,7 +266,8 @@ class FileChecker:
self.logger.info( self.logger.info(
'Success to update the file `%s`.' % Config.SQLITE_DATABASE_PATH) 'Success to update the file `%s`.' % Config.SQLITE_DATABASE_PATH)
except ValueError: except Exception as e:
self.logger.error(format_exc())
self.logger.warning( self.logger.warning(
'Fail to update the file `%s`.' % Config.SQLITE_DATABASE_PATH) 'Fail to update the file `%s`.' % Config.SQLITE_DATABASE_PATH)
@@ -275,9 +280,20 @@ class FileChecker:
DatabaseMigrator(old_path, new_path).update_database() DatabaseMigrator(old_path, new_path).update_database()
os.remove(old_path) os.remove(old_path)
def check_song_file(self) -> bool:
'''检查song有关文件并初始化缓存'''
f = self.check_folder(Config.SONG_FILE_FOLDER_PATH)
self.logger.info("Start to initialize song data...")
try:
DownloadList.initialize_cache()
self.logger.info('Complete!')
except Exception as e:
self.logger.error(format_exc())
self.logger.warning('Initialization error!')
f = False
return f
def check_before_run(self) -> bool: def check_before_run(self) -> bool:
'''运行前检查,返回布尔值''' '''运行前检查,返回布尔值'''
# TODO: try
MemoryDatabase() # 初始化内存数据库 MemoryDatabase() # 初始化内存数据库
SonglistParser() # 解析songlist return self.check_song_file() & self.check_update_database()
return self.check_folder(Config.SONG_FILE_FOLDER_PATH) & self.check_update_database()

View File

@@ -1,5 +1,6 @@
from .sql import Connect, Sql from .sql import Connect, Sql
from .score import Score from .score import Score
from .download import DownloadList
class BaseOperation: class BaseOperation:
@@ -53,3 +54,14 @@ class RefreshAllScoreRating(BaseOperation):
if values: if values:
Sql(c).update_many('best_score', ['rating'], values, [ Sql(c).update_many('best_score', ['rating'], values, [
'user_id', 'song_id', 'difficulty'], where_values) 'user_id', 'song_id', 'difficulty'], where_values)
class RefreshSongFileCache(BaseOperation):
'''
刷新歌曲文件缓存包括文件hash缓存重建、文件目录重遍历、songlist重解析
'''
name = 'refresh_song_file_cache'
def run(self):
DownloadList.clear_all_cache()
DownloadList.initialize_cache()

View File

@@ -1,4 +1,5 @@
from .error import NoData from .error import NoData
from .config_manager import Config
class Chart: class Chart:
@@ -33,8 +34,10 @@ class Chart:
'''select rating_pst, rating_prs, rating_ftr, rating_byn from chart where song_id=:a''', {'a': self.song_id}) '''select rating_pst, rating_prs, rating_ftr, rating_byn from chart where song_id=:a''', {'a': self.song_id})
x = self.c.fetchone() x = self.c.fetchone()
if x is None: if x is None:
self.defnum = -10 if Config.ALLOW_SCORE_WITH_NO_SONG:
# raise NoData('The song `%s` does not exist.' % self.song_id) self.defnum = -10
else:
raise NoData(f'The song `{self.song_id}` does not exist.', 120)
else: else:
self.defnum = x[self.difficulty] self.defnum = x[self.difficulty]

View File

@@ -27,7 +27,7 @@ import server
import web.index import web.index
import web.login import web.login
from core.constant import Constant from core.constant import Constant
from core.download import (UserDownload, initialize_songfile) from core.download import UserDownload
from core.error import ArcError, NoAccess, RateLimit from core.error import ArcError, NoAccess, RateLimit
from core.init import FileChecker from core.init import FileChecker
from core.sql import Connect from core.sql import Connect
@@ -128,6 +128,18 @@ def tcp_server_run():
app.run(Config.HOST, Config.PORT) app.run(Config.HOST, Config.PORT)
def generate_log_file_dict(level: str, filename: str) -> dict:
return {
"class": "logging.handlers.RotatingFileHandler",
"maxBytes": 1024 * 1024,
"backupCount": 1,
"encoding": "utf-8",
"level": level,
"formatter": "default",
"filename": filename
}
def main(): def main():
log_dict = { log_dict = {
'version': 1, 'version': 1,
@@ -141,15 +153,7 @@ def main():
'stream': 'ext://flask.logging.wsgi_errors_stream', 'stream': 'ext://flask.logging.wsgi_errors_stream',
'formatter': 'default' 'formatter': 'default'
}, },
"error_file": { "error_file": generate_log_file_dict('ERROR', './log/error.log')
"class": "logging.handlers.RotatingFileHandler",
"maxBytes": 1024 * 1024,
"backupCount": 1,
"encoding": "utf-8",
"level": "ERROR",
"formatter": "default",
"filename": "./log/error.log"
}
}, },
'formatters': { 'formatters': {
'default': { 'default': {
@@ -159,51 +163,30 @@ def main():
} }
if Config.ALLOW_INFO_LOG: if Config.ALLOW_INFO_LOG:
log_dict['root']['handlers'].append('info_file') log_dict['root']['handlers'].append('info_file')
log_dict['handlers']['info_file'] = { log_dict['handlers']['info_file'] = generate_log_file_dict(
"class": "logging.handlers.RotatingFileHandler", 'INFO', './log/info.log')
"maxBytes": 1024 * 1024,
"backupCount": 1,
"encoding": "utf-8",
"level": "INFO",
"formatter": "default",
"filename": "./log/info.log"
}
if Config.ALLOW_WARNING_LOG: if Config.ALLOW_WARNING_LOG:
log_dict['root']['handlers'].append('warning_file') log_dict['root']['handlers'].append('warning_file')
log_dict['handlers']['warning_file'] = { log_dict['handlers']['warning_file'] = generate_log_file_dict(
"class": "logging.handlers.RotatingFileHandler", 'WARNING', './log/warning.log')
"maxBytes": 1024 * 1024,
"backupCount": 1,
"encoding": "utf-8",
"level": "WARNING",
"formatter": "default",
"filename": "./log/warning.log"
}
dictConfig(log_dict) dictConfig(log_dict)
Connect.logger = app.logger Connect.logger = app.logger
if not FileChecker(app.logger).check_before_run(): if not FileChecker(app.logger).check_before_run():
app.logger.error('Something wrong. The server will not run.') app.logger.error('Some errors occurred. The server will not run.')
input('Press ENTER key to exit.') input('Press ENTER key to exit.')
sys.exit() sys.exit()
app.logger.info("Start to initialize song data...")
try:
initialize_songfile()
app.logger.info('Complete!')
except:
app.logger.warning('Initialization error!')
if Config.LINKPLAY_HOST and Config.SET_LINKPLAY_SERVER_AS_SUB_PROCESS: if Config.LINKPLAY_HOST and Config.SET_LINKPLAY_SERVER_AS_SUB_PROCESS:
from linkplay_server import link_play from linkplay_server import link_play
process = [Process(target=link_play, args=( process = [Process(target=link_play, args=(
Config.LINKPLAY_HOST, int(Config.LINKPLAY_UDP_PORT), int(Config.LINKPLAY_TCP_PORT)))] Config.LINKPLAY_HOST, int(Config.LINKPLAY_UDP_PORT), int(Config.LINKPLAY_TCP_PORT)))]
[p.start() for p in process] [p.start() for p in process]
app.logger.info("Link Play UDP server is running on " + app.logger.info(
Config.LINKPLAY_HOST + ':' + str(Config.LINKPLAY_UDP_PORT) + " ...") f"Link Play UDP server is running on {Config.LINKPLAY_HOST}:{Config.LINKPLAY_UDP_PORT} ...")
app.logger.info("Link Play TCP server is running on " + app.logger.info(
Config.LINKPLAY_HOST + ':' + str(Config.LINKPLAY_TCP_PORT) + " ...") f"Link Play TCP server is running on {Config.LINKPLAY_HOST}:{Config.LINKPLAY_TCP_PORT} ...")
tcp_server_run() tcp_server_run()
[p.join() for p in process] [p.join() for p in process]
else: else:

View File

@@ -5,7 +5,7 @@ from core.config_manager import Config
from core.error import ArcError, NoAccess from core.error import ArcError, NoAccess
from core.sql import Connect from core.sql import Connect
from core.user import UserAuth, UserLogin from core.user import UserAuth, UserLogin
from flask import Blueprint, jsonify, request from flask import Blueprint, g, jsonify, request
from .func import arc_try, error_return from .func import arc_try, error_return
@@ -55,6 +55,7 @@ def auth_required(request):
user = UserAuth(c) user = UserAuth(c)
user.token = headers['Authorization'][7:] user.token = headers['Authorization'][7:]
user_id = user.token_get_id() user_id = user.token_get_id()
g.user = user
except ArcError as e: except ArcError as e:
return error_return(e) return error_return(e)
return view(user_id, *args, **kwargs) return view(user_id, *args, **kwargs)

View File

@@ -3,7 +3,7 @@ from traceback import format_exc
from core.config_manager import Config from core.config_manager import Config
from core.error import ArcError from core.error import ArcError
from flask import current_app, jsonify from flask import current_app, g, jsonify
default_error = ArcError('Unknown Error', status=500) default_error = ArcError('Unknown Error', status=500)
@@ -83,6 +83,9 @@ def arc_try(view):
except ArcError as e: except ArcError as e:
if Config.ALLOW_WARNING_LOG: if Config.ALLOW_WARNING_LOG:
current_app.logger.warning(format_exc()) current_app.logger.warning(format_exc())
user = g.get("user", None)
current_app.logger.warning(
f'{user.user_id if user is not None else ""} - {e.error_code}|{e.api_error_code}: {e}')
return error_return(e) return error_return(e)
return wrapped_view return wrapped_view

View File

@@ -1,9 +1,8 @@
import os import os
import time import time
from core.download import DownloadList, initialize_songfile
from core.init import FileChecker from core.init import FileChecker
from core.operation import RefreshAllScoreRating from core.operation import RefreshAllScoreRating, RefreshSongFileCache
from core.rank import RankList from core.rank import RankList
from core.sql import Connect from core.sql import Connect
from flask import Blueprint, flash, redirect, render_template, request, url_for from flask import Blueprint, flash, redirect, render_template, request, url_for
@@ -290,8 +289,7 @@ def update_database():
def update_song_hash(): def update_song_hash():
# 更新数据库内谱面文件hash值 # 更新数据库内谱面文件hash值
try: try:
DownloadList.clear_all_cache() RefreshSongFileCache().run()
initialize_songfile()
flash('数据刷新成功 Success refresh data.') flash('数据刷新成功 Success refresh data.')
except: except:
flash('Something error!') flash('Something error!')