class GroupCountByMinute(Model): """ Stores the total number of messages seen by a group at N minute intervals. e.g. if it happened at 08:34:55 the time would be normalized to 08:30:00 """ project = models.ForeignKey(Project, null=True) group = models.ForeignKey(Group) date = models.DateTimeField(db_index=True) # normalized to HH:MM:00 times_seen = BoundedPositiveIntegerField(default=0) time_spent_total = models.FloatField(default=0) time_spent_count = BoundedIntegerField(default=0) objects = BaseManager() class Meta: db_table = 'sentry_messagecountbyminute' unique_together = (('project', 'group', 'date'), ) __repr__ = sane_repr('project_id', 'group_id', 'date')
class MessageCountByMinute(Model): """ Stores the total number of messages seen by a group at N minute intervals. e.g. if it happened at 08:34:55 the time would be normalized to 08:30:00 """ project = models.ForeignKey(Project, null=True) group = models.ForeignKey(Group) date = models.DateTimeField(db_index=True) # normalized to HH:MM:00 times_seen = models.PositiveIntegerField(default=0) time_spent_total = models.FloatField(default=0) time_spent_count = models.IntegerField(default=0) objects = BaseManager() class Meta: unique_together = (('project', 'group', 'date'),) def __unicode__(self): return u'group_id=%s, times_seen=%s, date=%s' % (self.group_id, self.times_seen, self.date)
class TagValue(Model): """ Stores references to available filters. """ project = models.ForeignKey(Project, null=True) key = models.CharField(max_length=MAX_TAG_KEY_LENGTH) value = models.CharField(max_length=MAX_TAG_VALUE_LENGTH) data = GzippedDictField(blank=True, null=True) times_seen = BoundedPositiveIntegerField(default=0) last_seen = models.DateTimeField( default=timezone.now, db_index=True, null=True) first_seen = models.DateTimeField( default=timezone.now, db_index=True, null=True) objects = BaseManager() class Meta: db_table = 'sentry_filtervalue' unique_together = (('project', 'key', 'value'),) __repr__ = sane_repr('project_id', 'key', 'value')
class MessageFilterValue(Model): """ Stores the total number of messages seen by a group matching the given filter. """ project = models.ForeignKey(Project, null=True) group = models.ForeignKey(Group) times_seen = models.PositiveIntegerField(default=0) key = models.CharField(choices=FILTER_KEYS, max_length=32) value = models.CharField(max_length=200) last_seen = models.DateTimeField(default=datetime.now, db_index=True, null=True) first_seen = models.DateTimeField(default=datetime.now, db_index=True, null=True) objects = BaseManager() class Meta: unique_together = (('project', 'key', 'value', 'group'),) def __unicode__(self): return u'group_id=%s, times_seen=%s, key=%s, value=%s' % (self.group_id, self.times_seen, self.key, self.value)
class FilterValue(Model): """ Stores references to available filters. """ project = models.ForeignKey(Project, null=True) key = models.CharField(max_length=32) value = models.CharField(max_length=200) times_seen = models.PositiveIntegerField(default=0) last_seen = models.DateTimeField(default=timezone.now, db_index=True, null=True) first_seen = models.DateTimeField(default=timezone.now, db_index=True, null=True) objects = BaseManager() class Meta: unique_together = (('project', 'key', 'value'), ) __repr__ = sane_repr('project_id', 'key', 'value')
class ProjectKey(Model): project = models.ForeignKey(Project, related_name='key_set') public_key = models.CharField(max_length=32, unique=True, null=True) secret_key = models.CharField(max_length=32, unique=True, null=True) user = models.ForeignKey(User, null=True) objects = BaseManager(cache_fields=( 'public_key', 'secret_key', )) def __unicode__(self): return u'project=%s, user=%s' % (self.project_id, self.user_id) @classmethod def generate_api_key(cls): return uuid.uuid4().hex def save(self, *args, **kwargs): if not self.public_key: self.public_key = ProjectKey.generate_api_key() if not self.secret_key: self.secret_key = ProjectKey.generate_api_key() super(ProjectKey, self).save(*args, **kwargs) def get_dsn(self, domain=None, secure=True, public=False): # TODO: change the DSN to use project slug once clients are compatible if not public: key = '%s:%s' % (self.public_key, self.secret_key) else: key = self.public_key urlparts = urlparse.urlparse(settings.URL_PREFIX) return '%s://%s@%s/%s' % ( urlparts.scheme, key, urlparts.netloc + urlparts.path, self.project_id, )
class ProjectDomain(Model): """ Currently unused. Planned for 'trusted domains' for JS apis. """ project = models.ForeignKey(Project, related_name="domain_set") domain = models.CharField(max_length=128) objects = BaseManager() class Meta: unique_together = (('project', 'domain'),) def __unicode__(self): return u'project=%s, domain=%s' % (self.project_id, self.domain) @classmethod def test(cls, project, url, strict=False): """ Tests whether the ``url`` is a trusted domain for the given project. """ if not url: return False url = urlparse.urlsplit(url).hostname if not url: # If we fail to parse the referral url return False if url in ('127.0.0.1', 'localhost'): return True if url.endswith('.local'): return True url = url.split('.') domains = ProjectDomain.objects.filter(project=project).values_list('domain', flat=True) for d in domains: d = d.split('.') if url[-len(d):] == d: return True return False
class Event(EventBase): """ An individual event. """ group = models.ForeignKey(Group, blank=True, null=True, related_name="event_set") event_id = models.CharField(max_length=32, null=True, db_column="message_id") datetime = models.DateTimeField(default=timezone.now, db_index=True) time_spent = models.FloatField(null=True) server_name = models.CharField(max_length=128, db_index=True, null=True) site = models.CharField(max_length=128, db_index=True, null=True) objects = BaseManager() class Meta: verbose_name = _('message') verbose_name_plural = _('messages') db_table = 'sentry_message' unique_together = ('project', 'event_id') __repr__ = sane_repr('project_id', 'group_id', 'checksum') @memoize def interfaces(self): result = [] for key, data in self.data.iteritems(): if '.' not in key: continue try: cls = import_string(key) except ImportError: continue # suppress invalid interfaces value = safe_execute(cls, **data) if not value: continue result.append((key, value)) return SortedDict((k, v) for k, v in sorted( result, key=lambda x: x[1].get_score(), reverse=True)) def get_version(self): if not self.data: return if '__sentry__' not in self.data: return if 'version' not in self.data['__sentry__']: return module = self.data['__sentry__'].get('module', 'ver') return module, self.data['__sentry__']['version'] def get_tags(self): try: return [(t, v) for t, v in self.data.get('tags') or () if not t.startswith('sentry:')] except ValueError: # at one point Sentry allowed invalid tag sets such as (foo, bar) # vs ((tag, foo), (tag, bar)) return [] def as_dict(self): # We use a SortedDict to keep elements ordered for a potential JSON serializer data = SortedDict() data['id'] = self.event_id data['checksum'] = self.checksum data['project'] = self.project.slug data['logger'] = self.logger data['level'] = self.get_level_display() data['culprit'] = self.culprit for k, v in sorted(self.data.iteritems()): data[k] = v return data @property def size(self): return len(unicode(vars(self)))
class Event(MessageBase): """ An individual event. """ group = models.ForeignKey(Group, blank=True, null=True, related_name="event_set") event_id = models.CharField(max_length=32, null=True, db_column="message_id") datetime = models.DateTimeField(default=timezone.now, db_index=True) time_spent = models.FloatField(null=True) server_name = models.CharField(max_length=128, db_index=True, null=True) site = models.CharField(max_length=128, db_index=True, null=True) objects = BaseManager() class Meta: verbose_name = _('message') verbose_name_plural = _('messages') db_table = 'sentry_message' unique_together = ('project', 'event_id') def __unicode__(self): return self.error() def get_absolute_url(self): if self.project_id: return reverse('sentry-group-event', kwargs={ 'group_id': self.group_id, 'event_id': self.pk, 'project_id': self.project.slug }) return '#' @cached_property def request(self): data = self.data if 'META' in data: kwargs = { 'META': data.get('META'), 'GET': data.get('GET'), 'POST': data.get('POST'), 'FILES': data.get('FILES'), 'COOKIES': data.get('COOKIES'), 'url': data.get('url'), } elif 'sentry.interfaces.Http' in data: http = data['sentry.interfaces.Http'] kwargs = {'META': http} else: return MockDjangoRequest() fake_request = MockDjangoRequest(**kwargs) if kwargs['url']: fake_request.path_info = '/' + kwargs['url'].split('/', 3)[-1] else: fake_request.path_info = '' fake_request.path = fake_request.path_info return fake_request @cached_property def interfaces(self): result = [] for key, data in self.data.iteritems(): if '.' not in key: continue try: cls = import_string(key) except ImportError: pass # suppress invalid interfaces value = cls(**data) result.append((value.score, key, value)) return SortedDict( (k, v) for _, k, v in sorted(result, key=lambda x: x[0], reverse=True)) def get_version(self): if not self.data: return if '__sentry__' not in self.data: return if 'version' not in self.data['__sentry__']: return module = self.data['__sentry__'].get('module', 'ver') return module, self.data['__sentry__']['version'] def as_dict(self): # We use a SortedDict to keep elements ordered for a potential JSON serializer data = SortedDict() data['id'] = self.event_id data['checksum'] = self.checksum data['project'] = self.project.slug data['logger'] = self.logger data['level'] = self.get_level_display() data['culprit'] = self.culprit for k, v in sorted(self.data.iteritems()): data[k] = v return data
class Event(MessageBase): """ An individual event. """ group = models.ForeignKey(Group, blank=True, null=True, related_name="event_set") event_id = models.CharField(max_length=32, null=True, db_column="message_id") datetime = models.DateTimeField(default=datetime.now, db_index=True) time_spent = models.FloatField(null=True) server_name = models.CharField(max_length=128, db_index=True, null=True) site = models.CharField(max_length=128, db_index=True, null=True) objects = BaseManager() class Meta: verbose_name = _('message') verbose_name_plural = _('messages') db_table = 'sentry_message' unique_together = ('project', 'event_id') def __unicode__(self): return self.error() def get_absolute_url(self): if self.project_id: return reverse('sentry-group-event', kwargs={'group_id': self.group_id, 'event_id': self.pk, 'project_id': self.project_id}) return '#' @cached_property def request(self): data = self.data if 'META' in data: kwargs = { 'META': data.get('META'), 'GET': data.get('GET'), 'POST': data.get('POST'), 'FILES': data.get('FILES'), 'COOKIES': data.get('COOKIES'), 'url': data.get('url'), } elif 'sentry.interfaces.Http' in data: http = data['sentry.interfaces.Http'] kwargs = { 'META': http } else: return MockDjangoRequest() fake_request = MockDjangoRequest(**kwargs) if kwargs['url']: fake_request.path_info = '/' + kwargs['url'].split('/', 3)[-1] else: fake_request.path_info = '' fake_request.path = fake_request.path_info return fake_request @cached_property def interfaces(self): result = [] for k, v in self.data.iteritems(): if '.' not in k: continue m, c = k.rsplit('.', 1) cls = getattr(__import__(m, {}, {}, [c]), c) v = cls(**v) result.append((v.score, k, v)) return SortedDict((k, v) for _, k, v in sorted(result, key=lambda x: x[0], reverse=True)) def get_version(self): if not self.data: return if '__sentry__' not in self.data: return if 'version' not in self.data['__sentry__']: return module = self.data['__sentry__'].get('module', 'ver') return module, self.data['__sentry__']['version']