deepdata: core product code (backend, frontend, runtime, deepflow module)
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from pathlib import Path
|
||||
|
||||
from app.core.config import settings
|
||||
from app.core.logging import setup_logging
|
||||
|
||||
|
||||
def _remove_deepdata_handlers(logger: logging.Logger) -> None:
|
||||
for handler in list(logger.handlers):
|
||||
if getattr(handler, "_deepdata_managed", False):
|
||||
logger.removeHandler(handler)
|
||||
handler.close()
|
||||
|
||||
|
||||
def _repo_root() -> Path:
|
||||
for parent in Path(__file__).resolve().parents:
|
||||
if (parent / "scripts/images/common/log-rotate-copytruncate.sh").exists():
|
||||
return parent
|
||||
raise RuntimeError("repo root not found")
|
||||
|
||||
|
||||
def test_setup_logging_uses_rotating_file_handler_without_duplicates(
|
||||
tmp_path: Path,
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
monkeypatch.setattr(settings, "log_file_dir", str(tmp_path))
|
||||
monkeypatch.setattr(settings, "log_rotation_max_size", "1MB")
|
||||
monkeypatch.setattr(settings, "log_rotation_backup_count", 7)
|
||||
|
||||
root_logger = logging.getLogger()
|
||||
try:
|
||||
setup_logging()
|
||||
setup_logging()
|
||||
|
||||
file_handlers = [
|
||||
handler
|
||||
for handler in root_logger.handlers
|
||||
if getattr(handler, "_deepdata_managed", False)
|
||||
and isinstance(handler, RotatingFileHandler)
|
||||
]
|
||||
assert len(file_handlers) == 1
|
||||
assert file_handlers[0].maxBytes == 1024 * 1024
|
||||
assert file_handlers[0].backupCount == 7
|
||||
assert file_handlers[0].namer(str(tmp_path / "python-backend.log.1")).endswith(
|
||||
"python-backend.1.log"
|
||||
)
|
||||
finally:
|
||||
_remove_deepdata_handlers(root_logger)
|
||||
_remove_deepdata_handlers(logging.getLogger("uvicorn"))
|
||||
_remove_deepdata_handlers(logging.getLogger("uvicorn.access"))
|
||||
_remove_deepdata_handlers(logging.getLogger("uvicorn.error"))
|
||||
_remove_deepdata_handlers(logging.getLogger("sqlalchemy.engine"))
|
||||
|
||||
|
||||
def test_copytruncate_log_rotation_script_rotates_only_log_like_files(tmp_path: Path) -> None:
|
||||
log_file = tmp_path / "worker.log"
|
||||
old_backup = tmp_path / "worker.1.log"
|
||||
ignored_file = tmp_path / "worker.txt"
|
||||
|
||||
log_file.write_text("x" * 20, encoding="utf-8")
|
||||
old_backup.write_text("old", encoding="utf-8")
|
||||
ignored_file.write_text("x" * 20, encoding="utf-8")
|
||||
|
||||
env = {
|
||||
**os.environ,
|
||||
"LOG_ROTATION_MAX_SIZE": "10B",
|
||||
"LOG_ROTATION_BACKUP_COUNT": "2",
|
||||
}
|
||||
subprocess.run(
|
||||
[
|
||||
"bash",
|
||||
str(_repo_root() / "scripts/images/common/log-rotate-copytruncate.sh"),
|
||||
"--once",
|
||||
str(tmp_path),
|
||||
],
|
||||
check=True,
|
||||
env=env,
|
||||
)
|
||||
|
||||
assert log_file.read_text(encoding="utf-8") == ""
|
||||
assert (tmp_path / "worker.1.log").read_text(encoding="utf-8") == "x" * 20
|
||||
assert (tmp_path / "worker.2.log").read_text(encoding="utf-8") == "old"
|
||||
assert ignored_file.read_text(encoding="utf-8") == "x" * 20
|
||||
|
||||
|
||||
def test_log_rotation_shell_scripts_are_valid_bash() -> None:
|
||||
root = _repo_root()
|
||||
scripts = [
|
||||
root / "scripts/images/common/log-rotate-copytruncate.sh",
|
||||
root / "scripts/images/frontend/rotate-nginx-logs.sh",
|
||||
root / "scripts/images/database/prune-postgres-logs.sh",
|
||||
root / "scripts/images/database/start.sh",
|
||||
]
|
||||
for script in scripts:
|
||||
subprocess.run(["bash", "-n", str(script)], check=True)
|
||||
Reference in New Issue
Block a user