master
jinql 2025-09-03 20:07:41 +08:00
parent a5290c5e03
commit 452e110ee5
15 changed files with 156 additions and 47 deletions

19
.dockerignore Normal file
View File

@ -0,0 +1,19 @@
# 忽略所有隐藏文件
.*
# 忽略构建产物
dist/
*.egg-info/
# 忽略本地依赖
node_modules/
venv/
.pipenv/
# 忽略临时文件
*.tmp
*.log
__pycache__/
# 忽略指定目录
data/

2
.gitignore vendored
View File

@ -165,4 +165,4 @@ cython_debug/
!/.vscode/
.vscode/
icon/*
md5/*
data/*

View File

@ -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"]

49
conf/gunicorn.conf.py Normal file
View File

@ -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

60
conf/logging.yaml Normal file
View File

@ -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

8
entrypoint.sh Normal file
View File

@ -0,0 +1,8 @@
#!/usr/bin/env sh
set -e
# 首次启动时把镜像里的默认配置拷到挂载点
[ -z "$(ls -A /app/conf)" ] && cp -r /app/conf.default/* /app/conf/
exec "$@"

View File

@ -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()

View File

@ -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'

View File

@ -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)

View File

@ -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}")

View File

@ -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

View File

@ -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__))

View File

@ -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

View File

@ -1,3 +1,3 @@
#!/usr/bin/env sh
gunicorn main:app -c gunicorn.conf.py
gunicorn -c conf/gunicorn.conf.py main:app