Exemple #1
0
 def data(self, value):
     node_id = Event.generate_node_id(self.project_id, self.event_id)
     self._data = NodeData(node_id,
                           data=value,
                           wrapper=EventDict,
                           ref_version=2,
                           ref_func=ref_func)
Exemple #2
0
    def __init__(self, snuba_values):
        assert set(snuba_values.keys()) == set(self.selected_columns)

        self.__dict__ = snuba_values

        # This should be lazy loaded and will only be accessed if we access any
        # properties on self.data
        node_id = SnubaEvent.generate_node_id(self.project_id, self.event_id)
        self.data = NodeData(None, node_id, data=None)
Exemple #3
0
    def __init__(self, snuba_values):
        assert set(snuba_values.keys()) == set(self.selected_columns)

        self.__dict__ = snuba_values

        # self.data is a (lazy) dict of everything we got from nodestore
        node_id = SnubaEvent.generate_node_id(self.snuba_data['project_id'],
                                              self.snuba_data['event_id'])
        self.data = NodeData(None, node_id, data=None, wrapper=EventDict)
Exemple #4
0
    def __init__(self, snuba_values):
        """
            When initializing a SnubaEvent, think about the attributes you
            might need to access on it. If you only need a few properties, and
            they are all available in snuba, then you should use
            `SnubaEvent.selected_colums` (or a subset depending on your needs)
            But if you know you are going to need the entire event body anyway
            (which requires a nodestore lookup) you may as well just initialize
            the event with `SnubaEvent.minimal_colums` and let the rest of of
            the attributes come from nodestore.
        """
        assert all(k in snuba_values for k in SnubaEvent.minimal_columns)

        # self.snuba_data is a dict of all the stuff we got from snuba
        self.snuba_data = snuba_values

        # self.data is a (lazy) dict of everything we got from nodestore
        node_id = SnubaEvent.generate_node_id(self.snuba_data["project_id"],
                                              self.snuba_data["event_id"])
        self.data = NodeData(None, node_id, data=None, wrapper=EventDict)
Exemple #5
0
    def __init__(self, snuba_values):
        """
            When initializing a SnubaEvent, think about the attributes you
            might need to access on it. If you only need a few properties, and
            they are all available in snuba, then you should use
            `SnubaEvent.selected_colums` (or a subset depending on your needs)
            But if you know you are going to need the entire event body anyway
            (which requires a nodestore lookup) you may as well just initialize
            the event with `SnubaEvent.minimal_colums` and let the rest of of
            the attributes come from nodestore.
        """
        assert all(k in snuba_values for k in SnubaEvent.minimal_columns)

        # self.snuba_data is a dict of all the stuff we got from snuba
        self.snuba_data = snuba_values

        # self.data is a (lazy) dict of everything we got from nodestore
        node_id = SnubaEvent.generate_node_id(
            self.snuba_data['project_id'],
            self.snuba_data['event_id'])
        self.data = NodeData(None, node_id, data=None)
