observability_routes.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. """HTTP boundary for SLO monitoring and evidence-bound data incidents."""
  2. from __future__ import annotations
  3. from flask import g, jsonify, request
  4. from app import db
  5. from app.api.data_factory import bp
  6. from app.models.result import failed, success
  7. def get_data_observability_service():
  8. from app.core.events.data_observability import DataObservabilityService
  9. from app.core.events.data_observability_repository import (
  10. SqlAlchemyDataObservabilityRepository,
  11. )
  12. return DataObservabilityService(
  13. SqlAlchemyDataObservabilityRepository(db.session),
  14. commit=db.session.commit,
  15. rollback=db.session.rollback,
  16. )
  17. def _actor_uid():
  18. identity = getattr(g, "current_user", {}) or {}
  19. return identity.get("id") or identity.get("sub")
  20. def _payload():
  21. value = request.get_json(silent=True)
  22. if not isinstance(value, dict):
  23. raise ValueError("request body must be an object")
  24. return value
  25. def _error(error):
  26. db.session.rollback()
  27. if isinstance(error, LookupError):
  28. return jsonify(failed(str(error), code=404)), 404
  29. if isinstance(error, ValueError):
  30. return jsonify(failed(str(error), code=400)), 400
  31. if isinstance(error, RuntimeError):
  32. return jsonify(failed(str(error), code=409)), 409
  33. return jsonify(failed("数据可观测与事故请求处理失败", code=500)), 500
  34. @bp.get("/observability/overview")
  35. def get_observability_overview():
  36. try:
  37. return jsonify(success(get_data_observability_service().overview())), 200
  38. except Exception as error:
  39. return _error(error)
  40. @bp.get("/observability/slos")
  41. def list_slo_policies():
  42. try:
  43. return jsonify(success(get_data_observability_service().list_slos())), 200
  44. except Exception as error:
  45. return _error(error)
  46. @bp.post("/observability/slos")
  47. def create_slo_policy():
  48. try:
  49. record = get_data_observability_service().create_slo(
  50. _payload(), actor_uid=_actor_uid()
  51. )
  52. return jsonify(success(record)), 201
  53. except Exception as error:
  54. return _error(error)
  55. @bp.post("/observability/collect")
  56. def collect_observability_signals():
  57. try:
  58. _payload()
  59. record = get_data_observability_service().collect(
  60. actor_uid=_actor_uid()
  61. )
  62. return jsonify(success(record)), 200
  63. except Exception as error:
  64. return _error(error)
  65. @bp.get("/observability/alerts")
  66. def list_observability_alerts():
  67. try:
  68. return jsonify(
  69. success(get_data_observability_service().list_alerts())
  70. ), 200
  71. except Exception as error:
  72. return _error(error)
  73. @bp.post("/observability/alerts/<alert_uid>/suppress")
  74. def suppress_observability_alert(alert_uid):
  75. try:
  76. payload = _payload()
  77. record = get_data_observability_service().suppress_alert(
  78. alert_uid,
  79. until=payload.get("until"),
  80. reason=payload.get("reason"),
  81. actor_uid=_actor_uid(),
  82. )
  83. return jsonify(success(record)), 200
  84. except Exception as error:
  85. return _error(error)
  86. @bp.post("/observability/alerts/<alert_uid>/acknowledge-delivery")
  87. def acknowledge_observability_alert_delivery(alert_uid):
  88. try:
  89. payload = _payload()
  90. record = get_data_observability_service().acknowledge_delivery(
  91. alert_uid,
  92. channel=payload.get("channel"),
  93. receipt_id=payload.get("receipt_id"),
  94. actor_uid=_actor_uid(),
  95. )
  96. return jsonify(success(record)), 200
  97. except Exception as error:
  98. return _error(error)
  99. @bp.get("/observability/incidents")
  100. def list_data_incidents():
  101. try:
  102. return jsonify(
  103. success(get_data_observability_service().list_incidents())
  104. ), 200
  105. except Exception as error:
  106. return _error(error)
  107. @bp.get("/observability/incidents/<incident_uid>")
  108. def get_data_incident(incident_uid):
  109. try:
  110. return jsonify(
  111. success(
  112. get_data_observability_service().incident_detail(incident_uid)
  113. )
  114. ), 200
  115. except Exception as error:
  116. return _error(error)
  117. @bp.post("/observability/incidents/<incident_uid>/escalate")
  118. def escalate_data_incident(incident_uid):
  119. try:
  120. payload = _payload()
  121. record = get_data_observability_service().escalate_incident(
  122. incident_uid,
  123. reason=payload.get("reason"),
  124. actor_uid=_actor_uid(),
  125. )
  126. return jsonify(success(record)), 200
  127. except Exception as error:
  128. return _error(error)
  129. @bp.post("/observability/incidents/<incident_uid>/close")
  130. def close_data_incident(incident_uid):
  131. try:
  132. record = get_data_observability_service().close_incident(
  133. incident_uid,
  134. _payload(),
  135. actor_uid=_actor_uid(),
  136. )
  137. return jsonify(success(record)), 200
  138. except Exception as error:
  139. return _error(error)