From 3c183344aa501cce26b2f5df33db1d321fcc1135 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 1 Feb 2024 22:16:06 +0530 Subject: [PATCH] feat: profile requests using recorder WIP: - [x] Basic working feature - [ ] Make this optional, this has insanely high overhead. - [ ] Specify requests/function filter to profile/record. This will allow better recording in production sites. - [ ] Make SQL profiling optional too --- frappe/core/doctype/recorder/recorder.json | 16 ++++++++++++++-- frappe/core/doctype/recorder/recorder.py | 1 + frappe/recorder.py | 14 ++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/recorder/recorder.json b/frappe/core/doctype/recorder/recorder.json index 391f808f31..72291dbfe2 100644 --- a/frappe/core/doctype/recorder/recorder.json +++ b/frappe/core/doctype/recorder/recorder.json @@ -20,7 +20,9 @@ "section_break_sgro", "form_dict", "section_break_9jhm", - "sql_queries" + "sql_queries", + "section_break_optn", + "profile" ], "fields": [ { @@ -107,6 +109,16 @@ "fieldtype": "Data", "hidden": 1, "label": "Event Type" + }, + { + "fieldname": "section_break_optn", + "fieldtype": "Section Break" + }, + { + "fieldname": "profile", + "fieldtype": "Code", + "label": "cProfile Output", + "read_only": 1 } ], "hide_toolbar": 1, @@ -114,7 +126,7 @@ "index_web_pages_for_search": 1, "is_virtual": 1, "links": [], - "modified": "2024-01-03 16:45:47.110048", + "modified": "2024-02-01 22:13:26.505174", "modified_by": "Administrator", "module": "Core", "name": "Recorder", diff --git a/frappe/core/doctype/recorder/recorder.py b/frappe/core/doctype/recorder/recorder.py index 347a237743..26ccfcf378 100644 --- a/frappe/core/doctype/recorder/recorder.py +++ b/frappe/core/doctype/recorder/recorder.py @@ -24,6 +24,7 @@ class Recorder(Document): method: DF.Literal["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"] number_of_queries: DF.Int path: DF.Data | None + profile: DF.Code | None request_headers: DF.Code | None sql_queries: DF.Table[RecorderQuery] time: DF.Datetime | None diff --git a/frappe/recorder.py b/frappe/recorder.py index 3094f83ad6..bb47304fe5 100644 --- a/frappe/recorder.py +++ b/frappe/recorder.py @@ -1,9 +1,12 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE +import cProfile import datetime import functools import inspect +import io import json +import pstats import re import time from collections import Counter @@ -148,6 +151,8 @@ class Recorder: self.uuid = frappe.generate_hash(length=10) self.time = datetime.datetime.now() self.calls = [] + self.profiler = cProfile.Profile() + self.profiler.enable() if frappe.request: self.path = frappe.request.path self.cmd = frappe.local.form_dict.cmd or "" @@ -176,6 +181,13 @@ class Recorder: self.calls.append(data) def dump(self): + self.profiler.disable() + + profiler_output = io.StringIO() + pstats.Stats(self.profiler, stream=profiler_output).strip_dirs().sort_stats( + "cumulative" + ).print_stats() + request_data = { "uuid": self.uuid, "path": self.path, @@ -197,6 +209,8 @@ class Recorder: request_data["calls"] = self.calls request_data["headers"] = self.headers request_data["form_dict"] = self.form_dict + request_data["profile"] = profiler_output.getvalue() + profiler_output.close() frappe.cache.hset(RECORDER_REQUEST_HASH, self.uuid, request_data)