Exemple #6
0
class SnubaEvent(EventCommon):
    """
        An event backed by data stored in snuba.

        This is a readonly event and does not support event creation or save.
        The basic event data is fetched from snuba, and the event body is
        fetched from nodestore and bound to the data property in the same way
        as a regular Event.
    """

    # The minimal list of columns we need to get from snuba to bootstrap an
    # event. If the client is planning on loading the entire event body from
    # nodestore anyway, we may as well only fetch the minimum from snuba to
    # avoid duplicated work.
    minimal_columns = ["event_id", "group_id", "project_id", "timestamp"]

    __repr__ = sane_repr("project_id", "group_id")

    def __init__(self, snuba_values):
        """
            When initializing a SnubaEvent, think about the attributes you
            might need to access on it. If you only need a few properties, and
            they are all available in snuba, then you should use
            `SnubaEvent.selected_colums` (or a subset depending on your needs)
            But if you know you are going to need the entire event body anyway
            (which requires a nodestore lookup) you may as well just initialize
            the event with `SnubaEvent.minimal_colums` and let the rest of of
            the attributes come from nodestore.
        """
        assert all(k in snuba_values for k in SnubaEvent.minimal_columns)

        # self.snuba_data is a dict of all the stuff we got from snuba
        self.snuba_data = snuba_values

        # self.data is a (lazy) dict of everything we got from nodestore
        node_id = SnubaEvent.generate_node_id(self.snuba_data["project_id"],
                                              self.snuba_data["event_id"])
        self.data = NodeData(None, node_id, data=None, wrapper=EventDict)

    def __getattr__(self, name):
        """
        Depending on what snuba data this event was initialized with, we may
        have the data available to return, or we may have to look in the
        `data` dict (which would force a nodestore load). All unresolved
        self.foo type accesses will come through here.
        """
        if name in ("_project_cache", "_group_cache", "_environment_cache"):
            raise AttributeError()

        if name in self.snuba_data:
            return self.snuba_data[name]
        else:
            return self.data[name]

    # ============================================
    # Snuba-only implementations of properties that
    # would otherwise require nodestore data.
    # ============================================
    @property
    def tags(self):
        """
        Override of tags property that uses tags from snuba rather than
        the nodestore event body. This might be useful for implementing
        tag deletions without having to rewrite nodestore blobs.
        """
        if "tags.key" in self.snuba_data and "tags.value" in self.snuba_data:
            keys = getattr(self, "tags.key")
            values = getattr(self, "tags.value")
            if keys and values and len(keys) == len(values):
                return sorted(zip(keys, values))
            else:
                return []
        else:
            return super(SnubaEvent, self).tags

    def get_minimal_user(self):
        from sentry.interfaces.user import User

        return User.to_python({
            "id": self.user_id,
            "email": self.email,
            "username": self.username,
            "ip_address": self.ip_address,
        })

    # If the data for these is available from snuba, we assume
    # it was already normalized on the way in and we can just return
    # it, otherwise we defer to EventCommon implementation.
    def get_event_type(self):
        if "type" in self.snuba_data:
            return self.snuba_data["type"]
        return super(SnubaEvent, self).get_event_type()

    @property
    def ip_address(self):
        if "ip_address" in self.snuba_data:
            return self.snuba_data["ip_address"]
        return super(SnubaEvent, self).ip_address

    @property
    def title(self):
        if "title" in self.snuba_data:
            return self.snuba_data["title"]
        return super(SnubaEvent, self).title

    @property
    def culprit(self):
        if "culprit" in self.snuba_data:
            return self.snuba_data["culprit"]
        return super(SnubaEvent, self).culprit

    @property
    def location(self):
        if "location" in self.snuba_data:
            return self.snuba_data["location"]
        return super(SnubaEvent, self).location

    # ====================================================
    # Snuba implementations of the django fields on Event
    # ====================================================
    @property
    def datetime(self):
        """
        Reconstruct the datetime of this event from the snuba timestamp
        """
        # dateutil seems to use tzlocal() instead of UTC even though the string
        # ends with '+00:00', so just replace the TZ with UTC because we know
        # all timestamps from snuba are UTC.
        return parse_date(self.timestamp).replace(tzinfo=pytz.utc)

    @property
    def message(self):
        if "message" in self.snuba_data:
            return self.snuba_data["message"]
        return self.data.get("message")

    @property
    def platform(self):
        if "platform" in self.snuba_data:
            return self.snuba_data["platform"]
        return self.data.get("platform")

    @property
    def id(self):
        # Because a snuba event will never have a django row id, just return
        # the hex event_id here. We should be moving to a world where we never
        # have to reference the row id anyway.
        return self.event_id

    def save(self):
        raise NotImplementedError
