dev: api logging and moving the capture exception to utils for logging and then capturing

This commit is contained in:
pablohashescobar 2024-03-11 20:25:25 +05:30
parent 6be72149d9
commit 31a2bbc4de
15 changed files with 159 additions and 152 deletions

View File

@ -15,12 +15,12 @@ from rest_framework.response import Response
# Third party imports # Third party imports
from rest_framework.views import APIView from rest_framework.views import APIView
from sentry_sdk import capture_exception
# Module imports # Module imports
from plane.api.middleware.api_authentication import APIKeyAuthentication from plane.api.middleware.api_authentication import APIKeyAuthentication
from plane.api.rate_limit import ApiKeyRateThrottle from plane.api.rate_limit import ApiKeyRateThrottle
from plane.bgtasks.webhook_task import send_webhook from plane.bgtasks.webhook_task import send_webhook
from plane.utils.exception_logger import log_exception
from plane.utils.paginator import BasePaginator from plane.utils.paginator import BasePaginator
@ -126,9 +126,7 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator):
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
logger = logging.getLogger("plane") log_exception(e)
logger.error(e)
capture_exception(e)
return Response( return Response(
{"error": "Something went wrong please try again later"}, {"error": "Something went wrong please try again later"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR, status=status.HTTP_500_INTERNAL_SERVER_ERROR,

View File

@ -1,27 +1,27 @@
# Python imports # Python imports
import zoneinfo import zoneinfo
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.db import IntegrityError
# Django imports # Django imports
from django.urls import resolve from django.urls import resolve
from django.conf import settings
from django.utils import timezone from django.utils import timezone
from django.db import IntegrityError from django_filters.rest_framework import DjangoFilterBackend
from django.core.exceptions import ObjectDoesNotExist, ValidationError
# Third part imports # Third part imports
from rest_framework import status from rest_framework import status
from rest_framework.viewsets import ModelViewSet
from rest_framework.response import Response
from rest_framework.exceptions import APIException from rest_framework.exceptions import APIException
from rest_framework.views import APIView
from rest_framework.filters import SearchFilter from rest_framework.filters import SearchFilter
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from sentry_sdk import capture_exception from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
# Module imports # Module imports
from plane.utils.paginator import BasePaginator
from plane.bgtasks.webhook_task import send_webhook from plane.bgtasks.webhook_task import send_webhook
from plane.utils.exception_logger import log_exception
from plane.utils.paginator import BasePaginator
class TimezoneMixin: class TimezoneMixin:
@ -87,7 +87,7 @@ class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator):
try: try:
return self.model.objects.all() return self.model.objects.all()
except Exception as e: except Exception as e:
capture_exception(e) log_exception(e)
raise APIException( raise APIException(
"Please check the view", status.HTTP_400_BAD_REQUEST "Please check the view", status.HTTP_400_BAD_REQUEST
) )
@ -121,13 +121,13 @@ class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator):
) )
if isinstance(e, KeyError): if isinstance(e, KeyError):
capture_exception(e) log_exception(e)
return Response( return Response(
{"error": "The required key does not exist."}, {"error": "The required key does not exist."},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
capture_exception(e) log_exception(e)
return Response( return Response(
{"error": "Something went wrong please try again later"}, {"error": "Something went wrong please try again later"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR, status=status.HTTP_500_INTERNAL_SERVER_ERROR,
@ -233,9 +233,7 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator):
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
if settings.DEBUG: log_exception(e)
print(e)
capture_exception(e)
return Response( return Response(
{"error": "Something went wrong please try again later"}, {"error": "Something went wrong please try again later"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR, status=status.HTTP_500_INTERNAL_SERVER_ERROR,

View File

@ -5,18 +5,17 @@ import logging
# Third party imports # Third party imports
from celery import shared_task from celery import shared_task
from django.conf import settings
# Django imports # Django imports
from django.core.mail import EmailMultiAlternatives, get_connection from django.core.mail import EmailMultiAlternatives, get_connection
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.html import strip_tags from django.utils.html import strip_tags
from sentry_sdk import capture_exception
# Module imports # Module imports
from plane.db.models import Issue from plane.db.models import Issue
from plane.license.utils.instance_value import get_email_configuration from plane.license.utils.instance_value import get_email_configuration
from plane.utils.analytics_plot import build_graph_plot from plane.utils.analytics_plot import build_graph_plot
from plane.utils.exception_logger import log_exception
from plane.utils.issue_filters import issue_filters from plane.utils.issue_filters import issue_filters
row_mapping = { row_mapping = {
@ -505,9 +504,8 @@ def analytic_export_task(email, data, slug):
csv_buffer = generate_csv_from_rows(rows) csv_buffer = generate_csv_from_rows(rows)
send_export_email(email, slug, csv_buffer, rows) send_export_email(email, slug, csv_buffer, rows)
logging.getLogger("plane").info("Email sent succesfully.")
return return
except Exception as e: except Exception as e:
logger = logging.getLogger("plane") log_exception(e)
logger.error(e)
capture_exception(e)
return return

View File

@ -1,21 +1,22 @@
import logging
from datetime import datetime from datetime import datetime
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
# Third party imports # Third party imports
from celery import shared_task from celery import shared_task
from sentry_sdk import capture_exception from django.core.mail import EmailMultiAlternatives, get_connection
from django.template.loader import render_to_string
# Django imports # Django imports
from django.utils import timezone from django.utils import timezone
from django.core.mail import EmailMultiAlternatives, get_connection
from django.template.loader import render_to_string
from django.utils.html import strip_tags from django.utils.html import strip_tags
from django.conf import settings
# Module imports # Module imports
from plane.db.models import EmailNotificationLog, User, Issue from plane.db.models import EmailNotificationLog, Issue, User
from plane.license.utils.instance_value import get_email_configuration from plane.license.utils.instance_value import get_email_configuration
from plane.settings.redis import redis_instance from plane.settings.redis import redis_instance
from plane.utils.exception_logger import log_exception
# acquire and delete redis lock # acquire and delete redis lock
@ -69,7 +70,9 @@ def stack_email_notification():
receiver_notification.get("entity_identifier"), {} receiver_notification.get("entity_identifier"), {}
).setdefault( ).setdefault(
str(receiver_notification.get("triggered_by_id")), [] str(receiver_notification.get("triggered_by_id")), []
).append(receiver_notification.get("data")) ).append(
receiver_notification.get("data")
)
# append processed notifications # append processed notifications
processed_notifications.append(receiver_notification.get("id")) processed_notifications.append(receiver_notification.get("id"))
email_notification_ids.append(receiver_notification.get("id")) email_notification_ids.append(receiver_notification.get("id"))
@ -296,7 +299,9 @@ def send_email_notification(
) )
msg.attach_alternative(html_content, "text/html") msg.attach_alternative(html_content, "text/html")
msg.send() msg.send()
logging.getLogger("plane").info("Email Sent Successfully")
# Update the logs
EmailNotificationLog.objects.filter( EmailNotificationLog.objects.filter(
pk__in=email_notification_ids pk__in=email_notification_ids
).update(sent_at=timezone.now()) ).update(sent_at=timezone.now())
@ -305,15 +310,20 @@ def send_email_notification(
release_lock(lock_id=lock_id) release_lock(lock_id=lock_id)
return return
except Exception as e: except Exception as e:
capture_exception(e) log_exception(e)
# release the lock # release the lock
release_lock(lock_id=lock_id) release_lock(lock_id=lock_id)
return return
else: else:
print("Duplicate task recived. Skipping...") logging.getLogger("plane").info(
"Duplicate email received skipping"
)
return return
except (Issue.DoesNotExist, User.DoesNotExist) as e: except (Issue.DoesNotExist, User.DoesNotExist) as e:
if settings.DEBUG: log_exception(e)
print(e) release_lock(lock_id=lock_id)
return
except Exception as e:
log_exception(e)
release_lock(lock_id=lock_id) release_lock(lock_id=lock_id)
return return

View File

@ -1,13 +1,13 @@
import uuid
import os import os
import uuid
# third party imports # third party imports
from celery import shared_task from celery import shared_task
from sentry_sdk import capture_exception
from posthog import Posthog from posthog import Posthog
# module imports # module imports
from plane.license.utils.instance_value import get_configuration_value from plane.license.utils.instance_value import get_configuration_value
from plane.utils.exception_logger import log_exception
def posthogConfiguration(): def posthogConfiguration():
@ -51,7 +51,8 @@ def auth_events(user, email, user_agent, ip, event_name, medium, first_time):
}, },
) )
except Exception as e: except Exception as e:
capture_exception(e) log_exception(e)
return
@shared_task @shared_task
@ -77,4 +78,5 @@ def workspace_invite_event(
}, },
) )
except Exception as e: except Exception as e:
capture_exception(e) log_exception(e)
return

