Projet

Général

Profil

0001-logging-manage-log_retention_days-log-parameters-474.patch

Nicolas Roche, 03 novembre 2020 12:37

Télécharger (9,76 ko)

Voir les différences:

Subject: [PATCH] logging: manage log_retention_days log parameters (#47426)

 ...23_loggingparameters_log_retention_days.py | 20 ++++++++++
 passerelle/base/models.py                     |  9 ++++-
 passerelle/base/views.py                      |  6 ++-
 tests/test_misc.py                            | 37 ++++++++++++++++++-
 4 files changed, 69 insertions(+), 3 deletions(-)
 create mode 100644 passerelle/base/migrations/0023_loggingparameters_log_retention_days.py
passerelle/base/migrations/0023_loggingparameters_log_retention_days.py
1
# -*- coding: utf-8 -*-
2
# Generated by Django 1.11.18 on 2020-11-03 11:16
3
from __future__ import unicode_literals
4

  
5
from django.db import migrations, models
6

  
7

  
8
class Migration(migrations.Migration):
9

  
10
    dependencies = [
11
        ('base', '0022_auto_20200715_1033'),
12
    ]
13

  
14
    operations = [
15
        migrations.AddField(
16
            model_name='loggingparameters',
17
            name='log_retention_days',
18
            field=models.PositiveIntegerField(blank=True, help_text='Number of days to keep logs', null=True, verbose_name='Log retention days'),
19
        ),
20
    ]
passerelle/base/models.py
444 444
                                '%s is unsupported' % (field, cls))
445 445
        instance.save()
446 446
        if 'log_level' in d:
447 447
            instance.set_log_level(d['log_level'])
448 448
        return instance
449 449

  
450 450
    def clean_logs(self):
451 451
        # clean logs
452
        timestamp = timezone.now() - datetime.timedelta(days=settings.LOG_RETENTION_DAYS)
452
        timestamp = timezone.now() - datetime.timedelta(
453
            days=self.logging_parameters.log_retention_days or settings.LOG_RETENTION_DAYS)
453 454
        ResourceLog.objects.filter(
454 455
                appname=self.get_connector_slug(),
455 456
                slug=self.slug,
456 457
                timestamp__lt=timestamp).delete()
457 458

  
458 459
    def check_status(self):
459 460
        # should raise an exception if status is not ok
460 461
        raise NotImplementedError
......
665 666
        help_text=_('Maximum HTTP request size to log'),
666 667
        default=settings.LOGGED_REQUESTS_MAX_SIZE
667 668
    )
668 669
    responses_max_size = models.PositiveIntegerField(
669 670
        verbose_name=_('Responses maximum size'),
670 671
        help_text=_('Maximum HTTP reponse size to log'),
671 672
        default=settings.LOGGED_RESPONSES_MAX_SIZE
672 673
    )
674
    log_retention_days = models.PositiveIntegerField(
675
        verbose_name=_('Log retention days'),
676
        help_text=_('Number of days to keep logs'),
677
        blank=True,
678
        null=True
679
    )
673 680

  
674 681
    class Meta:
675 682
        unique_together = (('resource_type', 'resource_pk'))
676 683

  
677 684

  
678 685
def parse_notification_delays(value):
679 686
    delays = [int(v.strip()) for v in value.split(',')]
680 687
    if not all(delay >= 0 for delay in delays):
passerelle/base/views.py
14 14
# You should have received a copy of the GNU Affero General Public License
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17 17
import datetime
18 18
import json
19 19

  
20 20
from dateutil import parser as date_parser
21 21

  
22
from django.conf import settings
22 23
from django.contrib.contenttypes.models import ContentType
23 24
from django.core.urlresolvers import reverse
24 25
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
25 26
from django.db.models import Q
26 27
from django.forms import models as model_forms
27 28
from django.views.generic import (
28 29
    View, DetailView, ListView, CreateView, UpdateView, DeleteView, FormView)
29 30
from django.http import Http404, HttpResponse, HttpResponseRedirect
......
141 142
    def get_context_data(self, **kwargs):
142 143
        context = super(LoggingParametersUpdateView, self).get_context_data(**kwargs)
143 144
        context['connector'] = self.get_resource()
144 145
        return context
145 146

  
146 147
    def get_form_class(self):
147 148
        form_class = model_forms.modelform_factory(
148 149
            LoggingParameters,
149
            fields=['log_level', 'trace_emails', 'requests_max_size', 'responses_max_size'])
150
            fields=['log_level', 'trace_emails', 'requests_max_size', 'responses_max_size',
151
                    'log_retention_days'])
150 152
        form_class.base_fields['trace_emails'].widget.attrs['rows'] = '3'
151 153
        return form_class
152 154

  
153 155
    def get_initial(self):
154 156
        d = self.initial.copy()
155 157
        d['resource_type'] = self.kwargs['resource_type']
156 158
        d['resource_pk'] = self.kwargs['resource_pk']
157 159
        parameters = self.get_resource().logging_parameters
158 160
        d['log_level'] = parameters.log_level
159 161
        d['trace_emails'] = parameters.trace_emails
160 162
        d['requests_max_size'] = parameters.requests_max_size
161 163
        d['responses_max_size'] = parameters.responses_max_size
