Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 
 
 

125 righe
3.3 KiB

  1. # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
  2. # License: MIT. See LICENSE
  3. import json
  4. import os
  5. import traceback
  6. import uuid
  7. from datetime import datetime
  8. import rq
  9. import frappe
  10. MONITOR_REDIS_KEY = "monitor-transactions"
  11. MONITOR_MAX_ENTRIES = 1000000
  12. def start(transaction_type="request", method=None, kwargs=None):
  13. if frappe.conf.monitor:
  14. frappe.local.monitor = Monitor(transaction_type, method, kwargs)
  15. def stop(response=None):
  16. if hasattr(frappe.local, "monitor"):
  17. frappe.local.monitor.dump(response)
  18. def add_data_to_monitor(**kwargs) -> None:
  19. """Add additional custom key-value pairs along with monitor log.
  20. Note: Key-value pairs should be simple JSON exportable types."""
  21. if hasattr(frappe.local, "monitor"):
  22. frappe.local.monitor.add_custom_data(**kwargs)
  23. def log_file():
  24. return os.path.join(frappe.utils.get_bench_path(), "logs", "monitor.json.log")
  25. class Monitor:
  26. __slots__ = ("data",)
  27. def __init__(self, transaction_type, method, kwargs):
  28. try:
  29. self.data = frappe._dict(
  30. {
  31. "site": frappe.local.site,
  32. "timestamp": datetime.utcnow(),
  33. "transaction_type": transaction_type,
  34. "uuid": str(uuid.uuid4()),
  35. }
  36. )
  37. if transaction_type == "request":
  38. self.collect_request_meta()
  39. else:
  40. self.collect_job_meta(method, kwargs)
  41. except Exception:
  42. traceback.print_exc()
  43. def collect_request_meta(self):
  44. self.data.request = frappe._dict(
  45. {
  46. "ip": frappe.local.request_ip,
  47. "method": frappe.request.method,
  48. "path": frappe.request.path,
  49. }
  50. )
  51. def collect_job_meta(self, method, kwargs):
  52. self.data.job = frappe._dict({"method": method, "scheduled": False, "wait": 0})
  53. if "run_scheduled_job" in method:
  54. self.data.job.method = kwargs["job_type"]
  55. self.data.job.scheduled = True
  56. job = rq.get_current_job()
  57. if job:
  58. self.data.uuid = job.id
  59. waitdiff = self.data.timestamp - job.enqueued_at
  60. self.data.job.wait = int(waitdiff.total_seconds() * 1000000)
  61. def add_custom_data(self, **kwargs):
  62. if self.data:
  63. self.data.update(kwargs)
  64. def dump(self, response=None):
  65. try:
  66. timediff = datetime.utcnow() - self.data.timestamp
  67. # Obtain duration in microseconds
  68. self.data.duration = int(timediff.total_seconds() * 1000000)
  69. if self.data.transaction_type == "request":
  70. self.data.request.status_code = response.status_code
  71. self.data.request.response_length = int(response.headers.get("Content-Length", 0))
  72. if hasattr(frappe.local, "rate_limiter"):
  73. limiter = frappe.local.rate_limiter
  74. self.data.request.counter = limiter.counter
  75. if limiter.rejected:
  76. self.data.request.reset = limiter.reset
  77. self.store()
  78. except Exception:
  79. traceback.print_exc()
  80. def store(self):
  81. if frappe.cache().llen(MONITOR_REDIS_KEY) > MONITOR_MAX_ENTRIES:
  82. frappe.cache().ltrim(MONITOR_REDIS_KEY, 1, -1)
  83. serialized = json.dumps(self.data, sort_keys=True, default=str, separators=(",", ":"))
  84. frappe.cache().rpush(MONITOR_REDIS_KEY, serialized)
  85. def flush():
  86. try:
  87. # Fetch all the logs without removing from cache
  88. logs = frappe.cache().lrange(MONITOR_REDIS_KEY, 0, -1)
  89. if logs:
  90. logs = list(map(frappe.safe_decode, logs))
  91. with open(log_file(), "a", os.O_NONBLOCK) as f:
  92. f.write("\n".join(logs))
  93. f.write("\n")
  94. # Remove fetched entries from cache
  95. frappe.cache().ltrim(MONITOR_REDIS_KEY, len(logs) - 1, -1)
  96. except Exception:
  97. traceback.print_exc()