Exemple #7
0
class SnubaEvent(EventCommon):
    """
        An event backed by data stored in snuba.

        This is a readonly event and does not support event creation or save.
        The basic event data is fetched from snuba, and the event body is
        fetched from nodestore and bound to the data property in the same way
        as a regular Event.
    """

    # The minimal list of columns we need to get from snuba to bootstrap an
    # event. If the client is planning on loading the entire event body from
    # nodestore anyway, we may as well only fetch the minimum from snuba to
    # avoid duplicated work.
    minimal_columns = [
        'event_id',
        'group_id',
        'project_id',
        'timestamp',
    ]

    # A list of all useful columns we can get from snuba.
    selected_columns = minimal_columns + [
        'culprit',
        'location',
        'message',
        'platform',
        'title',
        'type',

        # Required to provide snuba-only tags
        'tags.key',
        'tags.value',

        # Required to provide snuba-only 'user' interface
        'email',
        'ip_address',
        'user_id',
        'username',
    ]

    objects = SnubaEventManager()

    __repr__ = sane_repr('project_id', 'group_id')

    @classmethod
    def get_event(cls, project_id, event_id, snuba_cols=selected_columns):
        from sentry.utils import snuba
        result = snuba.raw_query(
            start=datetime.utcfromtimestamp(
                0),  # will be clamped to project retention
            end=datetime.utcnow(),  # will be clamped to project retention
            selected_columns=snuba_cols,
            filter_keys={
                'event_id': [event_id],
                'project_id': [project_id],
            },
            referrer='SnubaEvent.get_event',
            limit=1,
        )
        if 'error' not in result and len(result['data']) == 1:
            return SnubaEvent(result['data'][0])
        return None

    def __init__(self, snuba_values):
        """
            When initializing a SnubaEvent, think about the attributes you
            might need to access on it. If you only need a few properties, and
            they are all available in snuba, then you should use
            `SnubaEvent.selected_colums` (or a subset depending on your needs)
            But if you know you are going to need the entire event body anyway
            (which requires a nodestore lookup) you may as well just initialize
            the event with `SnubaEvent.minimal_colums` and let the rest of of
            the attributes come from nodestore.
        """
        assert all(k in snuba_values for k in SnubaEvent.minimal_columns)

        # self.snuba_data is a dict of all the stuff we got from snuba
        self.snuba_data = snuba_values

        # self.data is a (lazy) dict of everything we got from nodestore
        node_id = SnubaEvent.generate_node_id(self.snuba_data['project_id'],
                                              self.snuba_data['event_id'])
        self.data = NodeData(None, node_id, data=None, wrapper=EventDict)

    def __getattr__(self, name):
        """
        Depending on what snuba data this event was initialized with, we may
        have the data available to return, or we may have to look in the
        `data` dict (which would force a nodestore load). All unresolved
        self.foo type accesses will come through here.
        """
        if name in ('_project_cache', '_group_cache', '_environment_cache'):
            raise AttributeError()

        if name in self.snuba_data:
            return self.snuba_data[name]
        else:
            return self.data[name]

    # ============================================
    # Snuba-only implementations of properties that
    # would otherwise require nodestore data.
    # ============================================
    @property
    def tags(self):
        """
        Override of tags property that uses tags from snuba rather than
        the nodestore event body. This might be useful for implementing
        tag deletions without having to rewrite nodestore blobs.
        """
        if 'tags.key' in self.snuba_data and 'tags.value' in self.snuba_data:
            keys = getattr(self, 'tags.key')
            values = getattr(self, 'tags.value')
            if keys and values and len(keys) == len(values):
                return sorted(zip(keys, values))
            else:
                return []
        else:
            return super(SnubaEvent, self).tags

    def get_minimal_user(self):
        from sentry.interfaces.user import User
        return User.to_python({
            'id': self.user_id,
            'email': self.email,
            'username': self.username,
            'ip_address': self.ip_address,
        })

    # If the data for these is availablle from snuba, we asssume
    # it was already normalized on the way in and we can just return
    # it, otherwise we defer to EventCommon implementation.
    def get_event_type(self):
        if 'type' in self.snuba_data:
            return self.snuba_data['type']
        return super(SnubaEvent, self).get_event_type()

    @property
    def ip_address(self):
        if 'ip_address' in self.snuba_data:
            return self.snuba_data['ip_address']
        return super(SnubaEvent, self).ip_address

    @property
    def title(self):
        if 'title' in self.snuba_data:
            return self.snuba_data['title']
        return super(SnubaEvent, self).title

    @property
    def culprit(self):
        if 'culprit' in self.snuba_data:
            return self.snuba_data['culprit']
        return super(SnubaEvent, self).culprit

    @property
    def location(self):
        if 'location' in self.snuba_data:
            return self.snuba_data['location']
        return super(SnubaEvent, self).location

    # ====================================================
    # Snuba implementations of the django fields on Event
    # ====================================================
    @property
    def datetime(self):
        """
        Reconstruct the datetime of this event from the snuba timestamp
        """
        # dateutil seems to use tzlocal() instead of UTC even though the string
        # ends with '+00:00', so just replace the TZ with UTC because we know
        # all timestamps from snuba are UTC.
        return parse_date(self.timestamp).replace(tzinfo=pytz.utc)

    @property
    def time_spent(self):
        return None

    @property
    def message(self):
        if 'message' in self.snuba_data:
            return self.snuba_data['message']
        return self.data.get('message')

    @property
    def platform(self):
        if 'platform' in self.snuba_data:
            return self.snuba_data['platform']
        return self.data.get('platform')

    @property
    def id(self):
        # Because a snuba event will never have a django row id, just return
        # the hex event_id here. We should be moving to a world where we never
        # have to reference the row id anyway.
        return self.event_id

    def next_event_id(self, environments=None):
        from sentry.utils import snuba

        conditions = [['timestamp', '>=', self.timestamp],
                      [['timestamp', '>', self.timestamp],
                       ['event_id', '>', self.event_id]]]

        if environments:
            conditions.append(['environment', 'IN', environments])

        result = snuba.raw_query(
            start=self.datetime,  # gte current event
            end=datetime.utcnow(),  # will be clamped to project retention
            selected_columns=['event_id'],
            conditions=conditions,
            filter_keys={
                'project_id': [self.project_id],
                'issue': [self.group_id] if self.group_id else [],
            },
            orderby=['timestamp', 'event_id'],
            limit=1,
            referrer='SnubaEvent.next_event_id',
        )

        if 'error' in result or len(result['data']) == 0:
            return None

        return six.text_type(result['data'][0]['event_id'])

    def prev_event_id(self, environments=None):
        from sentry.utils import snuba

        conditions = [['timestamp', '<=', self.timestamp],
                      [['timestamp', '<', self.timestamp],
                       ['event_id', '<', self.event_id]]]

        if environments:
            conditions.append(['environment', 'IN', environments])

        result = snuba.raw_query(
            start=datetime.utcfromtimestamp(
                0),  # will be clamped to project retention
            end=self.datetime,  # lte current event
            selected_columns=['event_id'],
            conditions=conditions,
            filter_keys={
                'project_id': [self.project_id],
                'issue': [self.group_id] if self.group_id else [],
            },
            orderby=['-timestamp', '-event_id'],
            limit=1,
            referrer='SnubaEvent.prev_event_id',
        )

        if 'error' in result or len(result['data']) == 0:
            return None

        return six.text_type(result['data'][0]['event_id'])

    def save(self):
        raise NotImplementedError
