From 5a0ae44a45502081289e0ace0485591bb32fefa8 Mon Sep 17 00:00:00 2001 From: Jonas Linter <{email_address}> Date: Wed, 15 Oct 2025 08:55:51 +0200 Subject: [PATCH] fixed test warning --- alpinebits.log | 14 +++++++++ config/config.yaml | 20 ++++++------- docs/EMAIL_MONITORING_QUICKSTART.md | 13 ++++++--- src/alpine_bits_python/email_monitoring.py | 33 ++++++++++++++-------- 4 files changed, 54 insertions(+), 26 deletions(-) diff --git a/alpinebits.log b/alpinebits.log index a17a9b0..94a6c66 100644 --- a/alpinebits.log +++ b/alpinebits.log @@ -14059,3 +14059,17 @@ IndexError: list index out of range 2025-10-10 10:59:53 - alpine_bits_python.api - INFO - Hotel 39040_001 has no push_endpoint configured 2025-10-10 10:59:53 - alpine_bits_python.api - INFO - Database tables checked/created at startup. 2025-10-10 10:59:53 - httpx - INFO - HTTP Request: PUT http://testserver/api/hoteldata/conversions_import/test_reservation.xml "HTTP/1.1 401 Unauthorized" +2025-10-15 08:49:50 - root - INFO - Logging to file: alpinebits.log +2025-10-15 08:49:50 - root - INFO - Logging configured at INFO level +2025-10-15 08:49:52 - alpine_bits_python.email_service - INFO - Email service initialized: smtp.gmail.com:587 +2025-10-15 08:49:52 - root - INFO - Logging to file: alpinebits.log +2025-10-15 08:49:52 - root - INFO - Logging configured at INFO level +2025-10-15 08:49:54 - alpine_bits_python.email_service - INFO - Email service initialized: smtp.gmail.com:587 +2025-10-15 08:52:37 - root - INFO - Logging to file: alpinebits.log +2025-10-15 08:52:37 - root - INFO - Logging configured at INFO level +2025-10-15 08:52:54 - root - INFO - Logging to file: alpinebits.log +2025-10-15 08:52:54 - root - INFO - Logging configured at INFO level +2025-10-15 08:52:56 - alpine_bits_python.email_service - INFO - Email service initialized: smtp.titan.email:465 +2025-10-15 08:52:56 - root - INFO - Logging to file: alpinebits.log +2025-10-15 08:52:56 - root - INFO - Logging configured at INFO level +2025-10-15 08:52:58 - alpine_bits_python.email_service - INFO - Email service initialized: smtp.titan.email:465 diff --git a/config/config.yaml b/config/config.yaml index 6686536..c46fadf 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -44,15 +44,15 @@ alpine_bits_auth: email: # SMTP server configuration smtp: - host: "smtp.gmail.com" # Your SMTP server - port: 587 # Usually 587 for TLS, 465 for SSL - username: !secret EMAIL_USERNAME # SMTP username + host: "smtp.titan.email" # Your SMTP server + port: 465 # Usually 587 for TLS, 465 for SSL + username: info@99tales.net # SMTP username password: !secret EMAIL_PASSWORD # SMTP password - use_tls: true # Use STARTTLS - use_ssl: false # Use SSL/TLS from start + use_tls: false # Use STARTTLS + use_ssl: true # Use SSL/TLS from start # Email addresses - from_address: "noreply@99tales.com" # Sender address + from_address: "info@99tales.net" # Sender address from_name: "AlpineBits Monitor" # Sender display name # Monitoring and alerting @@ -61,8 +61,8 @@ email: daily_report: enabled: false # Set to true to enable daily reports recipients: - - "admin@99tales.com" - - "dev@99tales.com" + - "jonas@vaius.ai" + #- "dev@99tales.com" send_time: "08:00" # Time to send daily report (24h format, local time) include_stats: true # Include reservation/customer stats include_errors: true # Include error summary @@ -71,8 +71,8 @@ email: error_alerts: enabled: false # Set to true to enable error alerts recipients: - - "alerts@99tales.com" - - "oncall@99tales.com" + - "jonas@vaius.ai" + #- "oncall@99tales.com" # Alert is sent immediately if threshold is reached error_threshold: 5 # Send immediate alert after N errors # Otherwise, alert is sent after buffer time expires diff --git a/docs/EMAIL_MONITORING_QUICKSTART.md b/docs/EMAIL_MONITORING_QUICKSTART.md index b54a9f9..c9c6dbf 100644 --- a/docs/EMAIL_MONITORING_QUICKSTART.md +++ b/docs/EMAIL_MONITORING_QUICKSTART.md @@ -20,11 +20,11 @@ email: ## 2. Set Environment Variables -Create a `.env` file in the project root: +In the secrets.yaml file add the secrets -```bash -EMAIL_USERNAME=your-email@gmail.com -EMAIL_PASSWORD=your-app-password +```yaml +EMAIL_USERNAME: "your_email_username" +EMAIL_PASSWORD: "your_email_password" ``` > **Note:** For Gmail, use an [App Password](https://support.google.com/accounts/answer/185833), not your regular password. @@ -46,6 +46,7 @@ email: ``` **How it works:** + - Sends immediate alert after 5 errors - Otherwise sends after 15 minutes - Waits 15 minutes between alerts (cooldown) @@ -75,6 +76,7 @@ uv run python examples/test_email_monitoring.py ``` This will: + - ✅ Send a test email - ✅ Trigger an error alert - ✅ Send a test daily report @@ -124,12 +126,14 @@ Errors (3): ### No emails received? 1. Check your SMTP credentials: + ```bash echo $EMAIL_USERNAME echo $EMAIL_PASSWORD ``` 2. Check application logs for errors: + ```bash tail -f alpinebits.log | grep -i email ``` @@ -167,6 +171,7 @@ Errors (3): ## Support For issues or questions: + - Check the [documentation](./EMAIL_MONITORING.md) - Review [test examples](../examples/test_email_monitoring.py) - Open an issue on GitHub diff --git a/src/alpine_bits_python/email_monitoring.py b/src/alpine_bits_python/email_monitoring.py index 81d4809..ea8349a 100644 --- a/src/alpine_bits_python/email_monitoring.py +++ b/src/alpine_bits_python/email_monitoring.py @@ -174,13 +174,12 @@ class EmailAlertHandler(logging.Handler): self._flush_buffer(immediate=True), self.loop, ) - else: - # Schedule delayed flush if not already scheduled - if not self._flush_task or self._flush_task.done(): - self._flush_task = asyncio.run_coroutine_threadsafe( - self._schedule_delayed_flush(), - self.loop, - ) + # Schedule delayed flush if not already scheduled + elif not self._flush_task or self._flush_task.done(): + self._flush_task = asyncio.run_coroutine_threadsafe( + self._schedule_delayed_flush(), + self.loop, + ) except Exception: # Never let the handler crash - just log and continue @@ -237,7 +236,9 @@ class EmailAlertHandler(logging.Handler): emoji = "⚠️" reason = f"({self.buffer_minutes} minute buffer)" - subject = f"{emoji} AlpineBits Error {alert_type}: {error_count} errors {reason}" + subject = ( + f"{emoji} AlpineBits Error {alert_type}: {error_count} errors {reason}" + ) # Build plain text body body = f"Error Alert - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n" @@ -290,10 +291,18 @@ class EmailAlertHandler(logging.Handler): # Flush any remaining errors immediately if self.error_buffer and self.loop: try: - asyncio.run_coroutine_threadsafe( - self._flush_buffer(immediate=False), - self.loop, - ).result(timeout=5) + # Check if the loop is still running + if not self.loop.is_closed(): + future = asyncio.run_coroutine_threadsafe( + self._flush_buffer(immediate=False), + self.loop, + ) + future.result(timeout=5) + else: + _LOGGER.warning( + "Event loop closed, cannot flush %d remaining errors", + len(self.error_buffer), + ) except Exception: _LOGGER.exception("Error flushing buffer on close")