diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index aa4ef4bf8..fb0962902 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -153,19 +153,6 @@ class DocumentSerializer(DynamicFieldsModelSerializer): ) -class LogSerializer(serializers.ModelSerializer): - - class Meta: - model = Log - fields = ( - "id", - "created", - "message", - "group", - "level" - ) - - class SavedViewFilterRuleSerializer(serializers.ModelSerializer): class Meta: diff --git a/src/documents/tests/test_api.py b/src/documents/tests/test_api.py index 9e4b77189..54d2b292d 100644 --- a/src/documents/tests/test_api.py +++ b/src/documents/tests/test_api.py @@ -4,7 +4,9 @@ import shutil import tempfile from unittest import mock +from django.conf import settings from django.contrib.auth.models import User +from django.test import override_settings from rest_framework.test import APITestCase from whoosh.writing import AsyncWriter @@ -717,6 +719,28 @@ class TestDocumentApi(DirectoriesMixin, APITestCase): v1 = SavedView.objects.get(id=v1.id) self.assertEqual(v1.filter_rules.count(), 0) + def test_get_logs(self): + response = self.client.get("/api/logs/") + self.assertEqual(response.status_code, 200) + self.assertCountEqual(response.data, ["mail", "paperless"]) + + def test_get_invalid_log(self): + response = self.client.get("/api/logs/bogus_log/") + self.assertEqual(response.status_code, 404) + + @override_settings(LOGGING_DIR="bogus_dir") + def test_get_nonexistent_log(self): + response = self.client.get("/api/logs/paperless/") + self.assertEqual(response.status_code, 404) + + def test_get_log(self): + log_data = "test\ntest2\n" + with open(os.path.join(settings.LOGGING_DIR, "paperless.log"), "w") as f: + f.write(log_data) + response = self.client.get("/api/logs/paperless/") + self.assertEqual(response.status_code, 200) + self.assertListEqual(response.data, ["test", "test2"]) + class TestBulkEdit(DirectoriesMixin, APITestCase): diff --git a/src/documents/tests/test_logger.py b/src/documents/tests/test_logger.py deleted file mode 100644 index bbc9c2b5d..000000000 --- a/src/documents/tests/test_logger.py +++ /dev/null @@ -1,66 +0,0 @@ -import logging -import uuid -from unittest import mock - -from django.test import TestCase, override_settings - -from ..models import Log - - -class TestPaperlessLog(TestCase): - - def __init__(self, *args, **kwargs): - TestCase.__init__(self, *args, **kwargs) - self.logger = logging.getLogger( - "documents.management.commands.document_consumer") - - @override_settings(DISABLE_DBHANDLER=False) - def test_that_it_saves_at_all(self): - - kw = {"group": uuid.uuid4()} - - self.assertEqual(Log.objects.all().count(), 0) - - with mock.patch("logging.StreamHandler.emit") as __: - - # Debug messages are ignored by default - self.logger.debug("This is a debugging message", extra=kw) - self.assertEqual(Log.objects.all().count(), 1) - - self.logger.info("This is an informational message", extra=kw) - self.assertEqual(Log.objects.all().count(), 2) - - self.logger.warning("This is an warning message", extra=kw) - self.assertEqual(Log.objects.all().count(), 3) - - self.logger.error("This is an error message", extra=kw) - self.assertEqual(Log.objects.all().count(), 4) - - self.logger.critical("This is a critical message", extra=kw) - self.assertEqual(Log.objects.all().count(), 5) - - @override_settings(DISABLE_DBHANDLER=False) - def test_groups(self): - - kw1 = {"group": uuid.uuid4()} - kw2 = {"group": uuid.uuid4()} - - self.assertEqual(Log.objects.all().count(), 0) - - with mock.patch("logging.StreamHandler.emit") as __: - - self.logger.info("This is an informational message", extra=kw2) - self.assertEqual(Log.objects.all().count(), 1) - self.assertEqual(Log.objects.filter(group=kw2["group"]).count(), 1) - - self.logger.warning("This is an warning message", extra=kw1) - self.assertEqual(Log.objects.all().count(), 2) - self.assertEqual(Log.objects.filter(group=kw1["group"]).count(), 1) - - self.logger.error("This is an error message", extra=kw2) - self.assertEqual(Log.objects.all().count(), 3) - self.assertEqual(Log.objects.filter(group=kw2["group"]).count(), 2) - - self.logger.critical("This is a critical message", extra=kw1) - self.assertEqual(Log.objects.all().count(), 4) - self.assertEqual(Log.objects.filter(group=kw1["group"]).count(), 2) diff --git a/src/documents/tests/utils.py b/src/documents/tests/utils.py index dfefc4061..ebf79f6c5 100644 --- a/src/documents/tests/utils.py +++ b/src/documents/tests/utils.py @@ -19,12 +19,15 @@ def setup_directories(): dirs.originals_dir = os.path.join(dirs.media_dir, "documents", "originals") dirs.thumbnail_dir = os.path.join(dirs.media_dir, "documents", "thumbnails") dirs.archive_dir = os.path.join(dirs.media_dir, "documents", "archive") + dirs.logging_dir = os.path.join(dirs.data_dir, "log") os.makedirs(dirs.index_dir, exist_ok=True) os.makedirs(dirs.originals_dir, exist_ok=True) os.makedirs(dirs.thumbnail_dir, exist_ok=True) os.makedirs(dirs.archive_dir, exist_ok=True) + os.makedirs(dirs.logging_dir, exist_ok=True) + dirs.settings_override = override_settings( DATA_DIR=dirs.data_dir, SCRATCH_DIR=dirs.scratch_dir, @@ -33,6 +36,7 @@ def setup_directories(): THUMBNAIL_DIR=dirs.thumbnail_dir, ARCHIVE_DIR=dirs.archive_dir, CONSUMPTION_DIR=dirs.consumption_dir, + LOGGING_DIR=dirs.logging_dir, INDEX_DIR=dirs.index_dir, MODEL_FILE=os.path.join(dirs.data_dir, "classification_model.pickle"), MEDIA_LOCK=os.path.join(dirs.media_dir, "media.lock") diff --git a/src/documents/views.py b/src/documents/views.py index e413d4b35..2443ae342 100755 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -29,7 +29,7 @@ from rest_framework.views import APIView from rest_framework.viewsets import ( GenericViewSet, ModelViewSet, - ReadOnlyModelViewSet + ViewSet ) import documents.index as index @@ -40,16 +40,14 @@ from .filters import ( CorrespondentFilterSet, DocumentFilterSet, TagFilterSet, - DocumentTypeFilterSet, - LogFilterSet + DocumentTypeFilterSet ) from .matching import match_correspondents, match_tags, match_document_types -from .models import Correspondent, Document, Log, Tag, DocumentType, SavedView +from .models import Correspondent, Document, Tag, DocumentType, SavedView from .parsers import get_parser_class_for_mime_type from .serialisers import ( CorrespondentSerializer, DocumentSerializer, - LogSerializer, TagSerializer, DocumentTypeSerializer, PostDocumentSerializer, @@ -307,16 +305,28 @@ class DocumentViewSet(RetrieveModelMixin, raise Http404() -class LogViewSet(ReadOnlyModelViewSet): - model = Log +class LogViewSet(ViewSet): - queryset = Log.objects.all() - serializer_class = LogSerializer - pagination_class = StandardPagination permission_classes = (IsAuthenticated,) - filter_backends = (DjangoFilterBackend, OrderingFilter) - filterset_class = LogFilterSet - ordering_fields = ("created",) + + log_files = ["paperless", "mail"] + + def retrieve(self, request, pk=None, *args, **kwargs): + if not pk in self.log_files: + raise Http404() + + filename = os.path.join(settings.LOGGING_DIR, f"{pk}.log") + + if not os.path.isfile(filename): + raise Http404() + + with open(filename, "r") as f: + lines = [l.rstrip() for l in f.readlines()] + + return Response(lines) + + def list(self, request, *args, **kwargs): + return Response(self.log_files) class SavedViewViewSet(ModelViewSet): diff --git a/src/paperless/urls.py b/src/paperless/urls.py index 5b58de454..40f4bd754 100755 --- a/src/paperless/urls.py +++ b/src/paperless/urls.py @@ -31,7 +31,7 @@ api_router = DefaultRouter() api_router.register(r"correspondents", CorrespondentViewSet) api_router.register(r"document_types", DocumentTypeViewSet) api_router.register(r"documents", DocumentViewSet) -api_router.register(r"logs", LogViewSet) +api_router.register(r"logs", LogViewSet, basename="logs") api_router.register(r"tags", TagViewSet) api_router.register(r"saved_views", SavedViewViewSet)