feat: add loguru-based logging with file rotation and stdout output
This commit is contained in:
@@ -10,3 +10,4 @@ wheels/
|
|||||||
.venv
|
.venv
|
||||||
|
|
||||||
.vscode
|
.vscode
|
||||||
|
*.log
|
||||||
@@ -8,4 +8,5 @@ dependencies = [
|
|||||||
"pydantic>=2.11.4",
|
"pydantic>=2.11.4",
|
||||||
"typer==0.9.0",
|
"typer==0.9.0",
|
||||||
"click==8.1.3",
|
"click==8.1.3",
|
||||||
|
"loguru>=0.7.3",
|
||||||
]
|
]
|
||||||
|
|||||||
+4
-2
@@ -1,6 +1,7 @@
|
|||||||
import typer
|
import typer
|
||||||
import os
|
import os
|
||||||
from helpers import get_targets, run_backup, ask_repokey
|
from helpers import get_targets, run_backup, ask_repokey
|
||||||
|
from logger import logger
|
||||||
|
|
||||||
app = typer.Typer()
|
app = typer.Typer()
|
||||||
|
|
||||||
@@ -13,10 +14,11 @@ def backup():
|
|||||||
|
|
||||||
for target in targets:
|
for target in targets:
|
||||||
if not target.enabled:
|
if not target.enabled:
|
||||||
print(f"Skipping disabled target: {target.path}")
|
logger.info(f"Skipping disabled target: {target.path}")
|
||||||
continue
|
continue
|
||||||
result_info = run_backup(target)
|
result_info = run_backup(target)
|
||||||
print(result_info.model_dump_json(indent=2))
|
logger.info(f"Backup completed for {target.path}")
|
||||||
|
logger.debug(result_info.model_dump_json(indent=2))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
+11
-7
@@ -6,6 +6,7 @@ from subprocess import run
|
|||||||
import os
|
import os
|
||||||
import typer
|
import typer
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
from logger import logger
|
||||||
|
|
||||||
# Constants
|
# Constants
|
||||||
JSON_TARGETS = Path(__file__).parent / "targets.json"
|
JSON_TARGETS = Path(__file__).parent / "targets.json"
|
||||||
@@ -73,6 +74,7 @@ def get_targets() -> list[BackupItem]:
|
|||||||
targets = json.load(f)
|
targets = json.load(f)
|
||||||
return [BackupItem(**target) for target in targets]
|
return [BackupItem(**target) for target in targets]
|
||||||
except (json.JSONDecodeError, FileNotFoundError) as e:
|
except (json.JSONDecodeError, FileNotFoundError) as e:
|
||||||
|
logger.error(f"Error loading targets: {e}")
|
||||||
typer.echo(f"Error loading targets: {e}")
|
typer.echo(f"Error loading targets: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -98,6 +100,7 @@ def add_target(target: BackupItem) -> bool:
|
|||||||
write_targets(targets)
|
write_targets(targets)
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
logger.error(f"Error adding target: {e}")
|
||||||
typer.echo(f"Error adding target: {e}")
|
typer.echo(f"Error adding target: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -122,6 +125,7 @@ def write_targets(targets: list[BackupItem]) -> bool:
|
|||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
logger.error(f"Error writing targets: {e}")
|
||||||
typer.echo(f"Error writing targets: {e}")
|
typer.echo(f"Error writing targets: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -162,7 +166,7 @@ def prune_backups(targets: list[BackupItem], daily: int = 1, weekly: int = 2,
|
|||||||
"--keep-yearly", str(yearly),
|
"--keep-yearly", str(yearly),
|
||||||
target.target,
|
target.target,
|
||||||
]
|
]
|
||||||
print("Pruning target:", target.path)
|
logger.info(f"Pruning target: {target.path}")
|
||||||
prune_result = run(cmd, capture_output=True, text=True)
|
prune_result = run(cmd, capture_output=True, text=True)
|
||||||
|
|
||||||
if prune_result.returncode != 0:
|
if prune_result.returncode != 0:
|
||||||
@@ -170,8 +174,8 @@ def prune_backups(targets: list[BackupItem], daily: int = 1, weekly: int = 2,
|
|||||||
results.append(result)
|
results.append(result)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print("Pruned target:", target.path)
|
logger.info(f"Pruned target: {target.path}")
|
||||||
print("Compacting cache")
|
logger.info("Compacting cache")
|
||||||
compact_result = run(["borg", "compact", target.target], capture_output=True, text=True)
|
compact_result = run(["borg", "compact", target.target], capture_output=True, text=True)
|
||||||
|
|
||||||
if compact_result.returncode != 0:
|
if compact_result.returncode != 0:
|
||||||
@@ -179,7 +183,7 @@ def prune_backups(targets: list[BackupItem], daily: int = 1, weekly: int = 2,
|
|||||||
results.append(result)
|
results.append(result)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print("Compacted cache")
|
logger.info("Compacted cache")
|
||||||
result["success"] = True
|
result["success"] = True
|
||||||
result["message"] = "Pruned and compacted successfully"
|
result["message"] = "Pruned and compacted successfully"
|
||||||
|
|
||||||
@@ -226,11 +230,11 @@ def run_backup(target: BackupItem) -> Optional[ResultInfo]:
|
|||||||
cmd.insert(3, "--exclude")
|
cmd.insert(3, "--exclude")
|
||||||
cmd.insert(4, ",".join(target.excludes))
|
cmd.insert(4, ",".join(target.excludes))
|
||||||
|
|
||||||
print("Running target:", target.path)
|
logger.info(f"Running target: {target.path}")
|
||||||
result = run(cmd, capture_output=True)
|
result = run(cmd, capture_output=True)
|
||||||
|
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
print("Result:", result.stderr.decode("utf-8"))
|
logger.error(f"Backup error: {result.stderr.decode('utf-8')}")
|
||||||
raise Exception(result.stderr.decode("utf-8"))
|
raise Exception(result.stderr.decode("utf-8"))
|
||||||
|
|
||||||
result_info = ResultInfo(**json.loads(result.stdout.decode("utf-8")))
|
result_info = ResultInfo(**json.loads(result.stdout.decode("utf-8")))
|
||||||
@@ -239,5 +243,5 @@ def run_backup(target: BackupItem) -> Optional[ResultInfo]:
|
|||||||
return result_info
|
return result_info
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Backup failed: {str(e)}")
|
logger.error(f"Backup failed: {str(e)}")
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
from loguru import logger
|
||||||
|
from pathlib import Path
|
||||||
|
from sys import stdout
|
||||||
|
|
||||||
|
LOGS_DIR = Path(__file__).parent / "logs"
|
||||||
|
if not LOGS_DIR.exists():
|
||||||
|
LOGS_DIR.mkdir(parents=True)
|
||||||
|
|
||||||
|
logger.remove()
|
||||||
|
logger.add(LOGS_DIR / "backupper.log", level="INFO", rotation="1 day", compression="gz")
|
||||||
|
logger.add(stdout, level="INFO", format="<level>{message}</level>")
|
||||||
@@ -17,6 +17,7 @@ version = "0.1.0"
|
|||||||
source = { virtual = "." }
|
source = { virtual = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "click" },
|
{ name = "click" },
|
||||||
|
{ name = "loguru" },
|
||||||
{ name = "pydantic" },
|
{ name = "pydantic" },
|
||||||
{ name = "typer" },
|
{ name = "typer" },
|
||||||
]
|
]
|
||||||
@@ -24,6 +25,7 @@ dependencies = [
|
|||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "click", specifier = "==8.1.3" },
|
{ name = "click", specifier = "==8.1.3" },
|
||||||
|
{ name = "loguru", specifier = ">=0.7.3" },
|
||||||
{ name = "pydantic", specifier = ">=2.11.4" },
|
{ name = "pydantic", specifier = ">=2.11.4" },
|
||||||
{ name = "typer", specifier = "==0.9.0" },
|
{ name = "typer", specifier = "==0.9.0" },
|
||||||
]
|
]
|
||||||
@@ -49,6 +51,19 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "loguru"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||||
|
{ name = "win32-setctime", marker = "sys_platform == 'win32'" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559, upload-time = "2024-12-06T11:20:56.608Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pydantic"
|
name = "pydantic"
|
||||||
version = "2.11.4"
|
version = "2.11.4"
|
||||||
@@ -139,3 +154,12 @@ sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846
|
|||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125, upload-time = "2025-02-25T17:27:57.754Z" },
|
{ url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125, upload-time = "2025-02-25T17:27:57.754Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "win32-setctime"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867, upload-time = "2024-12-07T15:28:28.314Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" },
|
||||||
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user