diff options
| author | 2019-04-11 20:07:29 +0100 | |
|---|---|---|
| committer | 2019-04-11 20:07:29 +0100 | |
| commit | b2e88fe4e9b5de241ce26cd17bd52ef1503ed1f3 (patch) | |
| tree | cf4dfabb903e0b4b40e8cf95140781fda9c70030 | |
| parent | Ignore pip version pinning complaints. (diff) | |
| parent | Document `LogEntryViewSet`. (diff) | |
Merge pull request #199 from python-discord/add-logs-api
Add an API to post logs to.
| -rw-r--r-- | pydis_site/apps/api/migrations/0035_create_table_log_entry.py | 29 | ||||
| -rw-r--r-- | pydis_site/apps/api/models.py | 46 | ||||
| -rw-r--r-- | pydis_site/apps/api/serializers.py | 21 | ||||
| -rw-r--r-- | pydis_site/apps/api/urls.py | 12 | ||||
| -rw-r--r-- | pydis_site/apps/api/viewsets.py | 57 | 
5 files changed, 142 insertions, 23 deletions
diff --git a/pydis_site/apps/api/migrations/0035_create_table_log_entry.py b/pydis_site/apps/api/migrations/0035_create_table_log_entry.py new file mode 100644 index 00000000..a8256a0e --- /dev/null +++ b/pydis_site/apps/api/migrations/0035_create_table_log_entry.py @@ -0,0 +1,29 @@ +# Generated by Django 2.1.5 on 2019-04-08 18:27 + +from django.db import migrations, models +import django.utils.timezone +import pydis_site.apps.api.models + + +class Migration(migrations.Migration): + +    dependencies = [ +        ('api', '0034_add_botsetting_name_validator'), +    ] + +    operations = [ +        migrations.CreateModel( +            name='LogEntry', +            fields=[ +                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), +                ('application', models.CharField(choices=[('bot', 'Bot'), ('seasonalbot', 'Seasonalbot'), ('site', 'Website')], help_text='The application that generated this log entry.', max_length=20)), +                ('logger_name', models.CharField(help_text='The name of the logger that generated this log entry.', max_length=100)), +                ('timestamp', models.DateTimeField(default=django.utils.timezone.now, help_text='The date and time when this entry was created.')), +                ('level', models.CharField(choices=[('debug', 'Debug'), ('info', 'Info'), ('warning', 'Warning'), ('error', 'Error'), ('critical', 'Critical')], help_text='The logger level at which this entry was emitted. The levels correspond to the Python `logging` levels.', max_length=8)), +                ('module', models.CharField(help_text='The fully qualified path of the module generating this log line.', max_length=100)), +                ('line', models.PositiveSmallIntegerField(help_text='The line at which the log line was emitted.')), +                ('message', models.TextField(help_text='The textual content of the log line.')), +            ], +            bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model), +        ), +    ] diff --git a/pydis_site/apps/api/models.py b/pydis_site/apps/api/models.py index 86c99f86..b2499f8d 100644 --- a/pydis_site/apps/api/models.py +++ b/pydis_site/apps/api/models.py @@ -450,3 +450,49 @@ class Nomination(ModelReprMixin, models.Model):          auto_now_add=True,          help_text="The creation date of this nomination."      ) + + +class LogEntry(ModelReprMixin, models.Model): +    """A log entry generated by one of the PyDis applications.""" + +    application = models.CharField( +        max_length=20, +        help_text="The application that generated this log entry.", +        choices=( +            ('bot', 'Bot'), +            ('seasonalbot', 'Seasonalbot'), +            ('site', 'Website') +        ) +    ) +    logger_name = models.CharField( +        max_length=100, +        help_text="The name of the logger that generated this log entry." +    ) +    timestamp = models.DateTimeField( +        default=timezone.now, +        help_text="The date and time when this entry was created." +    ) +    level = models.CharField( +        max_length=8,  # 'critical' +        choices=( +            ('debug', 'Debug'), +            ('info', 'Info'), +            ('warning', 'Warning'), +            ('error', 'Error'), +            ('critical', 'Critical') +        ), +        help_text=( +            "The logger level at which this entry was emitted. The levels " +            "correspond to the Python `logging` levels." +        ) +    ) +    module = models.CharField( +        max_length=100, +        help_text="The fully qualified path of the module generating this log line." +    ) +    line = models.PositiveSmallIntegerField( +        help_text="The line at which the log line was emitted." +    ) +    message = models.TextField( +        help_text="The textual content of the log line." +    ) diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py index 5b3cb28c..8f045044 100644 --- a/pydis_site/apps/api/serializers.py +++ b/pydis_site/apps/api/serializers.py @@ -4,12 +4,12 @@ from rest_framework_bulk import BulkSerializerMixin  from .models import (      BotSetting, DeletedMessage,      DocumentationLink, Infraction, -    MessageDeletionContext, Nomination, -    OffTopicChannelName, Reminder, -    Role, SnakeFact, -    SnakeIdiom, SnakeName, -    SpecialSnake, Tag, -    User +    LogEntry, MessageDeletionContext, +    Nomination, OffTopicChannelName, +    Reminder, Role, +    SnakeFact, SnakeIdiom, +    SnakeName, SpecialSnake, +    Tag, User  ) @@ -101,6 +101,15 @@ class ExpandedInfractionSerializer(InfractionSerializer):          return ret +class LogEntrySerializer(ModelSerializer): +    class Meta: +        model = LogEntry +        fields = ( +            'application', 'logger_name', 'timestamp', +            'level', 'module', 'line', 'message' +        ) + +  class OffTopicChannelNameSerializer(ModelSerializer):      class Meta:          model = OffTopicChannelName diff --git a/pydis_site/apps/api/urls.py b/pydis_site/apps/api/urls.py index 6c89a52e..724d7e2b 100644 --- a/pydis_site/apps/api/urls.py +++ b/pydis_site/apps/api/urls.py @@ -5,11 +5,12 @@ from .views import HealthcheckView, RulesView  from .viewsets import (      BotSettingViewSet, DeletedMessageViewSet,      DocumentationLinkViewSet, InfractionViewSet, -    NominationViewSet, OffTopicChannelNameViewSet, -    ReminderViewSet, RoleViewSet, -    SnakeFactViewSet, SnakeIdiomViewSet, -    SnakeNameViewSet, SpecialSnakeViewSet, -    TagViewSet, UserViewSet +    LogEntryViewSet, NominationViewSet, +    OffTopicChannelNameViewSet, ReminderViewSet, +    RoleViewSet, SnakeFactViewSet, +    SnakeIdiomViewSet, SnakeNameViewSet, +    SpecialSnakeViewSet, TagViewSet, +    UserViewSet  ) @@ -81,6 +82,7 @@ urlpatterns = (      # from django_hosts.resolvers import reverse      # snake_name_endpoint = reverse('bot:snakename-list', host='api')  # `bot/` endpoints      path('bot/', include((bot_router.urls, 'api'), namespace='bot')), +    path('logs', LogEntryViewSet.as_view({'post': 'create'}), name='logs'),      path('healthcheck', HealthcheckView.as_view(), name='healthcheck'),      path('rules', RulesView.as_view(), name='rules')  ) diff --git a/pydis_site/apps/api/viewsets.py b/pydis_site/apps/api/viewsets.py index 949ffaaa..47915256 100644 --- a/pydis_site/apps/api/viewsets.py +++ b/pydis_site/apps/api/viewsets.py @@ -15,22 +15,23 @@ from rest_framework_bulk import BulkCreateModelMixin  from .models import (      BotSetting, DocumentationLink, -    Infraction, MessageDeletionContext, -    Nomination, OffTopicChannelName, -    Reminder, Role, -    SnakeFact, SnakeIdiom, -    SnakeName, SpecialSnake, -    Tag, User +    Infraction, LogEntry, +    MessageDeletionContext, Nomination, +    OffTopicChannelName, Reminder, +    Role, SnakeFact, +    SnakeIdiom, SnakeName, +    SpecialSnake, Tag, +    User  )  from .serializers import (      BotSettingSerializer, DocumentationLinkSerializer,      ExpandedInfractionSerializer, InfractionSerializer, -    MessageDeletionContextSerializer, NominationSerializer, -    OffTopicChannelNameSerializer, ReminderSerializer, -    RoleSerializer, SnakeFactSerializer, -    SnakeIdiomSerializer, SnakeNameSerializer, -    SpecialSnakeSerializer, TagSerializer, -    UserSerializer +    LogEntrySerializer, MessageDeletionContextSerializer, +    NominationSerializer, OffTopicChannelNameSerializer, +    ReminderSerializer, RoleSerializer, +    SnakeFactSerializer, SnakeIdiomSerializer, +    SnakeNameSerializer, SpecialSnakeSerializer, +    TagSerializer, UserSerializer  ) @@ -280,6 +281,38 @@ class InfractionViewSet(CreateModelMixin, RetrieveModelMixin, ListModelMixin, Ge          return self.partial_update(*args, **kwargs) +class LogEntryViewSet(CreateModelMixin, GenericViewSet): +    """ +    View providing support for creating log entries in the site database +    for viewing via the log browser. + +    ## Routes +    ### POST /logs +    Create a new log entry. + +    #### Request body +    >>> { +    ...     'application': str,  # 'bot' | 'seasonalbot' | 'site' +    ...     'logger_name': str,  # such as 'bot.cogs.moderation' +    ...     'timestamp': Optional[str],  # from `datetime.utcnow().isoformat()` +    ...     'level': str,  # 'debug' | 'info' | 'warning' | 'error' | 'critical' +    ...     'module': str,  # such as 'pydis_site.apps.api.serializers' +    ...     'line': int,  # > 0 +    ...     'message': str,  # textual formatted content of the logline +    ... } + +    #### Status codes +    - 201: returned on success +    - 400: if the request body has invalid fields, see the response for details + +    ## Authentication +    Requires a API token. +    """ + +    queryset = LogEntry.objects.all() +    serializer_class = LogEntrySerializer + +  class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet):      """      View of off-topic channel names used by the bot  |