164
        d['log_retention_days'] = parameters.log_retention_days
162 165
        return d
163 166

  
164 167
    def get_resource(self):
165 168
        content_type = ContentType.objects.get_for_id(self.kwargs['resource_type'])
166 169
        return content_type.model_class().objects.get(pk=self.kwargs['resource_pk'])
167 170

  
168 171
    def get_success_url(self):
169 172
        return self.get_resource().get_absolute_url()
170 173

  
171 174
    def form_valid(self, form):
172 175
        parameters = self.get_resource().logging_parameters
173 176
        parameters.log_level = form.cleaned_data['log_level']
174 177
        parameters.trace_emails = form.cleaned_data['trace_emails']
175 178
        parameters.requests_max_size = form.cleaned_data['requests_max_size']
176 179
        parameters.responses_max_size = form.cleaned_data['responses_max_size']
180
        parameters.log_retention_days = form.cleaned_data['log_retention_days']
177 181
        parameters.save()
178 182
        return super(LoggingParametersUpdateView, self).form_valid(form)
179 183

  
180 184

  
181 185
class ManageAvailabilityView(UpdateView):
182 186
    template_name = 'passerelle/manage/manage_availability_form.html'
183 187
    form_class = AvailabilityParametersForm
184 188

  
tests/test_misc.py
1 1
import datetime
2 2
import pytest
3 3
from mock import patch
4 4

  
5
from django.contrib.contenttypes.models import ContentType
5 6
from django.core.files import File
6 7
from django.db import connection
7 8
from django.db.migrations.executor import MigrationExecutor
9
from django.core.urlresolvers import reverse
8 10
from django.utils import timezone
9 11
from django.utils.six import StringIO
10 12

  
11 13
from passerelle.base.models import ResourceLog
12 14
from passerelle.apps.opengis.models import OpenGIS
13 15
from passerelle.apps.clicrdv.models import ClicRdv
14 16

  
17
from test_manager import login, admin_user
18

  
15 19

  
16 20
def test_get_description_url_fields(db):
17 21
    connector = OpenGIS(slug='plop', wms_service_url='http://www.example.net')
18 22
    assert 'http://www.example.net' in [x[1] for x in connector.get_description_fields()]
19 23

  
20 24
    connector = OpenGIS(slug='plop', wms_service_url='http://username:secret@www.example.net')
21 25
    assert 'http://***:***@www.example.net' in [x[1] for x in connector.get_description_fields()]
22 26

  
......
25 29

  
26 30

  
27 31
def test_get_description_secret_fields(db):
28 32
    connector = ClicRdv(slug='plop', apikey='secret1', username='plop', password='secret2')
29 33
    assert not 'secret1' in [x[1] for x in connector.get_description_fields()]
30 34
    assert not 'secret2' in [x[1] for x in connector.get_description_fields()]
31 35

  
32 36

  
33
def test_log_cleaning(db):
37
def test_log_cleaning(app, db, admin_user, settings):
34 38
    ResourceLog.objects.all().delete()
35 39
    connector = OpenGIS(slug='plop', wms_service_url='http://www.example.net')
40
    connector.save()
36 41
    connector.logger.error('hello1')
37 42
    connector.logger.error('hello2')
38 43

  
39 44
    assert ResourceLog.objects.all().count() == 2
40 45

  
41 46
    ResourceLog.objects.update(timestamp=timezone.now() - datetime.timedelta(days=10))
42 47
    connector.logger.error('hello3')
43 48
    assert ResourceLog.objects.all().count() == 3
44 49

  
50
    settings.LOG_RETENTION_DAYS = 11
51
    connector.daily()
52
    assert ResourceLog.objects.all().count() == 3
53
    settings.LOG_RETENTION_DAYS = 10
54
    connector.daily()
55
    assert ResourceLog.objects.all().count() == 1
56

  
57
    ResourceLog.objects.all().delete()
58
    connector.logger.error('hello1')
59
    connector.logger.error('hello2')
60
    assert ResourceLog.objects.all().count() == 2
61
    ResourceLog.objects.update(timestamp=timezone.now() - datetime.timedelta(days=10))
62
    connector.logger.error('hello3')
63
    assert ResourceLog.objects.all().count() == 3
64

  
65
    url = reverse('logging-parameters', kwargs={
66
        'resource_type': ContentType.objects.get_for_model(connector).id,
67
        'resource_pk': connector.id})
68
    app = login(app)
69
    resp = app.get(url)
70
    assert not resp.html.find('input', {'name': 'log_retention_days'}).has_attr('value')
71
    resp.form['log_retention_days'] = '11'
72
    resp.form.submit()
73
    connector.daily()
74
    assert ResourceLog.objects.all().count() == 3
75

  
76
    resp = app.get(url)
77
    assert int(resp.html.find('input', {'name': 'log_retention_days'})['value']) == 11
78
    resp.form['log_retention_days'] = '10'
79
    resp.form.submit()
45 80
    connector.daily()
46 81
    assert ResourceLog.objects.all().count() == 1
47 82

  
48 83
@pytest.fixture
49 84
def email_handler():
50 85
    import logging
51 86
    from django.utils.log import AdminEmailHandler
52 87

  
53
-