219 lines
6.6 KiB
Python
219 lines
6.6 KiB
Python
"""
|
|
Deployment logging module
|
|
|
|
Handles writing deployment logs to success/failed directories
|
|
"""
|
|
|
|
import logging
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class DeploymentFileLogger:
|
|
"""Logs deployment results to files"""
|
|
|
|
def __init__(self, logs_dir: Path = Path("logs")):
|
|
"""
|
|
Initialize deployment file logger
|
|
|
|
Args:
|
|
logs_dir: Base directory for logs (default: logs/)
|
|
"""
|
|
self._logs_dir = logs_dir
|
|
self._success_dir = logs_dir / "success"
|
|
self._failed_dir = logs_dir / "failed"
|
|
self._logger = logging.getLogger(f"{__name__}.DeploymentFileLogger")
|
|
|
|
# Ensure directories exist
|
|
self._ensure_directories()
|
|
|
|
def _ensure_directories(self) -> None:
|
|
"""Create log directories if they don't exist"""
|
|
for directory in [self._success_dir, self._failed_dir]:
|
|
directory.mkdir(parents=True, exist_ok=True)
|
|
self._logger.debug(f"Ensured directory exists: {directory}")
|
|
|
|
def _sanitize_url(self, url: str) -> str:
|
|
"""
|
|
Sanitize URL for use in filename
|
|
|
|
Args:
|
|
url: URL to sanitize
|
|
|
|
Returns:
|
|
Sanitized URL safe for filename
|
|
"""
|
|
# Remove protocol if present
|
|
url = url.replace("https://", "").replace("http://", "")
|
|
# Replace invalid filename characters
|
|
return url.replace("/", "_").replace(":", "_")
|
|
|
|
def _generate_filename(self, status: str, url: str, timestamp: datetime) -> str:
|
|
"""
|
|
Generate log filename
|
|
|
|
Format: success_url_date.txt or failed_url_date.txt
|
|
|
|
Args:
|
|
status: 'success' or 'failed'
|
|
url: Deployment URL
|
|
timestamp: Deployment timestamp
|
|
|
|
Returns:
|
|
Filename string
|
|
"""
|
|
sanitized_url = self._sanitize_url(url)
|
|
date_str = timestamp.strftime("%Y%m%d_%H%M%S")
|
|
return f"{status}_{sanitized_url}_{date_str}.txt"
|
|
|
|
def log_success(
|
|
self,
|
|
url: str,
|
|
subdomain: str,
|
|
duration: float,
|
|
timestamp: Optional[datetime] = None
|
|
) -> Path:
|
|
"""
|
|
Log successful deployment
|
|
|
|
Args:
|
|
url: Deployment URL
|
|
subdomain: Subdomain used
|
|
duration: Deployment duration in seconds
|
|
timestamp: Deployment timestamp (default: now)
|
|
|
|
Returns:
|
|
Path to created log file
|
|
"""
|
|
if timestamp is None:
|
|
timestamp = datetime.now()
|
|
|
|
filename = self._generate_filename("success", url, timestamp)
|
|
log_file = self._success_dir / filename
|
|
|
|
log_content = self._format_success_log(
|
|
url, subdomain, duration, timestamp
|
|
)
|
|
|
|
log_file.write_text(log_content)
|
|
self._logger.info(f"✓ Success log written: {log_file}")
|
|
|
|
return log_file
|
|
|
|
def log_failure(
|
|
self,
|
|
url: str,
|
|
subdomain: str,
|
|
error: str,
|
|
timestamp: Optional[datetime] = None
|
|
) -> Path:
|
|
"""
|
|
Log failed deployment
|
|
|
|
Args:
|
|
url: Deployment URL (may be empty if failed early)
|
|
subdomain: Subdomain used (may be empty if failed early)
|
|
error: Error message
|
|
timestamp: Deployment timestamp (default: now)
|
|
|
|
Returns:
|
|
Path to created log file
|
|
"""
|
|
if timestamp is None:
|
|
timestamp = datetime.now()
|
|
|
|
# Handle case where URL is empty (failed before URL generation)
|
|
log_url = url if url else "unknown"
|
|
filename = self._generate_filename("failed", log_url, timestamp)
|
|
log_file = self._failed_dir / filename
|
|
|
|
log_content = self._format_failure_log(
|
|
url, subdomain, error, timestamp
|
|
)
|
|
|
|
log_file.write_text(log_content)
|
|
self._logger.info(f"✓ Failure log written: {log_file}")
|
|
|
|
return log_file
|
|
|
|
def _format_success_log(
|
|
self,
|
|
url: str,
|
|
subdomain: str,
|
|
duration: float,
|
|
timestamp: datetime
|
|
) -> str:
|
|
"""
|
|
Format success log content
|
|
|
|
Args:
|
|
url: Deployment URL
|
|
subdomain: Subdomain used
|
|
duration: Deployment duration in seconds
|
|
timestamp: Deployment timestamp
|
|
|
|
Returns:
|
|
Formatted log content
|
|
"""
|
|
return f"""╔══════════════════════════════════════════════╗
|
|
║ DEPLOYMENT SUCCESS LOG ║
|
|
╚══════════════════════════════════════════════╝
|
|
|
|
Timestamp: {timestamp.strftime("%Y-%m-%d %H:%M:%S")}
|
|
Status: SUCCESS
|
|
URL: https://{url}
|
|
Subdomain: {subdomain}
|
|
Duration: {duration:.2f} seconds
|
|
|
|
═══════════════════════════════════════════════
|
|
|
|
Deployment completed successfully.
|
|
All services are running and health checks passed.
|
|
"""
|
|
|
|
def _format_failure_log(
|
|
self,
|
|
url: str,
|
|
subdomain: str,
|
|
error: str,
|
|
timestamp: datetime
|
|
) -> str:
|
|
"""
|
|
Format failure log content
|
|
|
|
Args:
|
|
url: Deployment URL (may be empty)
|
|
subdomain: Subdomain used (may be empty)
|
|
error: Error message
|
|
timestamp: Deployment timestamp
|
|
|
|
Returns:
|
|
Formatted log content
|
|
"""
|
|
url_display = f"https://{url}" if url else "N/A (failed before URL generation)"
|
|
subdomain_display = subdomain if subdomain else "N/A"
|
|
|
|
return f"""╔══════════════════════════════════════════════╗
|
|
║ DEPLOYMENT FAILURE LOG ║
|
|
╚══════════════════════════════════════════════╝
|
|
|
|
Timestamp: {timestamp.strftime("%Y-%m-%d %H:%M:%S")}
|
|
Status: FAILED
|
|
URL: {url_display}
|
|
Subdomain: {subdomain_display}
|
|
|
|
═══════════════════════════════════════════════
|
|
|
|
ERROR:
|
|
{error}
|
|
|
|
═══════════════════════════════════════════════
|
|
|
|
Deployment failed. See error details above.
|
|
All changes have been rolled back.
|
|
"""
|