File indexing completed on 2026-04-27 07:41:42
0001
0002 """
0003 Test script for REST API logging functionality.
0004
0005 This script demonstrates how agents can send log messages to the swf-monitor
0006 database via the REST API endpoint. It provides both direct REST calls and
0007 a custom logging handler that integrates with Python's logging system.
0008 """
0009
0010 import requests
0011 import logging
0012 import json
0013 import os
0014 import sys
0015 from datetime import datetime
0016 from typing import Dict, Any, Optional
0017
0018
0019 class RestLogHandler(logging.Handler):
0020 """
0021 Custom logging handler that sends log records to the swf-monitor REST API.
0022
0023 This handler formats Python log records and sends them to the /api/logs/
0024 endpoint for storage in the database.
0025 """
0026
0027 def __init__(self, base_url: str, app_name: str, instance_name: str,
0028 timeout: int = 10):
0029 """
0030 Initialize the REST logging handler.
0031
0032 Args:
0033 base_url: Base URL of the swf-monitor API (e.g., 'http://localhost:8000')
0034 app_name: Name of the application sending logs
0035 instance_name: Instance identifier for this application
0036 timeout: Request timeout in seconds
0037 """
0038 super().__init__()
0039 self.logs_url = f"{base_url.rstrip('/')}/api/logs/"
0040 self.app_name = app_name
0041 self.instance_name = instance_name
0042 self.timeout = timeout
0043 self.session = requests.Session()
0044
0045 def emit(self, record: logging.LogRecord) -> None:
0046 """
0047 Send a log record to the REST API.
0048
0049 Args:
0050 record: Python LogRecord to send
0051 """
0052 try:
0053 log_data = self._format_log_record(record)
0054 response = self.session.post(
0055 self.logs_url,
0056 json=log_data,
0057 timeout=self.timeout,
0058 headers={'Content-Type': 'application/json'}
0059 )
0060 response.raise_for_status()
0061
0062 except requests.exceptions.RequestException as e:
0063
0064
0065 print(f"Failed to send log to REST API: {e}", file=sys.stderr)
0066 except Exception as e:
0067
0068 print(f"Error in REST log handler: {e}", file=sys.stderr)
0069
0070 def _format_log_record(self, record: logging.LogRecord) -> Dict[str, Any]:
0071 """
0072 Convert a Python LogRecord to the format expected by the REST API.
0073
0074 Args:
0075 record: Python LogRecord
0076
0077 Returns:
0078 Dictionary suitable for JSON serialization and REST API
0079 """
0080 return {
0081 'app_name': self.app_name,
0082 'instance_name': self.instance_name,
0083 'timestamp': datetime.fromtimestamp(record.created).isoformat(),
0084 'level': record.levelno,
0085 'levelname': record.levelname,
0086 'message': record.getMessage(),
0087 'module': record.module or 'unknown',
0088 'funcname': record.funcName or 'unknown',
0089 'lineno': record.lineno or 0,
0090 'process': record.process or 0,
0091 'thread': record.thread or 0,
0092 'extra_data': {
0093 'pathname': record.pathname,
0094 'filename': record.filename,
0095 'created': record.created,
0096 'msecs': record.msecs,
0097 }
0098 }
0099
0100
0101 def send_direct_log_message(base_url: str, app_name: str, instance_name: str,
0102 level: str, message: str, **kwargs) -> bool:
0103 """
0104 Send a log message directly to the REST API without using Python logging.
0105
0106 Args:
0107 base_url: Base URL of the swf-monitor API
0108 app_name: Application name
0109 instance_name: Instance identifier
0110 level: Log level (INFO, WARNING, ERROR, etc.)
0111 message: Log message text
0112 **kwargs: Additional fields for the log record
0113
0114 Returns:
0115 True if successful, False otherwise
0116 """
0117 logs_url = f"{base_url.rstrip('/')}/api/logs/"
0118
0119
0120 level_mapping = {
0121 'DEBUG': logging.DEBUG,
0122 'INFO': logging.INFO,
0123 'WARNING': logging.WARNING,
0124 'ERROR': logging.ERROR,
0125 'CRITICAL': logging.CRITICAL
0126 }
0127
0128 log_data = {
0129 'app_name': app_name,
0130 'instance_name': instance_name,
0131 'timestamp': datetime.now().isoformat(),
0132 'level': level_mapping.get(level.upper(), logging.INFO),
0133 'levelname': level.upper(),
0134 'message': message,
0135 'module': kwargs.get('module', 'test_script'),
0136 'funcname': kwargs.get('funcname', 'send_direct_log_message'),
0137 'lineno': kwargs.get('lineno', 0),
0138 'process': kwargs.get('process', os.getpid()),
0139 'thread': kwargs.get('thread', 0),
0140 'extra_data': kwargs.get('extra_data', {})
0141 }
0142
0143 try:
0144 response = requests.post(
0145 logs_url,
0146 json=log_data,
0147 timeout=10,
0148 headers={'Content-Type': 'application/json'}
0149 )
0150 response.raise_for_status()
0151 print(f"✅ Successfully sent {level} log: {message}")
0152 return True
0153
0154 except requests.exceptions.RequestException as e:
0155 print(f"❌ Failed to send log: {e}")
0156 return False
0157
0158
0159 def test_rest_logging(base_url: str = "http://localhost:8000") -> None:
0160 """
0161 Comprehensive test of REST API logging functionality.
0162
0163 Tests both direct REST calls and the custom logging handler.
0164
0165 Args:
0166 base_url: Base URL of the swf-monitor service
0167 """
0168 app_name = "test_logging_script"
0169 instance_name = f"test_instance_{os.getpid()}"
0170
0171 print(f"🧪 Testing REST logging against {base_url}")
0172 print(f"📝 App: {app_name}, Instance: {instance_name}")
0173 print("-" * 60)
0174
0175
0176 print("1️⃣ Testing direct REST API calls...")
0177
0178 test_messages = [
0179 ("INFO", "Application started successfully"),
0180 ("WARNING", "Configuration file not found, using defaults"),
0181 ("ERROR", "Failed to connect to external service"),
0182 ("DEBUG", "Processing item 42 of 100"),
0183 ("CRITICAL", "System out of memory, shutting down")
0184 ]
0185
0186 success_count = 0
0187 for level, message in test_messages:
0188 if send_direct_log_message(base_url, app_name, instance_name, level, message):
0189 success_count += 1
0190
0191 print(f"Direct API calls: {success_count}/{len(test_messages)} successful\n")
0192
0193
0194 print("2️⃣ Testing Python logging handler integration...")
0195
0196
0197 logger = logging.getLogger('test_rest_logger')
0198 logger.setLevel(logging.DEBUG)
0199
0200
0201 rest_handler = RestLogHandler(base_url, app_name, f"{instance_name}_handler")
0202 logger.addHandler(rest_handler)
0203
0204
0205 logger.debug("Debug message from Python logging")
0206 logger.info("Info message with data: %s", {"key": "value"})
0207 logger.warning("Warning about deprecated function")
0208 logger.error("Error processing request ID %d", 12345)
0209 logger.critical("Critical system failure detected")
0210
0211 print("Python logging handler tests completed\n")
0212
0213
0214 print("3️⃣ Testing bulk logging (simulating real agent usage)...")
0215
0216 for i in range(10):
0217 logger.info(f"Processing workflow step {i+1}/10")
0218 if i % 3 == 0:
0219 logger.debug(f"Step {i+1} details: processing file batch")
0220 if i == 7:
0221 logger.warning(f"Step {i+1} took longer than expected")
0222
0223 logger.info("Workflow processing completed successfully")
0224 print("Bulk logging simulation completed\n")
0225
0226 print("✅ All REST logging tests completed!")
0227 print("Check the swf-monitor database/UI to verify log entries were created.")
0228
0229
0230 def main():
0231 """Main entry point for the test script."""
0232 import argparse
0233
0234 parser = argparse.ArgumentParser(
0235 description="Test REST API logging functionality for swf-monitor"
0236 )
0237 parser.add_argument(
0238 '--url', '-u',
0239 default='http://localhost:8000',
0240 help='Base URL of swf-monitor service (default: http://localhost:8000)'
0241 )
0242 parser.add_argument(
0243 '--direct-only',
0244 action='store_true',
0245 help='Only test direct REST calls, skip Python logging handler'
0246 )
0247
0248 args = parser.parse_args()
0249
0250 if args.direct_only:
0251
0252 print("🧪 Quick REST API logging test...")
0253 success = send_direct_log_message(
0254 args.url,
0255 "quick_test",
0256 "test_instance",
0257 "INFO",
0258 "Quick test log message"
0259 )
0260 sys.exit(0 if success else 1)
0261 else:
0262
0263 test_rest_logging(args.url)
0264
0265
0266 if __name__ == "__main__":
0267 main()