Exemple #8
0
class SnubaEvent(EventCommon):
    """
        An event backed by data stored in snuba.

        This is a readonly event and does not support event creation or save.
        The basic event data is fetched from snuba, and the event body is
        fetched from nodestore and bound to the data property in the same way
        as a regular Event.
    """

    # The minimal list of columns we need to get from snuba to bootstrap an
    # event. If the client is planning on loading the entire event body from
    # nodestore anyway, we may as well only fetch the minimum from snuba to
    # avoid duplicated work.
    minimal_columns = [
        'event_id',
        'group_id',
        'project_id',
        'timestamp',
    ]

    # A list of all useful columns we can get from snuba.
    selected_columns = minimal_columns + [
        'type',
        'culprit',
        'location',
        'message',
        'platform',
        'title',
        'type',

        # Required to provide snuba-only tags
        'tags.key',
        'tags.value',

        # Required to provide snuba-only 'user' interface
        'email',
        'ip_address',
        'user_id',
        'username',
    ]

    objects = SnubaEventManager()

    __repr__ = sane_repr('project_id', 'group_id')

    @classmethod
    def get_event(cls, project_id, event_id, snuba_cols=selected_columns):
        from sentry.utils import snuba
        result = snuba.raw_query(
            start=datetime.utcfromtimestamp(0),  # will be clamped to project retention
            end=datetime.utcnow(),  # will be clamped to project retention
            selected_columns=snuba_cols,
            filter_keys={
                'event_id': [event_id],
                'project_id': [project_id],
            },
            referrer='SnubaEvent.get_event',
        )
        if 'error' not in result and len(result['data']) == 1:
            return SnubaEvent(result['data'][0])
        return None

    def __init__(self, snuba_values):
        """
            When initializing a SnubaEvent, think about the attributes you
            might need to access on it. If you only need a few properties, and
            they are all available in snuba, then you should use
            `SnubaEvent.selected_colums` (or a subset depending on your needs)
            But if you know you are going to need the entire event body anyway
            (which requires a nodestore lookup) you may as well just initialize
            the event with `SnubaEvent.minimal_colums` and let the rest of of
            the attributes come from nodestore.
        """
        assert all(k in snuba_values for k in SnubaEvent.minimal_columns)

        # self.snuba_data is a dict of all the stuff we got from snuba
        self.snuba_data = snuba_values

        # self.data is a (lazy) dict of everything we got from nodestore
        node_id = SnubaEvent.generate_node_id(
            self.snuba_data['project_id'],
            self.snuba_data['event_id'])
        self.data = NodeData(None, node_id, data=None)

    def __getattr__(self, name):
        """
        Depending on what snuba data this event was initialized with, we may
        have the data available to return, or we may have to look in the
        `data` dict (which would force a nodestore load). All unresolved
        self.foo type accesses will come through here.
        """
        if name in ('_project_cache', '_group_cache', '_environment_cache'):
            raise AttributeError()

        if name in self.snuba_data:
            return self.snuba_data[name]
        else:
            return self.data[name]

    # ============================================
    # Snuba-only implementations of properties that
    # would otherwise require nodestore data.
    # ============================================
    @property
    def tags(self):
        """
        Override of tags property that uses tags from snuba rather than
        the nodestore event body. This might be useful for implementing
        tag deletions without having to rewrite nodestore blobs.
        """
        if 'tags.key' in self.snuba_data and 'tags.value' in self.snuba_data:
            keys = getattr(self, 'tags.key')
            values = getattr(self, 'tags.value')
            if keys and values and len(keys) == len(values):
                return sorted(zip(keys, values))
            else:
                return []
        else:
            return super(SnubaEvent, self).tags

    def get_minimal_user(self):
        from sentry.interfaces.user import User
        return User.to_python({
            'id': self.user_id,
            'email': self.email,
            'username': self.username,
            'ip_address': self.ip_address,
        })

    # If the data for these is availablle from snuba, we asssume
    # it was already normalized on the way in and we can just return
    # it, otherwise we defer to EventCommon implementation.
    def get_event_type(self):
        if 'type' in self.snuba_data:
            return self.snuba_data['type']
        return super(SnubaEvent, self).get_event_type()

    @property
    def ip_address(self):
        if 'ip_address' in self.snuba_data:
            return self.snuba_data['ip_address']
        return super(SnubaEvent, self).ip_address

    @property
    def title(self):
        if 'title' in self.snuba_data:
            return self.snuba_data['title']
        return super(SnubaEvent, self).title

    @property
    def culprit(self):
        if 'culprit' in self.snuba_data:
            return self.snuba_data['culprit']
        return super(SnubaEvent, self).culprit

    @property
    def location(self):
        if 'location' in self.snuba_data:
            return self.snuba_data['location']
        return super(SnubaEvent, self).location

    # ====================================================
    # Snuba implementations of the django fields on Event
    # ====================================================
    @property
    def datetime(self):
        """
        Reconstruct the datetime of this event from the snuba timestamp
        """
        # dateutil seems to use tzlocal() instead of UTC even though the string
        # ends with '+00:00', so just replace the TZ with UTC because we know
        # all timestamps from snuba are UTC.
        return parse_date(self.timestamp).replace(tzinfo=pytz.utc)

    @property
    def time_spent(self):
        return None

    @property
    def message(self):
        if 'message' in self.snuba_data:
            return self.snuba_data['message']
        return self.data.get('message')

    @property
    def platform(self):
        if 'platform' in self.snuba_data:
            return self.snuba_data['platform']
        return self.data.get('platform')

    @property
    def id(self):
        # Because a snuba event will never have a django row id, just return
        # the hex event_id here. We should be moving to a world where we never
        # have to reference the row id anyway.
        return self.event_id

    def next_event_id(self, environments=None):
        from sentry.utils import snuba

        conditions = [
            ['timestamp', '>=', self.timestamp],
            [['timestamp', '>', self.timestamp], ['event_id', '>', self.event_id]]
        ]

        if environments:
            conditions.append(['environment', 'IN', environments])

        result = snuba.raw_query(
            start=self.datetime,  # gte current event
            end=datetime.utcnow(),  # will be clamped to project retention
            selected_columns=['event_id'],
            conditions=conditions,
            filter_keys={
                'project_id': [self.project_id],
                'issue': [self.group_id],
            },
            orderby=['timestamp', 'event_id'],
            limit=1,
            referrer='SnubaEvent.next_event_id',
        )

        if 'error' in result or len(result['data']) == 0:
            return None

        return six.text_type(result['data'][0]['event_id'])

    def prev_event_id(self, environments=None):
        from sentry.utils import snuba

        conditions = [
            ['timestamp', '<=', self.timestamp],
            [['timestamp', '<', self.timestamp], ['event_id', '<', self.event_id]]
        ]

        if environments:
            conditions.append(['environment', 'IN', environments])

        result = snuba.raw_query(
            start=datetime.utcfromtimestamp(0),  # will be clamped to project retention
            end=self.datetime,  # lte current event
            selected_columns=['event_id'],
            conditions=conditions,
            filter_keys={
                'project_id': [self.project_id],
                'issue': [self.group_id],
            },
            orderby=['-timestamp', '-event_id'],
            limit=1,
            referrer='SnubaEvent.prev_event_id',
        )

        if 'error' in result or len(result['data']) == 0:
            return None

        return six.text_type(result['data'][0]['event_id'])

    def save(self):
        raise NotImplementedError