aboutsummaryrefslogtreecommitdiffstats
path: root/pydis_site
diff options
context:
space:
mode:
Diffstat (limited to 'pydis_site')
-rw-r--r--pydis_site/apps/api/migrations/0035_create_table_log_entry.py29
-rw-r--r--pydis_site/apps/api/models.py46
-rw-r--r--pydis_site/apps/api/serializers.py21
-rw-r--r--pydis_site/apps/api/urls.py12
-rw-r--r--pydis_site/apps/api/viewsets.py57
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