Skip to content

Commit

Permalink
Put EXPLAIN queries behind a config, defaulting to false
Browse files Browse the repository at this point in the history
  • Loading branch information
Simon Davy committed Jan 9, 2020
1 parent a5c4278 commit 795d171
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 8 deletions.
11 changes: 11 additions & 0 deletions talisker/config.py
Expand Up @@ -129,6 +129,7 @@ class Config():
'TALISKER_NETWORKS': [],
'TALISKER_ID_HEADER': 'X-Request-Id',
'TALISKER_DEADLINE_HEADER': 'X-Request-Deadline',
'TALISKER_EXPLAIN_SQL': False,
}

Metadata = collections.namedtuple(
Expand Down Expand Up @@ -263,6 +264,16 @@ def slowquery_threshold(self, raw_name):
"""
return force_int(self[raw_name])

@config_property('TALISKER_EXPLAIN_SQL')
def explain_sql(self, raw_name):
"""Include EXPLAIN plans in sql sentry breadcrumbs. Defaults to false.
When enabled, this will issue extra queries to the db when generating
sentry reports. The EXPLAIN queries will be quick to execute, as it
doesn't actually run the query, but still, caution is advised.
"""
return self.is_active(raw_name)

@config_property('TALISKER_SOFT_REQUEST_TIMEOUT')
def soft_request_timeout(self, raw_name):
"""Set the threshold (in ms) over which WSGI requests will report a
Expand Down
22 changes: 15 additions & 7 deletions talisker/postgresql.py
Expand Up @@ -71,6 +71,7 @@ def prettify_sql(sql):
class TaliskerConnection(connection):
_logger = None
_threshold = None
_explain = None
_safe_dsn = None
_safe_dsn_format = '{user}@{host}:{port}/{dbname}'

Expand Down Expand Up @@ -99,6 +100,12 @@ def query_threshold(self):
self._threshold = talisker.get_config().slowquery_threshold
return self._threshold

@property
def explain_breadcrumbs(self):
if self._explain is None:
self._explain = talisker.get_config().explain_sql
return self._explain

def cursor(self, *args, **kwargs):
kwargs.setdefault('cursor_factory', TaliskerCursor)
return super().cursor(*args, **kwargs)
Expand Down Expand Up @@ -129,13 +136,14 @@ def _record(self, msg, query, vars, duration, extra={}):

def processor(data):
qdata['query'] = self._format_query(query, vars)
try:
cursor = base_connection.cursor()
cursor.execute('EXPLAIN ' + query, vars)
plan = '\n'.join(l[0] for l in cursor.fetchall())
qdata['plan'] = plan
except Exception as e:
qdata['plan'] = 'could not explain query: ' + str(e)
if self.explain_breadcrumbs:
try:
cursor = base_connection.cursor()
cursor.execute('EXPLAIN ' + query, vars)
plan = '\n'.join(l[0] for l in cursor.fetchall())
qdata['plan'] = plan
except Exception as e:
qdata['plan'] = 'could not explain query: ' + str(e)

data['data'].update(qdata)

Expand Down
6 changes: 6 additions & 0 deletions tests/test_config.py
Expand Up @@ -61,6 +61,7 @@ def test_config_defaults():
debuglog=None,
colour=False,
slowquery_threshold=-1,
explain_sql=False,
soft_request_timeout=-1,
request_timeout=None,
logstatus=False,
Expand Down Expand Up @@ -127,6 +128,11 @@ def test_query_threshold_config():
assert msg == "'garbage' is not a valid integer"


def test_explain_sql_config():
assert_config({'TALISKER_EXPLAIN_SQL': '1'}, explain_sql=True)
assert_config({'TALISKER_EXPLAIN_SQL': 'garbage'}, explain_sql=False)


def test_request_timeout_config():
assert_config(
{'TALISKER_SOFT_REQUEST_TIMEOUT': '3000'}, soft_request_timeout=3000)
Expand Down
3 changes: 2 additions & 1 deletion tests/test_sentry.py
Expand Up @@ -301,10 +301,11 @@ def test_sql_summary_crumb():


@require_module('psycopg2')
def test_sql_crumbs_functional(request, postgresql, context):
def test_sql_crumbs_functional(request, postgresql, context, config):
import psycopg2
from talisker.postgresql import TaliskerConnection

config['TALISKER_EXPLAIN_SQL'] = '1'
table = request.function.__name__

with psycopg2.connect(postgresql.dsn) as ddl:
Expand Down

0 comments on commit 795d171

Please sign in to comment.