View File

@ -2,23 +2,23 @@
import csv import csv
import io import io
import json import json
import boto3
import zipfile
import logging import logging
from urllib.parse import urlparse, urlunparse import zipfile
import boto3
from botocore.client import Config
# Third party imports
from celery import shared_task
# Django imports # Django imports
from django.conf import settings from django.conf import settings
from django.utils import timezone from django.utils import timezone
# Third party imports
from celery import shared_task
from sentry_sdk import capture_exception
from botocore.client import Config
from openpyxl import Workbook from openpyxl import Workbook
# Module imports # Module imports
from plane.db.models import Issue, ExporterHistory from plane.db.models import ExporterHistory, Issue
from plane.utils.exception_logger import log_exception
def dateTimeConverter(time): def dateTimeConverter(time):
@ -405,7 +405,5 @@ def issue_export_task(
exporter_instance.status = "failed" exporter_instance.status = "failed"
exporter_instance.reason = str(e) exporter_instance.reason = str(e)
exporter_instance.save(update_fields=["status", "reason"]) exporter_instance.save(update_fields=["status", "reason"])
logger = logging.getLogger("plane") log_exception(e)
logger.error(e)
capture_exception(e)
return return

View File

@ -8,10 +8,10 @@ from celery import shared_task
from django.core.mail import EmailMultiAlternatives, get_connection from django.core.mail import EmailMultiAlternatives, get_connection
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.html import strip_tags from django.utils.html import strip_tags
from sentry_sdk import capture_exception
# Module imports # Module imports
from plane.license.utils.instance_value import get_email_configuration from plane.license.utils.instance_value import get_email_configuration
from plane.utils.exception_logger import log_exception
@shared_task @shared_task
@ -60,9 +60,8 @@ def forgot_password(first_name, email, uidb64, token, current_site):
) )
msg.attach_alternative(html_content, "text/html") msg.attach_alternative(html_content, "text/html")
msg.send() msg.send()
logging.getLogger("plane").info("Email sent successfully")
return return
except Exception as e: except Exception as e:
logger = logging.getLogger("plane") log_exception(e)
logger.error(e)
capture_exception(e)
return return

View File

@ -1,35 +1,37 @@
# Python imports # Python imports
import json import json
import requests
import logging import logging
import requests
# Third Party imports
from celery import shared_task
# Django imports # Django imports
from django.conf import settings from django.conf import settings
from django.core.serializers.json import DjangoJSONEncoder from django.core.serializers.json import DjangoJSONEncoder
from django.utils import timezone from django.utils import timezone
# Third Party imports from plane.app.serializers import IssueActivitySerializer
from celery import shared_task from plane.bgtasks.notification_task import notifications
from sentry_sdk import capture_exception
# Module imports # Module imports
from plane.db.models import ( from plane.db.models import (
User,
Issue,
Project,
Label,
IssueActivity,
State,
Cycle,
Module,
IssueReaction,
CommentReaction, CommentReaction,
Cycle,
Issue,
IssueActivity,
IssueComment, IssueComment,
IssueReaction,
IssueSubscriber, IssueSubscriber,
Label,
Module,
Project,
State,
User,
) )
from plane.app.serializers import IssueActivitySerializer
from plane.bgtasks.notification_task import notifications
from plane.settings.redis import redis_instance from plane.settings.redis import redis_instance
from plane.utils.exception_logger import log_exception
# Track Changes in name # Track Changes in name
@ -1648,7 +1650,7 @@ def issue_activity(
headers=headers, headers=headers,
) )
except Exception as e: except Exception as e:
capture_exception(e) log_exception(e)
if notification: if notification:
notifications.delay( notifications.delay(
@ -1669,7 +1671,5 @@ def issue_activity(
return return
except Exception as e: except Exception as e:
logger = logging.getLogger("plane") log_exception(e)
logger.error(e)
capture_exception(e)
return return

View File

@ -1,20 +1,19 @@
# Python imports # Python imports
import json import json
from datetime import timedelta
import logging import logging
from datetime import timedelta
# Django imports
from django.utils import timezone
from django.db.models import Q
from django.conf import settings
# Third party imports # Third party imports
from celery import shared_task from celery import shared_task
from sentry_sdk import capture_exception from django.db.models import Q
# Django imports
from django.utils import timezone
# Module imports # Module imports
from plane.db.models import Issue, Project, State
from plane.bgtasks.issue_activites_task import issue_activity from plane.bgtasks.issue_activites_task import issue_activity
from plane.db.models import Issue, Project, State
from plane.utils.exception_logger import log_exception
@shared_task @shared_task
@ -97,9 +96,7 @@ def archive_old_issues():
] ]
return return
except Exception as e: except Exception as e:
logger = logging.getLogger("plane") log_exception(e)
logger.error(e)
capture_exception(e)
return return
@ -180,7 +177,5 @@ def close_old_issues():
] ]
return return
except Exception as e: except Exception as e:
logger = logging.getLogger("plane") log_exception(e)
logger.error(e)
capture_exception(e)
return return

View File

@ -3,16 +3,15 @@ import logging
# Third party imports # Third party imports
from celery import shared_task from celery import shared_task
from django.conf import settings
# Django imports # Django imports
from django.core.mail import EmailMultiAlternatives, get_connection from django.core.mail import EmailMultiAlternatives, get_connection
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.html import strip_tags from django.utils.html import strip_tags
from sentry_sdk import capture_exception
# Module imports # Module imports
from plane.license.utils.instance_value import get_email_configuration from plane.license.utils.instance_value import get_email_configuration
from plane.utils.exception_logger import log_exception
@shared_task @shared_task
@ -53,9 +52,8 @@ def magic_link(email, key, token, current_site):
) )
msg.attach_alternative(html_content, "text/html") msg.attach_alternative(html_content, "text/html")
msg.send() msg.send()
logging.getLogger("plane").info("Email sent successfully.")
return return
except Exception as e: except Exception as e:
logger = logging.getLogger("plane") log_exception(e)
logger.error(e)
capture_exception(e)
return return

View File

@ -8,11 +8,11 @@ from celery import shared_task
from django.core.mail import EmailMultiAlternatives, get_connection from django.core.mail import EmailMultiAlternatives, get_connection
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.html import strip_tags from django.utils.html import strip_tags
from sentry_sdk import capture_exception
# Module imports # Module imports
from plane.db.models import Project, ProjectMemberInvite, User from plane.db.models import Project, ProjectMemberInvite, User
from plane.license.utils.instance_value import get_email_configuration from plane.license.utils.instance_value import get_email_configuration
from plane.utils.exception_logger import log_exception
@shared_task @shared_task
@ -73,11 +73,10 @@ def project_invitation(email, project_id, token, current_site, invitor):
msg.attach_alternative(html_content, "text/html") msg.attach_alternative(html_content, "text/html")
msg.send() msg.send()
logging.getLogger("plane").info("Email sent successfully.")
return return
except (Project.DoesNotExist, ProjectMemberInvite.DoesNotExist): except (Project.DoesNotExist, ProjectMemberInvite.DoesNotExist):
return return
except Exception as e: except Exception as e:
logger = logging.getLogger("plane") log_exception(e)
logger.error(e)
capture_exception(e)
return return

View File

@ -1,44 +1,45 @@
import requests
import uuid
import hashlib import hashlib
import json
import hmac import hmac
import json
import logging
import uuid
# Django imports import requests
from django.conf import settings
from django.core.serializers.json import DjangoJSONEncoder
from django.core.mail import EmailMultiAlternatives, get_connection
from django.template.loader import render_to_string
from django.utils.html import strip_tags
# Third party imports # Third party imports
from celery import shared_task from celery import shared_task
from sentry_sdk import capture_exception
from plane.db.models import ( # Django imports
Webhook, from django.conf import settings
WebhookLog, from django.core.mail import EmailMultiAlternatives, get_connection
Project, from django.core.serializers.json import DjangoJSONEncoder
Issue, from django.template.loader import render_to_string
Cycle, from django.utils.html import strip_tags
Module,
ModuleIssue,
CycleIssue,
IssueComment,
User,
)
from plane.api.serializers import (
ProjectSerializer,
CycleSerializer,
ModuleSerializer,
CycleIssueSerializer,
ModuleIssueSerializer,
IssueCommentSerializer,
IssueExpandSerializer,
)
# Module imports # Module imports
from plane.api.serializers import (
CycleIssueSerializer,
CycleSerializer,
IssueCommentSerializer,
IssueExpandSerializer,
ModuleIssueSerializer,
ModuleSerializer,
ProjectSerializer,
)
from plane.db.models import (
Cycle,
CycleIssue,
Issue,
IssueComment,
Module,
ModuleIssue,
Project,
User,
Webhook,
WebhookLog,
)
from plane.license.utils.instance_value import get_email_configuration from plane.license.utils.instance_value import get_email_configuration
from plane.utils.exception_logger import log_exception
SERIALIZER_MAPPER = { SERIALIZER_MAPPER = {
"project": ProjectSerializer, "project": ProjectSerializer,
@ -174,7 +175,7 @@ def webhook_task(self, webhook, slug, event, event_data, action, current_site):
except Exception as e: except Exception as e:
if settings.DEBUG: if settings.DEBUG:
print(e) print(e)
capture_exception(e) log_exception(e)
return return
@ -241,7 +242,7 @@ def send_webhook(event, payload, kw, action, slug, bulk, current_site):
except Exception as e: except Exception as e:
if settings.DEBUG: if settings.DEBUG:
print(e) print(e)
capture_exception(e) log_exception(e)
return return
@ -295,8 +296,8 @@ def send_webhook_deactivation_email(
) )
msg.attach_alternative(html_content, "text/html") msg.attach_alternative(html_content, "text/html")
msg.send() msg.send()
logging.getLogger("plane").info("Email sent successfully.")
return return
except Exception as e: except Exception as e:
print(e) log_exception(e)
return return

View File

@ -8,11 +8,11 @@ from celery import shared_task
from django.core.mail import EmailMultiAlternatives, get_connection from django.core.mail import EmailMultiAlternatives, get_connection
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.html import strip_tags from django.utils.html import strip_tags
from sentry_sdk import capture_exception
# Module imports # Module imports
from plane.db.models import User, Workspace, WorkspaceMemberInvite from plane.db.models import User, Workspace, WorkspaceMemberInvite
from plane.license.utils.instance_value import get_email_configuration from plane.license.utils.instance_value import get_email_configuration
from plane.utils.exception_logger import log_exception
@shared_task @shared_task
@ -76,13 +76,12 @@ def workspace_invitation(email, workspace_id, token, current_site, invitor):
) )
msg.attach_alternative(html_content, "text/html") msg.attach_alternative(html_content, "text/html")
msg.send() msg.send()
logging.getLogger("plane").info("Email sent succesfully")
return return
except (Workspace.DoesNotExist, WorkspaceMemberInvite.DoesNotExist): except (Workspace.DoesNotExist, WorkspaceMemberInvite.DoesNotExist) as e:
print("Workspace or WorkspaceMember Invite Does not exists") log_exception(e)
return return
except Exception as e: except Exception as e:
logger = logging.getLogger("plane") log_exception(e)
logger.error(e)
capture_exception(e)
return return

View File

@ -1,25 +1,25 @@
# Python imports # Python imports
import zoneinfo import zoneinfo
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.db import IntegrityError
# Django imports # Django imports
from django.urls import resolve from django.urls import resolve
from django.conf import settings
from django.utils import timezone from django.utils import timezone
from django.db import IntegrityError from django_filters.rest_framework import DjangoFilterBackend
from django.core.exceptions import ObjectDoesNotExist, ValidationError
# Third part imports # Third part imports
from rest_framework import status from rest_framework import status
from rest_framework.viewsets import ModelViewSet
from rest_framework.response import Response
from rest_framework.exceptions import APIException from rest_framework.exceptions import APIException
from rest_framework.views import APIView
from rest_framework.filters import SearchFilter from rest_framework.filters import SearchFilter
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from sentry_sdk import capture_exception from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
# Module imports # Module imports
from plane.utils.exception_logger import log_exception
from plane.utils.paginator import BasePaginator from plane.utils.paginator import BasePaginator
@ -57,7 +57,7 @@ class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator):
try: try:
return self.model.objects.all() return self.model.objects.all()
except Exception as e: except Exception as e:
capture_exception(e) log_exception(e)
raise APIException( raise APIException(
"Please check the view", status.HTTP_400_BAD_REQUEST "Please check the view", status.HTTP_400_BAD_REQUEST
) )
@ -90,14 +90,13 @@ class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator):
) )
if isinstance(e, KeyError): if isinstance(e, KeyError):
capture_exception(e) log_exception(e)
return Response( return Response(
{"error": "The required key does not exist."}, {"error": "The required key does not exist."},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
print(e) if settings.DEBUG else print("Server Error") log_exception(e)
capture_exception(e)
return Response( return Response(
{"error": "Something went wrong please try again later"}, {"error": "Something went wrong please try again later"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR, status=status.HTTP_500_INTERNAL_SERVER_ERROR,
@ -185,9 +184,7 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator):
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
if settings.DEBUG: log_exception(e)
print(e)
capture_exception(e)
return Response( return Response(
{"error": "Something went wrong please try again later"}, {"error": "Something went wrong please try again later"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR, status=status.HTTP_500_INTERNAL_SERVER_ERROR,

View File

@ -0,0 +1,15 @@
# Python imports
import logging
# Third party imports
from sentry_sdk import capture_exception
def log_exception(e):
# Log the error
logger = logging.getLogger("plane")
logger.error(e)
# Capture in sentry if configured
capture_exception(e)
return