0001-logs-better-queryset-to-search-for-logs-48074.patch
passerelle/base/migrations/0025_transaction_id.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import migrations, models |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('base', '0024_auto_20201103_1256'), |
|
11 |
] |
|
12 | ||
13 |
operations = [ |
|
14 |
migrations.AddField( |
|
15 |
model_name='resourcelog', |
|
16 |
name='transaction_id', |
|
17 |
field=models.UUIDField(null=True), |
|
18 |
), |
|
19 |
] |
passerelle/base/migrations/0026_transaction_id.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import migrations |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('base', '0025_transaction_id'), |
|
11 |
] |
|
12 | ||
13 |
operations = [ |
|
14 |
migrations.RunSQL( |
|
15 |
["UPDATE base_resourcelog SET transaction_id=(extra->>'transaction_id')::uuid"], |
|
16 |
reverse_sql=[] |
|
17 |
), |
|
18 |
] |
passerelle/base/migrations/0027_transaction_id.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import migrations, models |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('base', '0026_transaction_id'), |
|
11 |
] |
|
12 | ||
13 |
operations = [ |
|
14 |
migrations.AlterField( |
|
15 |
model_name='resourcelog', |
|
16 |
name='transaction_id', |
|
17 |
field=models.UUIDField(db_index=True, null=True), |
|
18 |
), |
|
19 |
] |
passerelle/base/models.py | ||
---|---|---|
799 | 799 |
sourceip = models.GenericIPAddressField(blank=True, null=True, verbose_name=_('Source IP')) |
800 | 800 |
message = models.TextField(verbose_name='message') |
801 | 801 |
extra = JSONField(verbose_name='extras', default={}) |
802 |
transaction_id = models.UUIDField(null=True, db_index=True) |
|
802 | 803 | |
803 | 804 |
class Meta: |
804 | 805 |
ordering = ('id',) |
... | ... | |
910 | 911 |
attr['extra'] = {key: value for key, value in extra.items() if is_json_serializable(value)} |
911 | 912 | |
912 | 913 |
if self.transaction_id: |
914 |
attr['transaction_id'] = self.transaction_id |
|
913 | 915 |
attr['extra']['transaction_id'] = self.transaction_id |
914 | 916 | |
915 | 917 |
if getattr(request, 'META', None): |
passerelle/templates/passerelle/manage/log.html | ||
---|---|---|
29 | 29 |
{% endfor %} |
30 | 30 |
</table> |
31 | 31 | |
32 |
{% if logline.extra.transaction_id %}
|
|
32 |
{% if logline.transaction_id %} |
|
33 | 33 |
<div class="buttons"> |
34 |
<a class="button" href="{% url 'view-logs-connector' connector=object.get_connector_slug slug=object.slug %}?q={{logline.extra.transaction_id}}">{% trans "Search for logs from the same call" %}</a>
|
|
34 |
<a class="button" href="{% url 'view-logs-connector' connector=object.get_connector_slug slug=object.slug %}?q={{logline.transaction_id}}">{% trans "Search for logs from the same call" %}</a> |
|
35 | 35 |
</div> |
36 | 36 |
{% endif %} |
37 | 37 |
passerelle/views.py | ||
---|---|---|
19 | 19 |
import inspect |
20 | 20 |
import json |
21 | 21 |
import logging |
22 |
import uuid |
|
22 | 23 | |
23 | 24 |
from django.apps import apps |
24 | 25 |
from django.conf.urls import url |
... | ... | |
27 | 28 |
from django.contrib.auth import logout as auth_logout |
28 | 29 |
from django.contrib.auth import views as auth_views |
29 | 30 |
from django.db import transaction |
30 |
from django.db.models import TextField, Q |
|
31 |
from django.db.models.functions import Cast |
|
31 |
from django.db.models import Q |
|
32 | 32 |
from django.http import HttpResponse, HttpResponseRedirect, Http404 |
33 | 33 |
from django.views.decorators.csrf import csrf_exempt |
34 | 34 |
from django.views.generic import ( |
... | ... | |
249 | 249 |
try: |
250 | 250 |
date = date_parser.parse(query, dayfirst=True) |
251 | 251 |
except Exception: |
252 |
qs = qs.annotate( |
|
253 |
text_extra=Cast('extra', TextField()) |
|
254 |
).filter(Q(text_extra__icontains=query) | Q(message__icontains=query)) |
|
252 |
query_uuid = None |
|
253 |
try: |
|
254 |
query_uuid = uuid.UUID(query) |
|
255 |
except ValueError: |
|
256 |
pass |
|
257 |
else: |
|
258 |
qs = qs.filter(transaction_id=query_uuid) |
|
259 |
if query_uuid is None or query_uuid is not None and not qs.exists(): |
|
260 |
qs = qs.filter(Q(message__icontains=query) | Q(extra__icontains=query)) |
|
255 | 261 |
else: |
256 | 262 |
date = make_aware(date) |
257 | 263 |
if date.hour == 0 and date.minute == 0 and date.second == 0: |
tests/test_manager.py | ||
---|---|---|
1 | 1 |
import datetime |
2 | 2 |
import re |
3 |
import uuid |
|
3 | 4 | |
4 | 5 |
from webtest import Upload |
5 | 6 | |
... | ... | |
247 | 248 |
assert 'title="Level 42 - Oct. 6, 2020 14:08:12"' in resp.text |
248 | 249 | |
249 | 250 | |
250 |
def test_logs_search_in_message(app, admin_user):
|
|
251 |
def test_logs_search(app, admin_user): |
|
251 | 252 |
csv = CsvDataSource.objects.create(csv_file=File(StringIO('1;t\n'), 't.csv'), slug='t', title='t') |
252 | 253 |
app = login(app) |
253 | 254 | |
254 |
ResourceLog.objects.create(appname=csv.get_connector_slug(), slug=csv.slug, levelno=1, message='hop') |
|
255 |
resp = app.get(csv.get_absolute_url()) |
|
256 |
resp = resp.click('full page') |
|
257 |
assert resp.text.count('<td class="timestamp">') == 1 |
|
255 |
transaction_id = str(uuid.uuid4()) |
|
256 |
log1 = ResourceLog.objects.create( |
|
257 |
appname=csv.get_connector_slug(), slug=csv.slug, levelno=1, message='hop', |
|
258 |
transaction_id=transaction_id, |
|
259 |
extra={'transaction_id': transaction_id, 'foo': 'plop'}) |
|
260 |
log2 = ResourceLog.objects.create( |
|
261 |
appname=csv.get_connector_slug(), slug=csv.slug, levelno=1, message='plop', |
|
262 |
transaction_id=transaction_id, |
|
263 |
extra={'bar': 'hop'}) |
|
264 |
log3 = ResourceLog.objects.create( |
|
265 |
appname=csv.get_connector_slug(), slug=csv.slug, levelno=1, message='foo', |
|
266 |
extra={'bar': 'hop'}) |
|
267 | ||
268 |
resp = app.get('/manage/csvdatasource/t/logs/') |
|
269 |
assert list(resp.context['page_obj'].object_list) == [log3, log2, log1] |
|
270 | ||
271 |
resp.form['q'] = transaction_id |
|
272 |
resp = resp.form.submit() |
|
273 |
assert list(resp.context['page_obj'].object_list) == [log2, log1] |
|
274 | ||
275 |
resp.form['q'] = str(uuid.uuid4()) |
|
276 |
resp = resp.form.submit() |
|
277 |
assert list(resp.context['page_obj'].object_list) == [] |
|
258 | 278 | |
259 | 279 |
resp.form['q'] = 'not there' |
260 | 280 |
resp = resp.form.submit() |
261 |
assert resp.text.count('<td class="timestamp">') == 0
|
|
281 |
assert list(resp.context['page_obj'].object_list) == []
|
|
262 | 282 | |
263 | 283 |
resp.form['q'] = 'hop' |
264 | 284 |
resp = resp.form.submit() |
265 |
assert resp.text.count('<td class="timestamp">') == 1 |
|
285 |
assert list(resp.context['page_obj'].object_list) == [log3, log2, log1] |
|
286 | ||
287 |
resp.form['q'] = 'plop' |
|
288 |
resp = resp.form.submit() |
|
289 |
assert list(resp.context['page_obj'].object_list) == [log2, log1] |
|
290 | ||
291 |
resp.form['q'] = 'foo' |
|
292 |
resp = resp.form.submit() |
|
293 |
assert list(resp.context['page_obj'].object_list) == [log3, log1] |
|
266 | 294 | |
267 | 295 | |
268 | 296 |
def test_logging_parameters(app, admin_user): |
269 |
- |