Spaces:
Sleeping
Sleeping
| """ | |
| Structured JSON Logging for S3 Backup/Restore Operations | |
| Provides consistent logging format for observability and debugging. | |
| All logs are emitted as JSON for easy parsing by log aggregation systems. | |
| """ | |
| import json | |
| import logging | |
| from datetime import datetime | |
| from typing import Optional, Dict, Any | |
| logger = logging.getLogger(__name__) | |
| def _log_event(event: str, **kwargs) -> None: | |
| """ | |
| Internal helper to emit structured JSON log messages. | |
| Args: | |
| event: Event name (e.g., "backup_started", "restore_completed") | |
| **kwargs: Additional event-specific fields | |
| """ | |
| log_data = { | |
| 'event': event, | |
| 'timestamp': datetime.utcnow().isoformat() + 'Z', | |
| **kwargs | |
| } | |
| # Mask sensitive data | |
| if 's3_key' in log_data: | |
| # S3 keys are safe to log | |
| pass | |
| if 'error' in log_data and isinstance(log_data['error'], Exception): | |
| # Convert exception to string | |
| log_data['error'] = str(log_data['error']) | |
| logger.info(json.dumps(log_data)) | |
| def backup_started(db_path: str, db_size_bytes: int) -> None: | |
| """ | |
| Log that a backup operation has started. | |
| Args: | |
| db_path: Path to the database file being backed up | |
| db_size_bytes: Size of the database in bytes | |
| """ | |
| _log_event( | |
| 'backup_started', | |
| db_path=db_path, | |
| db_size_bytes=db_size_bytes | |
| ) | |
| def backup_completed( | |
| duration_seconds: float, | |
| s3_key: str, | |
| upload_size_bytes: int | |
| ) -> None: | |
| """ | |
| Log that a backup operation completed successfully. | |
| Args: | |
| duration_seconds: Time taken for the backup operation | |
| s3_key: S3 object key where backup was stored | |
| upload_size_bytes: Size of uploaded file in bytes | |
| """ | |
| _log_event( | |
| 'backup_completed', | |
| duration_seconds=round(duration_seconds, 2), | |
| s3_key=s3_key, | |
| upload_size_bytes=upload_size_bytes | |
| ) | |
| def backup_failed( | |
| error: str, | |
| retry_attempt: Optional[int] = None, | |
| max_retries: Optional[int] = None | |
| ) -> None: | |
| """ | |
| Log that a backup operation failed. | |
| Args: | |
| error: Error message or exception | |
| retry_attempt: Current retry attempt number (if retrying) | |
| max_retries: Maximum number of retries (if retrying) | |
| """ | |
| log_data = { | |
| 'error': str(error) | |
| } | |
| if retry_attempt is not None: | |
| log_data['retry_attempt'] = retry_attempt | |
| if max_retries is not None: | |
| log_data['max_retries'] = max_retries | |
| _log_event('backup_failed', **log_data) | |
| def restore_started() -> None: | |
| """Log that a restore operation has started.""" | |
| _log_event('restore_started') | |
| def restore_completed( | |
| duration_seconds: float, | |
| s3_key: Optional[str], | |
| download_size_bytes: Optional[int], | |
| result: str | |
| ) -> None: | |
| """ | |
| Log that a restore operation completed. | |
| Args: | |
| duration_seconds: Time taken for the restore operation | |
| s3_key: S3 object key that was restored (None if no restore needed) | |
| download_size_bytes: Size of downloaded file (None if no download) | |
| result: Restore result enum value (e.g., "restored_from_s3", "local_newer") | |
| """ | |
| log_data = { | |
| 'duration_seconds': round(duration_seconds, 2), | |
| 'result': result | |
| } | |
| if s3_key: | |
| log_data['s3_key'] = s3_key | |
| if download_size_bytes: | |
| log_data['download_size_bytes'] = download_size_bytes | |
| _log_event('restore_completed', **log_data) | |
| def restore_fallback(reason: str, fallback_action: str) -> None: | |
| """ | |
| Log that a restore operation fell back to an alternative. | |
| Args: | |
| reason: Reason for fallback (e.g., "validation_failed", "network_error") | |
| fallback_action: Action taken (e.g., "using_local_database", "initializing_empty") | |
| """ | |
| _log_event( | |
| 'restore_fallback', | |
| reason=reason, | |
| fallback_action=fallback_action | |
| ) | |
| def backup_debounced(pending_requests: int, debounce_seconds: int) -> None: | |
| """ | |
| Log that backup requests are being debounced. | |
| Args: | |
| pending_requests: Number of backup requests collapsed into one | |
| debounce_seconds: Debounce period in seconds | |
| """ | |
| _log_event( | |
| 'backup_debounced', | |
| pending_requests=pending_requests, | |
| debounce_seconds=debounce_seconds | |
| ) | |
| def s3_credentials_validated(bucket: str, endpoint: Optional[str]) -> None: | |
| """ | |
| Log that S3 credentials were successfully validated. | |
| Args: | |
| bucket: S3 bucket name | |
| endpoint: S3 endpoint URL (None for AWS S3) | |
| """ | |
| _log_event( | |
| 's3_credentials_validated', | |
| bucket=bucket, | |
| endpoint=endpoint or 'AWS S3' | |
| ) | |
| def s3_credentials_invalid(error: str) -> None: | |
| """ | |
| Log that S3 credentials validation failed. | |
| Args: | |
| error: Error message | |
| """ | |
| _log_event( | |
| 's3_credentials_invalid', | |
| error=str(error) | |
| ) | |
| def backup_skip_reason(reason: str, operation: str) -> None: | |
| """ | |
| Log why a backup was skipped. | |
| Args: | |
| reason: Reason for skipping (e.g., "s3_not_enabled", "non_critical_update") | |
| operation: Operation that would have triggered backup | |
| """ | |
| _log_event( | |
| 'backup_skip', | |
| reason=reason, | |
| operation=operation | |
| ) | |