Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-25 08:29:08

0001 """
0002 REST logging module for swf-monitor agents.
0003 
0004 This module provides a simple way for agents to send logs to the swf-monitor
0005 database via REST API using standard Python logging.
0006 """
0007 
0008 import logging
0009 import requests
0010 from datetime import datetime
0011 
0012 
0013 class RestLogHandler(logging.Handler):
0014     """Logging handler that sends logs to swf-monitor REST API."""
0015     
0016     def __init__(self, base_url, app_name, instance_name, fallback_handler=None, timeout=5):
0017         super().__init__()
0018         self.logs_url = f"{base_url.rstrip('/')}/api/logs/"
0019         self.app_name = app_name
0020         self.instance_name = instance_name
0021         self.session = requests.Session()
0022         self.connection_failed = False
0023         self.fallback_handler = fallback_handler
0024         self.timeout = timeout
0025         
0026         # Load proxy settings from ~/.env if not already in environment
0027         self._load_proxy_settings()
0028     
0029     def _load_proxy_settings(self):
0030         """Load proxy bypass settings from ~/.env file."""
0031         import os
0032         from pathlib import Path
0033         
0034         env_file = Path.home() / ".env"
0035         if env_file.exists():
0036             with open(env_file) as f:
0037                 for line in f:
0038                     line = line.strip()
0039                     if line and not line.startswith('#') and '=' in line:
0040                         if line.startswith('export '):
0041                             line = line[7:]  # Remove 'export '
0042                         key, value = line.split('=', 1)
0043                         key = key.strip()
0044                         value = value.strip('"\'')
0045                         
0046                         # Set proxy-related environment variables
0047                         if key in ['NO_PROXY', 'no_proxy']:
0048                             os.environ[key] = value
0049         
0050         # Unset proxy variables if we're connecting to localhost
0051         if 'localhost' in self.logs_url or '127.0.0.1' in self.logs_url:
0052             for proxy_var in ['http_proxy', 'https_proxy', 'HTTP_PROXY', 'HTTPS_PROXY']:
0053                 if proxy_var in os.environ:
0054                     del os.environ[proxy_var]
0055         
0056     def emit(self, record):
0057         """Send log record to REST API."""
0058         try:
0059             extra_data = {}
0060             for key in ('execution_id', 'workflow_name', 'run_id', 'username'):
0061                 if hasattr(record, key):
0062                     extra_data[key] = getattr(record, key)
0063 
0064             log_data = {
0065                 'app_name': self.app_name,
0066                 'instance_name': self.instance_name,
0067                 'timestamp': datetime.fromtimestamp(record.created).isoformat(),
0068                 'level': record.levelno,
0069                 'levelname': record.levelname,
0070                 'message': record.getMessage(),
0071                 'module': record.module or 'unknown',
0072                 'funcname': record.funcName or 'unknown',
0073                 'lineno': record.lineno or 0,
0074                 'process': record.process or 0,
0075                 'thread': record.thread or 0,
0076                 'extra_data': extra_data or None,
0077             }
0078             
0079             response = self.session.post(self.logs_url, json=log_data, timeout=self.timeout)
0080             if response.status_code == 400:
0081                 # Log the detailed error for debugging
0082                 import logging
0083                 debug_logger = logging.getLogger('swf_common_lib.rest_logging.debug')
0084                 debug_logger.error(f"400 Bad Request details: {response.text}")
0085                 debug_logger.error(f"Sent data: {log_data}")
0086             response.raise_for_status()
0087 
0088             # Also emit to console handler if available (for dual output)
0089             if self.fallback_handler:
0090                 self.fallback_handler.emit(record)
0091 
0092         except Exception as e:
0093             # Use proper logging for warnings on first failure
0094             if not self.connection_failed:
0095                 # Use a separate logger for infrastructure warnings to avoid circular dependencies
0096                 import logging
0097                 infra_logger = logging.getLogger('swf_common_lib.rest_logging')
0098                 if not infra_logger.handlers:
0099                     # If no handlers configured, add a simple console handler
0100                     console_handler = logging.StreamHandler()
0101                     console_handler.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
0102                     infra_logger.addHandler(console_handler)
0103                     infra_logger.setLevel(logging.WARNING)
0104                 
0105                 infra_logger.warning(f"REST logging failed to send log to swf-monitor at {self.logs_url}: {e}")
0106                 infra_logger.warning("REST logging falling back to standard console logging")
0107                 self.connection_failed = True
0108             
0109             # Fall back to console handler if available
0110             if self.fallback_handler:
0111                 self.fallback_handler.emit(record)
0112             else:
0113                 # This is a serious configuration error - raise an exception
0114                 raise RuntimeError(
0115                     f"REST logging failed and no fallback handler is configured. "
0116                     f"This indicates setup_rest_logging() was not used correctly. "
0117                     f"Original log message: {record.levelname}: {record.getMessage()}"
0118                 )
0119 
0120 
0121 def setup_rest_logging(app_name, instance_name, base_url=None, timeout=10):
0122     """
0123     Setup REST logging for an agent.
0124     
0125     Args:
0126         app_name: Name of your application/agent
0127         instance_name: Unique identifier for this instance
0128         base_url: URL of swf-monitor service
0129         timeout: Timeout in seconds for REST requests (default: 10)
0130     
0131     Returns:
0132         Configured logger ready to use
0133     """
0134     # Use environment variable if base_url not provided
0135     if base_url is None:
0136         import os
0137         base_url = os.getenv('SWF_MONITOR_HTTP_URL', 'http://localhost:8002')
0138     
0139     logger = logging.getLogger(app_name)
0140     
0141     # Clear existing handlers to avoid duplicates
0142     for handler in logger.handlers[:]:
0143         logger.removeHandler(handler)
0144     
0145     logger.setLevel(logging.DEBUG)
0146     
0147     # Create console fallback handler
0148     console_handler = logging.StreamHandler()
0149     console_handler.setLevel(logging.DEBUG)
0150     formatter = logging.Formatter('%(levelname)s: %(message)s')
0151     console_handler.setFormatter(formatter)
0152     
0153     # Add REST handler with fallback capability
0154     rest_handler = RestLogHandler(base_url, app_name, instance_name, console_handler, timeout)
0155     logger.addHandler(rest_handler)
0156     
0157     return logger