Commit af131a3c by Harry Marr

Refactor tracker, add stack traces to queries

parent 330f1177
...@@ -2,7 +2,7 @@ from django.template.loader import render_to_string ...@@ -2,7 +2,7 @@ from django.template.loader import render_to_string
from debug_toolbar.panels import DebugPanel from debug_toolbar.panels import DebugPanel
from operation_tracker import MongoOperationTracker import operation_tracker
class MongoDebugPanel(DebugPanel): class MongoDebugPanel(DebugPanel):
...@@ -13,23 +13,18 @@ class MongoDebugPanel(DebugPanel): ...@@ -13,23 +13,18 @@ class MongoDebugPanel(DebugPanel):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(self.__class__, self).__init__(*args, **kwargs) super(self.__class__, self).__init__(*args, **kwargs)
self.op_tracker = MongoOperationTracker() operation_tracker.install_tracker()
self.op_tracker.install()
def process_request(self, request): def process_request(self, request):
self.op_tracker.reset() operation_tracker.reset()
self.op_tracker.start()
def process_response(self, request, response):
self.op_tracker.stop()
def nav_title(self): def nav_title(self):
return 'MongoDB' return 'MongoDB'
def nav_subtitle(self): def nav_subtitle(self):
num_queries = len(self.op_tracker.queries) num_queries = len(operation_tracker.queries)
attrs = ['queries', 'inserts', 'updates', 'removes'] attrs = ['queries', 'inserts', 'updates', 'removes']
total_time = sum(sum(o['time'] for o in getattr(self.op_tracker, a)) total_time = sum(sum(o['time'] for o in getattr(operation_tracker, a))
for a in attrs) for a in attrs)
return '{0} operations in {1:.2f}ms'.format(num_queries, total_time) return '{0} operations in {1:.2f}ms'.format(num_queries, total_time)
...@@ -41,10 +36,10 @@ class MongoDebugPanel(DebugPanel): ...@@ -41,10 +36,10 @@ class MongoDebugPanel(DebugPanel):
def content(self): def content(self):
context = self.context.copy() context = self.context.copy()
context['queries'] = self.op_tracker.queries context['queries'] = operation_tracker.queries
context['inserts'] = self.op_tracker.inserts context['inserts'] = operation_tracker.inserts
context['updates'] = self.op_tracker.updates context['updates'] = operation_tracker.updates
context['removes'] = self.op_tracker.removes context['removes'] = operation_tracker.removes
return render_to_string('mongo-panel.html', context) return render_to_string('mongo-panel.html', context)
...@@ -5,11 +5,23 @@ pre.mongo-highlight, pre.mongo-highlight span { ...@@ -5,11 +5,23 @@ pre.mongo-highlight, pre.mongo-highlight span {
font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', 'Monaco', 'Courier New', monospace !important; font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', 'Monaco', 'Courier New', monospace !important;
font-size: 1.05em !important; font-size: 1.05em !important;
} }
pre.mongo-stack-trace {
line-height: 2.3em !important;
}
table.mongo-stack-trace th {
text-align: left !important;
}
table.mongo-stack-trace td.code, table.mongo-stack-trace td.code span, table.mongo-stack-trace td.function {
font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', 'Monaco', 'Courier New', monospace !important;
}
table.mongo-op-table tbody {
width: 100% !important;
}
</style> </style>
<h4>Queries</h4> <h4>Queries</h4>
{% if queries %} {% if queries %}
<table> <table class="mongo-op-table">
<thead> <thead>
<tr> <tr>
<th>Time (ms)</th> <th>Time (ms)</th>
...@@ -19,6 +31,7 @@ pre.mongo-highlight, pre.mongo-highlight span { ...@@ -19,6 +31,7 @@ pre.mongo-highlight, pre.mongo-highlight span {
<th>Ordering</th> <th>Ordering</th>
<th>Skip</th> <th>Skip</th>
<th>Limit</th> <th>Limit</th>
<th>Stack Trace</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
...@@ -29,12 +42,38 @@ pre.mongo-highlight, pre.mongo-highlight span { ...@@ -29,12 +42,38 @@ pre.mongo-highlight, pre.mongo-highlight span {
<td>{{ query.collection }}</td> <td>{{ query.collection }}</td>
<td> <td>
{% if query.query %} {% if query.query %}
<pre class="mongo-highlight">{{ query.query|format_dict|highlight_json|safe }}</pre> <pre class="mongo-highlight">{{ query.query|format_dict|highlight:"javascript"|safe }}</pre>
{% endif %} {% endif %}
</td> </td>
<td><pre class="mongo-highlight">{% if query.ordering %}{{ query.ordering }}{% endif %}</pre></td> <td><pre class="mongo-highlight">{% if query.ordering %}{{ query.ordering }}{% endif %}</pre></td>
<td>{% if query.skip %}{{ query.skip }}{% endif %}</td> <td>{% if query.skip %}{{ query.skip }}{% endif %}</td>
<td>{% if query.limit %}{{ query.limit }}{% endif %}</td> <td>{% if query.limit %}{{ query.limit }}{% endif %}</td>
<td><a href="javascript:void(0);" class="mongo-toggle-trace" data-row="{{ forloop.counter }}">Toggle</a></td>
</tr>
<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">
<td colspan="8">
<table class="mongo-stack-trace "id="mongo-stack-trace-{{ forloop.counter }}">
<thead>
<tr>
<th>Line</th>
<th>File</th>
<th>Function</th>
<th>Code</th>
</tr>
</thead>
<tbody>
{% for line in query.stack_trace %}
<tr>
<td class="lineno">{{ line.1 }}</td>
<td class="file">{{ line.0 }}</td>
<td class="function">{{ line.2 }}</td>
<td class="code">{{ line.3|highlight:"python"|safe }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<!--<pre class="mongo-stack-trace">{{ query.stack_trace|format_stack_trace }}</pre>-->
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
...@@ -45,7 +84,7 @@ pre.mongo-highlight, pre.mongo-highlight span { ...@@ -45,7 +84,7 @@ pre.mongo-highlight, pre.mongo-highlight span {
<h4>Inserts</h4> <h4>Inserts</h4>
{% if inserts %} {% if inserts %}
<table> <table class="mongo-op-table">
<thead> <thead>
<tr> <tr>
<th>Time (ms)</th> <th>Time (ms)</th>
...@@ -58,7 +97,7 @@ pre.mongo-highlight, pre.mongo-highlight span { ...@@ -58,7 +97,7 @@ pre.mongo-highlight, pre.mongo-highlight span {
<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}"> <tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">
<td>{{ insert.time|floatformat:"4" }}</td> <td>{{ insert.time|floatformat:"4" }}</td>
<td> <td>
<pre class="mongo-highlight">{{ insert.document|format_dict:120|highlight_json|safe }}</pre> <pre class="mongo-highlight">{{ insert.document|format_dict:120|highlight:"javascript"|safe }}</pre>
</td> </td>
<td>{{ insert.safe }}</td> <td>{{ insert.safe }}</td>
</tr> </tr>
...@@ -71,7 +110,7 @@ pre.mongo-highlight, pre.mongo-highlight span { ...@@ -71,7 +110,7 @@ pre.mongo-highlight, pre.mongo-highlight span {
<h4>Removes</h4> <h4>Removes</h4>
{% if removes %} {% if removes %}
<table> <table class="mongo-op-table">
<thead> <thead>
<tr> <tr>
<th>Time (ms)</th> <th>Time (ms)</th>
...@@ -84,7 +123,7 @@ pre.mongo-highlight, pre.mongo-highlight span { ...@@ -84,7 +123,7 @@ pre.mongo-highlight, pre.mongo-highlight span {
<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}"> <tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">
<td>{{ remove.time|floatformat:"4" }}</td> <td>{{ remove.time|floatformat:"4" }}</td>
<td> <td>
<pre class="mongo-highlight">{{ remove.spec_or_id|format_dict:120|highlight_json|safe }}</pre> <pre class="mongo-highlight">{{ remove.spec_or_id|format_dict:120|highlight:"javascript"|safe }}</pre>
</td> </td>
<td>{{ remove.safe }}</td> <td>{{ remove.safe }}</td>
</tr> </tr>
...@@ -97,7 +136,7 @@ pre.mongo-highlight, pre.mongo-highlight span { ...@@ -97,7 +136,7 @@ pre.mongo-highlight, pre.mongo-highlight span {
<h4>Updates</h4> <h4>Updates</h4>
{% if updates %} {% if updates %}
<table> <table class="mongo-op-table">
<thead> <thead>
<tr> <tr>
<th>Time (ms)</th> <th>Time (ms)</th>
...@@ -113,10 +152,10 @@ pre.mongo-highlight, pre.mongo-highlight span { ...@@ -113,10 +152,10 @@ pre.mongo-highlight, pre.mongo-highlight span {
<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}"> <tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">
<td>{{ update.time|floatformat:"4" }}</td> <td>{{ update.time|floatformat:"4" }}</td>
<td> <td>
<pre class="mongo-highlight">{{ update.spec|format_dict:120|highlight_json|safe }}</pre> <pre class="mongo-highlight">{{ update.spec|format_dict:120|highlight:"javascript"|safe }}</pre>
</td> </td>
<td> <td>
<pre class="mongo-highlight">{{ update.document|format_dict:120|highlight_json|safe }}</pre> <pre class="mongo-highlight">{{ update.document|format_dict:120|highlight:"javascript"|safe }}</pre>
</td> </td>
<td>{{ update.safe }}</td> <td>{{ update.safe }}</td>
<td>{{ update.multi }}</td> <td>{{ update.multi }}</td>
...@@ -129,3 +168,24 @@ pre.mongo-highlight, pre.mongo-highlight span { ...@@ -129,3 +168,24 @@ pre.mongo-highlight, pre.mongo-highlight span {
<p>No updates recorded</p> <p>No updates recorded</p>
{% endif %} {% endif %}
<script>
(function() {
var toggleButtons = document.querySelectorAll('a.mongo-toggle-trace');
for (var i = 0; i < toggleButtons.length; i++) {
(function() {
var button = toggleButtons[i];
var index = button.dataset.row;
var trace = document.getElementById('mongo-stack-trace-' + index);
trace.style.display = "none";
button.addEventListener("click", function() {
if (trace.style.display == "none") {
trace.style.display = "";
} else {
trace.style.display = "none";
}
}, true);
})();
}
})();
</script>
from django import template from django import template
from django.utils.html import escape
from django.utils.safestring import mark_safe
import pprint import pprint
register = template.Library() register = template.Library()
@register.filter @register.filter
def format_stack_trace(value):
stack_trace = []
fmt = (
'<span class="path">{0}/</span>'
'<span class="file">{1}</span> in <span class="func">{3}</span>'
'(<span class="lineno">{2}</span>) <span class="code">{4}</span>'
)
for frame in value:
params = map(escape, frame[0].rsplit('/', 1) + list(frame[1:]))
stack_trace.append(fmt.format(*params))
return mark_safe('\n'.join(stack_trace))
@register.filter
def format_dict(value, width=60): def format_dict(value, width=60):
return pprint.pformat(value, width=int(width)) return pprint.pformat(value, width=int(width))
@register.filter @register.filter
def highlight_json(value): def highlight(value, language):
try: try:
from pygments import highlight from pygments import highlight
from pygments.lexers import JavascriptLexer from pygments.lexers import get_lexer_by_name
from pygments.formatters import HtmlFormatter from pygments.formatters import HtmlFormatter
except ImportError: except ImportError:
return value return value
# Can't use class-based colouring because the debug toolbar's css rules # Can't use class-based colouring because the debug toolbar's css rules
# are more specific so take precedence # are more specific so take precedence
formatter = HtmlFormatter(style='friendly', nowrap=True, noclasses=True) formatter = HtmlFormatter(style='friendly', nowrap=True, noclasses=True)
return highlight(value, JavascriptLexer(), formatter) return highlight(value, get_lexer_by_name(language), formatter)
...@@ -37,10 +37,10 @@ DEBUG_TOOLBAR_PANELS = ( ...@@ -37,10 +37,10 @@ DEBUG_TOOLBAR_PANELS = (
'debug_toolbar.panels.headers.HeaderDebugPanel', 'debug_toolbar.panels.headers.HeaderDebugPanel',
'debug_toolbar.panels.request_vars.RequestVarsDebugPanel', 'debug_toolbar.panels.request_vars.RequestVarsDebugPanel',
'debug_toolbar.panels.template.TemplateDebugPanel', 'debug_toolbar.panels.template.TemplateDebugPanel',
'debug_toolbar_mongo.panel.MongoDebugPanel',
'debug_toolbar.panels.sql.SQLDebugPanel', 'debug_toolbar.panels.sql.SQLDebugPanel',
'debug_toolbar.panels.signals.SignalDebugPanel', 'debug_toolbar.panels.signals.SignalDebugPanel',
'debug_toolbar.panels.logger.LoggingPanel', 'debug_toolbar.panels.logger.LoggingPanel',
'debug_toolbar_mongo.panel.MongoDebugPanel',
) )
ROOT_URLCONF = 'example.urls' ROOT_URLCONF = 'example.urls'
......
...@@ -8,7 +8,9 @@ conn = pymongo.Connection() ...@@ -8,7 +8,9 @@ conn = pymongo.Connection()
db = conn.debug_test db = conn.debug_test
def index(request): def index(request):
list(db.test.find({'name': 'test'})) #list(db.test.find({'name': 'test'}))
db.test.find({'name': 'test'}).count()
db.test.find({'name': 'test'}).count()
list(db.test.find({'name': 'test', 'age': {'$lt': 134234}}).skip(1)) list(db.test.find({'name': 'test', 'age': {'$lt': 134234}}).skip(1))
db.test.find({'name': 'test'}).count() db.test.find({'name': 'test'}).count()
db.test.find({'name': 'test'}).skip(1).count(with_limit_and_skip=True) db.test.find({'name': 'test'}).skip(1).count(with_limit_and_skip=True)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment