123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- import uuid
- from django.contrib.postgres.fields import HStoreField
- from django.db import models
- from glitchtip.base_models import CreatedModel
- from glitchtip.model_utils import FromStringIntegerChoices
- from projects.tasks import update_event_project_hourly_statistic
- from user_reports.models import UserReport
- class AbstractEvent(CreatedModel):
- event_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
- data = models.JSONField(help_text="General event data that is searchable")
- timestamp = models.DateTimeField(
- blank=True,
- null=True,
- help_text="Date created as claimed by client it came from",
- )
- class Meta:
- abstract = True
- def __str__(self):
- return self.event_id_hex
- @property
- def event_id_hex(self):
- """The public key without dashes"""
- if self.event_id:
- if isinstance(self.event_id, str):
- return self.event_id
- return self.event_id.hex
- class LogLevel(FromStringIntegerChoices):
- NOTSET = 0, "sample"
- DEBUG = 1, "debug"
- INFO = 2, "info"
- WARNING = 3, "warning"
- ERROR = 4, "error"
- FATAL = 5, "fatal"
- @classmethod
- def from_string(cls, string: str):
- result = super().from_string(string)
- if result:
- return result
- if string == "critical":
- return cls.FATAL
- if string == "log":
- return cls.INFO
- return cls.ERROR
- class Event(AbstractEvent):
- """
- An individual event. An issue is a set of like-events.
- Most is stored in "data" but some fields benefit from being real
- relational data types such as dates and foreign keys
- """
- issue = models.ForeignKey(
- "issues.Issue",
- on_delete=models.CASCADE,
- help_text="Sentry calls this a group",
- )
- level = models.PositiveSmallIntegerField(
- choices=LogLevel.choices, default=LogLevel.ERROR
- )
- errors = models.JSONField(
- null=True,
- blank=True,
- help_text="Event processing errors from event intake, including validation errors",
- )
- tags = HStoreField(default=dict)
- release = models.ForeignKey(
- "releases.Release", blank=True, null=True, on_delete=models.SET_NULL
- )
- class Meta:
- ordering = ["-created"]
- def save(self, *args, **kwargs):
- is_new = self._state.adding
- super().save(*args, **kwargs)
- if is_new:
- update_event_project_hourly_statistic(
- args=[self.issue.project_id, self.created], countdown=60
- )
- def event_json(self):
- """
- OSS Sentry Compatible raw event JSON
- Effectively this combines data and relational data
- """
- event = self.data
- event["event_id"] = self.event_id_hex
- event["project"] = self.issue.project_id
- event["level"] = self.get_level_display()
- event["tags"] = self.tags.items()
- if self.errors:
- event["errors"] = self.errors
- if self.timestamp:
- event["datetime"] = self.timestamp.isoformat().replace("+00:00", "Z")
- if self.release:
- event["release"] = self.release.version
- return event
- @property
- def context(self):
- return self.data.get("extra")
- @property
- def contexts(self):
- return self.data.get("contexts")
- @property
- def culprit(self):
- return self.data.get("culprit")
- @property
- def message(self):
- return self.data.get("message")
- @property
- def user(self):
- return self.data.get("user")
- @property
- def user_report(self):
- return UserReport.objects.filter(event_id=self.pk).first()
- def _build_context(self, context: list, base_line_no: int, is_pre: bool):
- context_length = len(context)
- result = []
- for index, pre_context_line in enumerate(context):
- if is_pre:
- line_no = base_line_no - context_length + index
- else:
- line_no = base_line_no + 1 + index
- result.append(
- [
- line_no,
- pre_context_line,
- ]
- )
- return result
- @property
- def metadata(self):
- return self.data.get("metadata")
- @property
- def packages(self):
- return self.data.get("modules")
- @property
- def platform(self):
- return self.data.get("platform")
- @property
- def sdk(self):
- return self.data.get("sdk")
- @property
- def title(self):
- return self.data.get("title")
- @property
- def type(self):
- return self.data.get("type")
- def next(self, *args, **kwargs):
- try:
- return self.get_next_by_created(**kwargs)
- except Event.DoesNotExist:
- return None
- def previous(self, *args, **kwargs):
- """Get previous object by created date, pass filter kwargs"""
- try:
- return self.get_previous_by_created(**kwargs)
- except Event.DoesNotExist:
- return None
|