Projet

Général

Profil

0001-logs-better-queryset-to-search-for-logs-48074.patch

Lauréline Guérin, 12 novembre 2020 12:04

Télécharger (8,78 ko)

Voir les différences:

Subject: [PATCH 1/2] logs: better queryset to search for logs (#48074)

 .../base/migrations/0025_transaction_id.py    | 19 +++++++++
 .../base/migrations/0026_transaction_id.py    | 18 ++++++++
 .../base/migrations/0027_transaction_id.py    | 19 +++++++++
 passerelle/base/models.py                     |  2 +
 .../templates/passerelle/manage/log.html      |  4 +-
 passerelle/views.py                           | 16 ++++---
 tests/test_manager.py                         | 42 +++++++++++++++----
 7 files changed, 106 insertions(+), 14 deletions(-)
 create mode 100644 passerelle/base/migrations/0025_transaction_id.py
 create mode 100644 passerelle/base/migrations/0026_transaction_id.py
 create mode 100644 passerelle/base/migrations/0027_transaction_id.py
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
-