health.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. """
  2. 系统健康检查模块
  3. 提供系统各组件健康状态检查和系统信息获取功能
  4. """
  5. import logging
  6. import platform
  7. import socket
  8. import psutil
  9. from flask import current_app
  10. from app import db
  11. from app.core.events import outbox_health
  12. from app.services.db_healthcheck import check_database_connection
  13. from app.services.neo4j_driver import Neo4jDriver
  14. logger = logging.getLogger(__name__)
  15. def data_source_pool_health():
  16. """Aggregate existing Worker pools without creating the runtime."""
  17. from app.core.data_source.runtime import peek_data_source_manager
  18. manager = peek_data_source_manager()
  19. statuses = manager.snapshot() if manager is not None else []
  20. return {
  21. "active_pool_count": sum(
  22. 1 for status in statuses if not status.draining
  23. ),
  24. "degraded_pool_count": sum(
  25. 1 for status in statuses if status.pool_state == "degraded"
  26. ),
  27. "open_circuit_count": sum(
  28. 1 for status in statuses if status.pool_state == "open"
  29. ),
  30. "checked_out_total": sum(
  31. int(status.checked_out) for status in statuses
  32. ),
  33. "pool_timeout_total": sum(
  34. int(status.pool_timeout_total) for status in statuses
  35. ),
  36. }
  37. def check_neo4j_connection():
  38. """
  39. 检查Neo4j数据库连接状态
  40. Returns:
  41. bool: 连接成功返回True,失败返回False
  42. """
  43. try:
  44. with Neo4jDriver().get_session() as session:
  45. # 执行简单查询确认连接
  46. session.run("RETURN 1")
  47. return True
  48. except Exception as e:
  49. logger.error(f"Neo4j数据库连接失败: {str(e)}")
  50. return False
  51. def check_system_health():
  52. """检查系统各个组件的健康状态"""
  53. health_status = {
  54. "database": check_database_connection(),
  55. "neo4j": Neo4jDriver().verify_connectivity(),
  56. "environment": current_app.config["FLASK_ENV"],
  57. "platform": current_app.config["PLATFORM"],
  58. "datasource_pools": data_source_pool_health(),
  59. }
  60. try:
  61. health_status["outbox"] = outbox_health(db.session)
  62. except Exception as exc:
  63. logger.warning("Outbox health unavailable: %s", exc)
  64. health_status["outbox"] = {"status": "unavailable"}
  65. # 检查所有组件是否都正常
  66. all_healthy = all([health_status["database"], health_status["neo4j"]])
  67. return {
  68. "status": "healthy" if all_healthy else "unhealthy",
  69. "components": health_status,
  70. }
  71. def get_system_info():
  72. """
  73. 获取系统运行环境信息
  74. 包括操作系统、Python版本、CPU使用率、内存使用情况等
  75. Returns:
  76. dict: 包含系统信息的字典
  77. """
  78. try:
  79. # 获取基本系统信息
  80. sys_info = {
  81. "os": {
  82. "name": platform.system(),
  83. "version": platform.version(),
  84. "platform": platform.platform(),
  85. },
  86. "python": {
  87. "version": platform.python_version(),
  88. "implementation": platform.python_implementation(),
  89. },
  90. "network": {
  91. "hostname": socket.gethostname(),
  92. "ip": socket.gethostbyname(socket.gethostname()),
  93. },
  94. "resources": {
  95. "cpu": {
  96. "cores": psutil.cpu_count(logical=False),
  97. "logical_cores": psutil.cpu_count(logical=True),
  98. "usage_percent": psutil.cpu_percent(interval=0.1),
  99. },
  100. "memory": {
  101. "total": _format_bytes(psutil.virtual_memory().total),
  102. "available": _format_bytes(psutil.virtual_memory().available),
  103. "used": _format_bytes(psutil.virtual_memory().used),
  104. "percent": psutil.virtual_memory().percent,
  105. },
  106. "disk": {
  107. "total": _format_bytes(psutil.disk_usage("/").total),
  108. "used": _format_bytes(psutil.disk_usage("/").used),
  109. "free": _format_bytes(psutil.disk_usage("/").free),
  110. "percent": psutil.disk_usage("/").percent,
  111. },
  112. },
  113. "application": {
  114. "environment": current_app.config["FLASK_ENV"],
  115. "debug_mode": current_app.config["DEBUG"],
  116. "port": current_app.config["PORT"],
  117. "platform": current_app.config["PLATFORM"],
  118. "bucket_name": current_app.config["BUCKET_NAME"],
  119. "prefix": current_app.config["PREFIX"],
  120. # 不返回敏感信息如密码、密钥等
  121. },
  122. }
  123. return sys_info
  124. except Exception as e:
  125. logger.error(f"获取系统信息失败: {str(e)}")
  126. return {"error": str(e)}
  127. def _format_bytes(bytes_value):
  128. """
  129. 将字节数格式化为易读形式
  130. Args:
  131. bytes_value: 字节数
  132. Returns:
  133. str: 格式化后的字符串,如"1.23 GB"
  134. """
  135. for unit in ["B", "KB", "MB", "GB", "TB"]:
  136. if bytes_value < 1024 or unit == "TB":
  137. return f"{bytes_value:.2f} {unit}"
  138. bytes_value /= 1024