summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavierDo <xdo@odoo.com>2018-09-06 15:33:25 +0200
committerXavierDo <xdo@odoo.com>2018-09-07 10:27:46 +0200
commitc44fe912329e2d4c7541ed810526d4a069f930c4 (patch)
treeafb963429dc3a6e5d05899231421b8d37a155808
parent2f033d718456d8698441620dace4647e96e99931 (diff)
[IMP] http,server: add query count and request times in logs
When there is a performance issue, it's sometimes difficult to discover which request increased the query count or its duration. With this commit, the query count, the query time and "python and io" time are displayed in the logs at the end of each werkzeug request line. Co-authored-by: Christophe Monniez <moc@odoo.com>
-rw-r--r--odoo/http.py3
-rw-r--r--odoo/netsvc.py46
-rw-r--r--odoo/sql_db.py9
3 files changed, 52 insertions, 6 deletions
diff --git a/odoo/http.py b/odoo/http.py
index 5ecb6a0c211..3ae800cc71b 100644
--- a/odoo/http.py
+++ b/odoo/http.py
@@ -1438,6 +1438,9 @@ class Root(object):
httprequest.app = self
httprequest.parameter_storage_class = werkzeug.datastructures.ImmutableOrderedMultiDict
threading.current_thread().url = httprequest.url
+ threading.current_thread().query_count = 0
+ threading.current_thread().query_time = 0
+ threading.current_thread().perf_t0 = time.time()
explicit_session = self.setup_session(httprequest)
self.setup_db(httprequest)
diff --git a/odoo/netsvc.py b/odoo/netsvc.py
index c8d9dbb6441..f344ec28048 100644
--- a/odoo/netsvc.py
+++ b/odoo/netsvc.py
@@ -9,6 +9,7 @@ import pprint
from . import release
import sys
import threading
+import time
import psycopg2
@@ -69,10 +70,40 @@ LEVEL_COLOR_MAPPING = {
logging.CRITICAL: (WHITE, RED),
}
+class PerfFilter(logging.Filter):
+ def format_perf(self, query_count, query_time, remaining_time):
+ return ("%d" % query_count, "%.3f" % query_time, "%.3f" % remaining_time)
+
+ def filter(self, record):
+ if hasattr(threading.current_thread(), "query_count"):
+ query_count = threading.current_thread().query_count
+ query_time = threading.current_thread().query_time
+ perf_t0 = threading.current_thread().perf_t0
+ remaining_time = time.time() - perf_t0 - query_time
+ record.perf_info = '%s %s %s' % self.format_perf(query_count, query_time, remaining_time)
+ delattr(threading.current_thread(), "query_count")
+ else:
+ record.perf_info = "- - -"
+ return True
+
+class ColoredPerfFilter(PerfFilter):
+ def format_perf(self, query_count, query_time, remaining_time):
+ def colorize_time(time, format, low=1, high=5):
+ if time > high:
+ return COLOR_PATTERN % (30 + RED, 40 + DEFAULT, format % time)
+ if time > low:
+ return COLOR_PATTERN % (30 + YELLOW, 40 + DEFAULT, format % time)
+ return format % time
+ return (
+ colorize_time(query_time, "%.3f", 100, 1000),
+ colorize_time(query_time, "%.3f", 0.1, 3),
+ colorize_time(remaining_time, "%.3f", 1, 5)
+ )
+
class DBFormatter(logging.Formatter):
def format(self, record):
record.pid = os.getpid()
- record.dbname = getattr(threading.currentThread(), 'dbname', '?')
+ record.dbname = getattr(threading.current_thread(), 'dbname', '?')
return logging.Formatter.format(self, record)
class ColoredFormatter(DBFormatter):
@@ -88,6 +119,13 @@ def init_logger():
return
_logger_init = True
+ old_factory = logging.getLogRecordFactory()
+ def record_factory(*args, **kwargs):
+ record = old_factory(*args, **kwargs)
+ record.perf_info = ""
+ return record
+ logging.setLogRecordFactory(record_factory)
+
logging.addLevelName(25, "INFO")
logging.captureWarnings(True)
@@ -95,7 +133,7 @@ def init_logger():
resetlocale()
# create a format for log messages and dates
- format = '%(asctime)s %(pid)s %(levelname)s %(dbname)s %(name)s: %(message)s'
+ format = '%(asctime)s %(pid)s %(levelname)s %(dbname)s %(name)s: %(message)s %(perf_info)s'
# Normal Handler on stderr
handler = logging.StreamHandler()
@@ -143,11 +181,13 @@ def init_logger():
if os.name == 'posix' and isinstance(handler, logging.StreamHandler) and is_a_tty(handler.stream):
formatter = ColoredFormatter(format)
+ perf_filter = ColoredPerfFilter()
else:
formatter = DBFormatter(format)
+ perf_filter = PerfFilter()
handler.setFormatter(formatter)
-
logging.getLogger().addHandler(handler)
+ logging.getLogger('werkzeug').addFilter(perf_filter)
if tools.config['log_db']:
db_levels = {
diff --git a/odoo/sql_db.py b/odoo/sql_db.py
index ae1db0d6436..afa396148f0 100644
--- a/odoo/sql_db.py
+++ b/odoo/sql_db.py
@@ -217,10 +217,9 @@ class Cursor(object):
raise ValueError("SQL query parameters should be a tuple, list or dict; got %r" % (params,))
if self.sql_log:
- now = time.time()
encoding = psycopg2.extensions.encodings[self.connection.encoding]
_logger.debug("query: %s", self._obj.mogrify(query, params).decode(encoding, 'replace'))
-
+ now = time.time()
try:
params = params or None
res = self._obj.execute(query, params)
@@ -231,10 +230,14 @@ class Cursor(object):
# simple query count is always computed
self.sql_log_count += 1
+ delay = (time.time() - now)
+ if hasattr(threading.current_thread(), 'query_count'):
+ threading.current_thread().query_count += 1
+ threading.current_thread().query_time += delay
# advanced stats only if sql_log is enabled
if self.sql_log:
- delay = (time.time() - now) * 1E6
+ delay *= 1E6
res_from = re_from.match(query.lower())
if res_from: