From 452e110ee5188b60a01d40c821577953a130060e Mon Sep 17 00:00:00 2001 From: jinql Date: Wed, 3 Sep 2025 20:07:41 +0800 Subject: [PATCH] 25.09.03 --- .dockerignore | 19 +++++++++ .gitignore | 2 +- Dockerfile | 6 ++- conf/gunicorn.conf.py | 49 ++++++++++++++++++++++ conf/logging.yaml | 60 +++++++++++++++++++++++++++ referrer.txt => conf/referrer.txt | 0 entrypoint.sh | 8 ++++ favicon_app/models/favicon.py | 2 +- favicon_app/routes/favicon_routes.py | 4 +- favicon_app/routes/favicon_service.py | 11 ++--- favicon_app/utils/file_util.py | 4 +- gunicorn.conf.py | 31 -------------- main.py | 4 +- requirements.txt | 1 + startup.sh | 2 +- 15 files changed, 156 insertions(+), 47 deletions(-) create mode 100644 .dockerignore create mode 100644 conf/gunicorn.conf.py create mode 100644 conf/logging.yaml rename referrer.txt => conf/referrer.txt (100%) create mode 100644 entrypoint.sh delete mode 100644 gunicorn.conf.py diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..ec8daad --- /dev/null +++ b/.dockerignore @@ -0,0 +1,19 @@ +# 忽略所有隐藏文件 +.* + +# 忽略构建产物 +dist/ +*.egg-info/ + +# 忽略本地依赖 +node_modules/ +venv/ +.pipenv/ + +# 忽略临时文件 +*.tmp +*.log +__pycache__/ + +# 忽略指定目录 +data/ diff --git a/.gitignore b/.gitignore index 4669a72..cf68290 100644 --- a/.gitignore +++ b/.gitignore @@ -165,4 +165,4 @@ cython_debug/ !/.vscode/ .vscode/ icon/* -md5/* +data/* diff --git a/Dockerfile b/Dockerfile index bc4e561..1bacfd1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,5 +29,9 @@ RUN find . -name "*.py" -delete EXPOSE 8000 +VOLUME ["/app/data", "/app/conf", "/app/logs"] + +ENTRYPOINT ["entrypoint.sh"] + # CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] -CMD ["gunicorn", "--config", "gunicorn.conf.pyc", "main:app"] +CMD ["gunicorn", "-c", "conf/gunicorn.conf.pyc", "main:app"] diff --git a/conf/gunicorn.conf.py b/conf/gunicorn.conf.py new file mode 100644 index 0000000..fa6d261 --- /dev/null +++ b/conf/gunicorn.conf.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +from pathlib import Path + +import yaml + +# 绑定地址和端口 +bind = "0.0.0.0:8000" + +# Worker 进程数(推荐 CPU 核心数 * 2 + 1) +workers = 1 + +# 工作模式(sync、gevent、uvicorn.workers.UvicornWorker) +worker_class = "uvicorn.workers.UvicornWorker" + +# 日志目录 +log_dir = Path("logs") +log_dir.mkdir(exist_ok=True) + +# 日志配置 +with open(Path(__file__).with_name("logging.yaml"), "r", encoding="utf-8") as f: + logconfig_dict = yaml.safe_load(f) + +# 日志级别(debug、info、warning、error、critical);以 YAML 配置优先 +loglevel = "info" + +# 访问日志文件("-" 表示输出到 stdout);以 YAML 配置优先 +accesslog = "logs/access.log" + +# 错误日志文件;以 YAML 配置优先 +errorlog = "-" + +# access_log_format 仅在 同步 worker 下有效,UvicornWorker下不可用;以 YAML 配置优先 +# access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s' +raw_env = [ + "UVICORN_ACCESS_LOGFORMAT=%(h)s %(l)s %(u)s %(t)s \"%(r)s\" %(s)s %(b)s \"%(f)s\" \"%(a)s\" %(D)s" +] + +# 可选:超时时间(秒) +timeout = 120 + +# Keep - Alive超时 +keepalive = 5 + +# 进程名(ps aux 中显示) +# proc_name = "gunicorn" + +# 守护进程运行(后台运行,默认 False) +# daemon = True diff --git a/conf/logging.yaml b/conf/logging.yaml new file mode 100644 index 0000000..28dc01f --- /dev/null +++ b/conf/logging.yaml @@ -0,0 +1,60 @@ +version: 1 +disable_existing_loggers: false +formatters: + default: + format: "[%(levelname)-7s] %(asctime)s [%(process)d] -[%(name)s:%(lineno)d] %(message)s" + datefmt: "%Y-%m-%d %H:%M:%S" + +handlers: + console: + class: logging.StreamHandler + level: INFO + formatter: default + stream: ext://sys.stdout + file_info: + class: logging.handlers.TimedRotatingFileHandler + level: INFO + formatter: default + filename: logs/info.log + when: midnight + interval: 1 + backupCount: 7 + encoding: utf8 + delay: true + file_error: + class: logging.handlers.TimedRotatingFileHandler + level: ERROR + formatter: default + filename: logs/error.log + when: midnight + interval: 1 + backupCount: 7 + encoding: utf8 + delay: true + +loggers: + uvicorn: + level: INFO + handlers: + - console + - file_info + propagate: false + uvicorn.error: + level: INFO + handlers: + - console + - file_error + propagate: false + uvicorn.access: + level: INFO + handlers: + - console + - file_info + propagate: false + +root: + level: INFO + handlers: + - console + - file_info + - file_error diff --git a/referrer.txt b/conf/referrer.txt similarity index 100% rename from referrer.txt rename to conf/referrer.txt diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..70224e6 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env sh + +set -e + +# 首次启动时把镜像里的默认配置拷到挂载点 +[ -z "$(ls -A /app/conf)" ] && cp -r /app/conf.default/* /app/conf/ + +exec "$@" diff --git a/favicon_app/models/favicon.py b/favicon_app/models/favicon.py index f0bfee4..9bca69a 100644 --- a/favicon_app/models/favicon.py +++ b/favicon_app/models/favicon.py @@ -20,7 +20,7 @@ from favicon_app.utils.filetype import helpers, filetype urllib3.disable_warnings() logging.captureWarnings(True) # 配置日志 -logger = logging.getLogger() +logger = logging.getLogger(__name__) # 创建requests会话池 requests_session = requests.Session() diff --git a/favicon_app/routes/favicon_routes.py b/favicon_app/routes/favicon_routes.py index b0c3fb5..da750d5 100644 --- a/favicon_app/routes/favicon_routes.py +++ b/favicon_app/routes/favicon_routes.py @@ -13,7 +13,7 @@ from favicon_app.utils.file_util import FileUtil urllib3.disable_warnings() logging.captureWarnings(True) -logger = logging.getLogger() +logger = logging.getLogger(__name__) _icon_root_path = favicon_service.icon_root_path _default_icon_path = favicon_service.default_icon_path @@ -58,7 +58,7 @@ async def get_count(): async def get_referrer(): """获取请求来源信息""" content = 'None' - path = os.path.join(_icon_root_path, 'referrer.txt') + path = os.path.join(_icon_root_path, 'conf', 'referrer.txt') if os.path.exists(path): try: content = FileUtil.read_file(path, mode='r') or 'None' diff --git a/favicon_app/routes/favicon_service.py b/favicon_app/routes/favicon_service.py index 22b95d6..2ba5dab 100644 --- a/favicon_app/routes/favicon_service.py +++ b/favicon_app/routes/favicon_service.py @@ -23,7 +23,7 @@ from favicon_app.utils.filetype import helpers, filetype urllib3.disable_warnings() logging.captureWarnings(True) -logger = logging.getLogger() +logger = logging.getLogger(__name__) # 获取当前所在目录的绝对路径 current_dir = os.path.dirname(os.path.abspath(__file__)) @@ -127,7 +127,7 @@ class FaviconService: def _get_cache_file(self, domain: str, refresh: bool = False) -> Tuple[Optional[bytes], Optional[bytes]]: """从缓存中获取图标文件""" - cache_path = os.path.join(icon_root_path, 'icon', domain + '.png') + cache_path = os.path.join(icon_root_path, 'data/icon', domain + '.png') if os.path.exists(cache_path) and os.path.isfile(cache_path) and os.path.getsize(cache_path) > 0: try: cached_icon = FileUtil.read_file(cache_path, mode='rb') @@ -269,7 +269,7 @@ class FaviconService: if _referrer: logger.debug(f"-> Referrer: {_referrer}") - _path = os.path.join(icon_root_path, 'referrer.txt') + _path = os.path.join(icon_root_path, 'conf', 'referrer.txt') with self._lock: # 首次加载现有referrer数据 @@ -336,8 +336,8 @@ class FaviconService: icon_content = _cached if _cached else default_icon_content if icon_content: - cache_path = os.path.join(icon_root_path, 'icon', entity.domain_md5 + '.png') - md5_path = os.path.join(icon_root_path, 'md5', entity.domain_md5 + '.txt') + cache_path = os.path.join(icon_root_path, 'data/icon', entity.domain_md5 + '.png') + md5_path = os.path.join(icon_root_path, 'data/text', entity.domain_md5 + '.txt') try: # 确保目录存在 @@ -391,6 +391,7 @@ class FaviconService: if not url: return {"message": "请提供url参数"} + logger.info('##########################################################') try: entity = Favicon(url) diff --git a/favicon_app/utils/file_util.py b/favicon_app/utils/file_util.py index c0df3b6..becfd79 100644 --- a/favicon_app/utils/file_util.py +++ b/favicon_app/utils/file_util.py @@ -10,7 +10,7 @@ import urllib3 # 配置日志 urllib3.disable_warnings() logging.captureWarnings(True) -logger = logging.getLogger() +logger = logging.getLogger(__name__) class FileUtil: @@ -244,7 +244,7 @@ class FileUtil: with open(file_path, mode, encoding=encoding) as f: f.write(content) - logger.info(f"文件写入成功: {file_path}") + # logger.info(f"文件写入成功: {file_path}") return True except PermissionError: logger.error(f"没有权限写入文件: {file_path}") diff --git a/gunicorn.conf.py b/gunicorn.conf.py deleted file mode 100644 index b795208..0000000 --- a/gunicorn.conf.py +++ /dev/null @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- - -# 绑定地址和端口 -bind = "0.0.0.0:8000" - -# Worker 进程数(推荐 CPU 核心数 * 2 + 1) -workers = 4 - -# 工作模式(sync、gevent、uvicorn.workers.UvicornWorker) -worker_class = "uvicorn.workers.UvicornWorker" - -# 日志级别(debug、info、warning、error、critical) -loglevel = "info" - -# 访问日志文件("-" 表示输出到 stdout) -accesslog = "-" - -# 错误日志文件 -errorlog = "-" - -# 可选:超时时间(秒) -timeout = 120 - -# Keep - Alive超时 -keepalive = 5 - -# 进程名(ps aux 中显示) -# proc_name = "gunicorn" - -# 守护进程运行(后台运行,默认 False) -# daemon = True diff --git a/main.py b/main.py index 7691f3f..864edb5 100644 --- a/main.py +++ b/main.py @@ -10,9 +10,7 @@ from fastapi.responses import Response from favicon_app.routes import favicon_router from favicon_app.utils.file_util import FileUtil -logging.basicConfig(level=logging.INFO, - format='[%(levelname)-7s] %(asctime)s -[%(filename)-10.10s:%(lineno)4d] %(message)s', - filename='favicon-app.log') +logger = logging.getLogger(__name__) # 获取当前所在目录 current_dir = os.path.dirname(os.path.abspath(__file__)) diff --git a/requirements.txt b/requirements.txt index e18e1f3..8b29293 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,5 +6,6 @@ requests~=2.32.5 bs4~=0.0.2 beautifulsoup4~=4.13.5 lxml~=6.0.1 +PyYAML~=6.0.2 uvicorn~=0.35.0 gunicorn~=23.0.0 diff --git a/startup.sh b/startup.sh index a435aea..bb528d3 100644 --- a/startup.sh +++ b/startup.sh @@ -1,3 +1,3 @@ #!/usr/bin/env sh -gunicorn main:app -c gunicorn.conf.py +gunicorn -c conf/gunicorn.conf.py main:app