def get_stack_info(frames): results = [] for frame in frames: # Support hidden frames if frame.f_locals.get('__traceback_hide__'): continue filename = frame.f_code.co_filename function = frame.f_code.co_name lineno = frame.f_lineno - 1 loader = frame.f_globals.get('__loader__') module_name = frame.f_globals.get('__name__') or '?' pre_context_lineno, pre_context, context_line, post_context = get_lines_from_file(filename, lineno, 7, loader, module_name) if pre_context_lineno is not None: results.append({ 'id': id(frame), 'filename': filename, 'module': module_name, 'function': function, 'lineno': lineno + 1, # TODO: vars need to be references 'vars': transform(frame.f_locals.items()), 'pre_context': pre_context, 'context_line': context_line, 'post_context': post_context, 'pre_context_lineno': pre_context_lineno + 1, }) return results
def capture(self, exc_info=None, **kwargs): if exc_info is None: exc_info = sys.exc_info() exc_type, exc_value, exc_traceback = exc_info tags = [('level', 'error')] culprit = self._get_culprit(exc_info[2]) if hasattr(exc_type, '__class__'): exc_module = exc_type.__class__.__module__ if exc_module == '__builtin__': exc_type = exc_type.__name__ else: exc_type = '%s.%s' % (exc_module, exc_type.__name__) else: exc_module = None exc_type = exc_type.__name__ # if isinstance(exc_value, TemplateSyntaxError) and hasattr(exc_value, 'source'): # origin, (start, end) = exc_value.source # result['template'] = (origin.reload(), start, end, origin.name) # result['tags'].append(('template', origin.loadname)) return { 'culprit': culprit, 'tags': tags, 'sentry.interfaces.Exception': { 'value': transform(exc_value), 'type': exc_type, 'frames': self._get_traceback_frames(exc_traceback) }, }
def get_stack_info(frames): results = [] for frame in frames: # Support hidden frames if frame.f_locals.get('__traceback_hide__'): continue filename = frame.f_code.co_filename function = frame.f_code.co_name lineno = frame.f_lineno - 1 loader = frame.f_globals.get('__loader__') module_name = frame.f_globals.get('__name__') pre_context_lineno, pre_context, context_line, post_context = get_lines_from_file(filename, lineno, 7, loader, module_name) if pre_context_lineno is not None: results.append({ 'id': id(frame), 'filename': filename, 'module': module_name, 'function': function, 'lineno': lineno + 1, # TODO: vars need to be references 'vars': transform(frame.f_locals.items()), 'pre_context': pre_context, 'context_line': context_line, 'post_context': post_context, 'pre_context_lineno': pre_context_lineno + 1, }) return results
def capture(self, exc_info=None, **kwargs): def contains(iterator, value): for k in iterator: if value.startswith(k): return True return False if exc_info is None: exc_info = sys.exc_info() exc_type, exc_value, exc_traceback = exc_info tags = [("level", "error")] if app.config["INCLUDE_PATHS"]: modules = app.config["INCLUDE_PATHS"] else: modules = [] # We iterate through each frame looking for an app in INSTALLED_APPS # When one is found, we mark it as last "best guess" (best_guess) and then # check it against SENTRY_EXCLUDE_PATHS. If it isnt listed, then we # use this option. If nothing is found, we use the "best guess". best_guess = None view = None for tb in self._iter_tb(exc_info[2]): frame = tb.tb_frame try: view = ".".join([frame.f_globals["__name__"], frame.f_code.co_name]) except: continue if contains(modules, view): if not (contains(app.config["EXCLUDE_PATHS"], view) and best_guess): best_guess = view elif best_guess: break if best_guess: view = best_guess if view: tags.append(("culprit", view)) if hasattr(exc_type, "__class__"): exc_module = exc_type.__class__.__module__ if exc_module == "__builtin__": exc_type = exc_type.__name__ else: exc_type = "%s.%s" % (exc_module, exc_type.__name__) else: exc_module = None exc_type = exc_type.__name__ result = {"value": transform(exc_value), "type": exc_type, "frames": self._get_traceback_frames(exc_traceback)} # if isinstance(exc_value, TemplateSyntaxError) and hasattr(exc_value, 'source'): # origin, (start, end) = exc_value.source # result['template'] = (origin.reload(), start, end, origin.name) # result['tags'].append(('template', origin.loadname)) return {"data": result, "tags": tags}
def _postWithKey(self, data): resp = self.client.post( reverse('sentry-store'), { 'data': base64.b64encode(pickle.dumps(transform(data))), 'key': settings.KEY, }) return resp
def test_dict_keys(self): x = {u'foo': 'bar'} result = transform(x) keys = result.keys() self.assertEquals(len(keys), 1) self.assertEquals(keys[0], 'foo') self.assertTrue(isinstance(keys[0], str))
def test_handles_gettext_lazy(self): def fake_gettext(to_translate): return u'Igpay Atinlay' fake_gettext_lazy = lazy(fake_gettext, str) self.assertEquals( pickle.loads(pickle.dumps( transform(fake_gettext_lazy("something")))), u'Igpay Atinlay')
def test_transform_handles_gettext_lazy(self): def fake_gettext(to_translate): return u'Igpay Atinlay' fake_gettext_lazy = lazy(fake_gettext, str) self.assertEquals( pickle.loads( pickle.dumps(transform(fake_gettext_lazy("something")))), u'Igpay Atinlay')
def _postWithSignature(self, data): ts = time.time() message = base64.b64encode(json.dumps(transform(data))) sig = get_signature(message, ts) resp = self.client.post(reverse('sentry-store'), message, content_type='application/octet-stream', HTTP_AUTHORIZATION=get_auth_header(sig, ts, '_postWithSignature'), ) return resp
def testUngzippedData(self): kwargs = {'message': 'hello', 'server_name': 'not_dcramer.local', 'level': 40, 'site': 'not_a_real_site'} resp = self.client.post(reverse('sentry-store'), { 'data': base64.b64encode(pickle.dumps(transform(kwargs))), 'key': conf.KEY, }) self.assertEquals(resp.status_code, 200) instance = Message.objects.get() self.assertEquals(instance.message, 'hello') self.assertEquals(instance.server_name, 'not_dcramer.local') self.assertEquals(instance.site, 'not_a_real_site') self.assertEquals(instance.level, 40)
def create_from_exception(self, exc_info=None, **kwargs): """ Creates an error log from an exception. """ new_exc = bool(exc_info) if not exc_info or exc_info is True: exc_info = sys.exc_info() data = kwargs.pop('data', {}) or {} try: exc_type, exc_value, exc_traceback = exc_info frames = varmap( shorten, get_stack_info(iter_traceback_frames(exc_traceback))) if hasattr(exc_type, '__class__'): exc_module = exc_type.__class__.__module__ else: exc_module = None data['__sentry__'] = {} data['__sentry__']['frames'] = frames data['__sentry__']['exception'] = [exc_module, exc_value.args] # As of r16833 (Django) all exceptions may contain a ``django_template_source`` attribute (rather than the # legacy ``TemplateSyntaxError.source`` check) which describes template information. if hasattr(exc_value, 'django_template_source') or ((isinstance(exc_value, TemplateSyntaxError) and \ isinstance(getattr(exc_value, 'source', None), (tuple, list)) and isinstance(exc_value.source[0], LoaderOrigin))): origin, (start, end) = getattr(exc_value, 'django_template_source', exc_value.source) data['__sentry__']['template'] = (origin.reload(), start, end, origin.name) kwargs['view'] = origin.loadname tb_message = '\n'.join( traceback.format_exception(exc_type, exc_value, exc_traceback)) kwargs.setdefault('message', transform(force_unicode(exc_value))) return self.process(class_name=exc_type.__name__, traceback=tb_message, data=data, **kwargs) finally: if new_exc: try: del exc_info except Exception, e: logger.exception(e)
def create_from_exception(self, exc_info=None, **kwargs): """ Creates an error log from an exception. """ new_exc = bool(exc_info) if not exc_info or exc_info is True: exc_info = sys.exc_info() data = kwargs.pop('data', {}) or {} try: exc_type, exc_value, exc_traceback = exc_info frames = varmap(shorten, get_stack_info(iter_traceback_frames(exc_traceback))) if hasattr(exc_type, '__class__'): exc_module = exc_type.__class__.__module__ else: exc_module = None data['__sentry__'] = {} data['__sentry__']['frames'] = frames data['__sentry__']['exception'] = [exc_module, exc_value.args] # As of r16833 (Django) all exceptions may contain a ``django_template_source`` attribute (rather than the # legacy ``TemplateSyntaxError.source`` check) which describes template information. if hasattr(exc_value, 'django_template_source') or ((isinstance(exc_value, TemplateSyntaxError) and \ isinstance(getattr(exc_value, 'source', None), (tuple, list)) and isinstance(exc_value.source[0], LoaderOrigin))): origin, (start, end) = getattr(exc_value, 'django_template_source', exc_value.source) data['__sentry__']['template'] = (origin.reload(), start, end, origin.name) kwargs['view'] = origin.loadname tb_message = '\n'.join(traceback.format_exception(exc_type, exc_value, exc_traceback)) kwargs.setdefault('message', transform(force_unicode(exc_value))) return self.process( class_name=exc_type.__name__, traceback=tb_message, data=data, **kwargs ) finally: if new_exc: try: del exc_info except Exception, e: logger.exception(e)
def capture(self, exc_info=None, **kwargs): if exc_info is None: exc_info = sys.exc_info() exc_type, exc_value, exc_traceback = exc_info tags = [('level', 'error')] culprit = self._get_culprit(exc_info[2]) if hasattr(exc_type, '__class__'): exc_module = exc_type.__class__.__module__ if exc_module == '__builtin__': exc_type = exc_type.__name__ else: exc_type = '%s.%s' % (exc_module, exc_type.__name__) else: exc_module = None exc_type = exc_type.__name__ # if isinstance(exc_value, TemplateSyntaxError) and hasattr(exc_value, 'source'): # origin, (start, end) = exc_value.source # result['template'] = (origin.reload(), start, end, origin.name) # result['tags'].append(('template', origin.loadname)) return { 'culprit': culprit, 'tags': tags, 'sentry.interfaces.Exception': { 'value': transform(exc_value), 'type': exc_type, }, 'sentry.interfaces.Stacktrace': { 'frames': self._get_traceback_frames(exc_traceback) }, }
def get_prep_value(self, value): if value is None: return return base64.b64encode(pickle.dumps(transform(value)).encode('zlib'))
def process(self, **kwargs): "Processes the message before passing it on to the server" from sentry.utils import get_filters if kwargs.get('data'): # Ensure we're not changing the original data which was passed # to Sentry kwargs['data'] = kwargs['data'].copy() request = kwargs.pop('request', None) if request: if not kwargs.get('data'): kwargs['data'] = {} if not request.POST and request.raw_post_data: post_data = request.raw_post_data else: post_data = request.POST kwargs['data'].update( dict( META=request.META, POST=post_data, GET=request.GET, COOKIES=request.COOKIES, )) if not kwargs.get('url'): kwargs['url'] = request.build_absolute_uri() kwargs.setdefault('level', logging.ERROR) kwargs.setdefault('server_name', settings.NAME) # save versions of all installed apps if 'data' not in kwargs or '__sentry__' not in (kwargs['data'] or {}): if kwargs.get('data') is None: kwargs['data'] = {} kwargs['data']['__sentry__'] = {} versions = get_versions() kwargs['data']['__sentry__']['versions'] = versions # Shorten lists/strings for k, v in kwargs['data'].iteritems(): if k == '__sentry__': continue kwargs['data'][k] = shorten(v) if kwargs.get('view'): # get list of modules from right to left parts = kwargs['view'].split('.') module_list = [ '.'.join(parts[:idx]) for idx in xrange(1, len(parts) + 1) ][::-1] version = None module = None for m in module_list: if m in versions: module = m version = versions[m] # store our "best guess" for application version if version: kwargs['data']['__sentry__'].update({ 'version': version, 'module': module, }) if 'checksum' not in kwargs: checksum = construct_checksum(**kwargs) else: checksum = kwargs['checksum'] (is_thrashing, message_id) = self.check_throttle(checksum) if is_thrashing: if request and message_id: # attach the sentry object to the request request.sentry = { 'id': message_id, 'thrashed': True, } return message_id for filter_ in get_filters(): kwargs = filter_(None).process(kwargs) or kwargs # create ID client-side so that it can be passed to application message_id = uuid.uuid4().hex kwargs['message_id'] = message_id # Make sure all data is coerced kwargs['data'] = transform(kwargs['data']) if 'timestamp' not in kwargs: kwargs['timestamp'] = datetime.datetime.now() self.send(**kwargs) if request: # attach the sentry object to the request request.sentry = { 'id': message_id, 'trashed': False, } # store the last message_id incase we hit thrashing limits self.set_last_message_id(checksum, message_id) return message_id
def create_from_exception(self, exc_info=None, **kwargs): """ Creates an error log from an exception. """ if not exc_info: exc_info = sys.exc_info() exc_type, exc_value, exc_traceback = exc_info reporter = ExceptionReporter(None, exc_type, exc_value, exc_traceback) frames = varmap(shorten, reporter.get_traceback_frames()) if not kwargs.get('view'): # This should be cached modules = get_installed_apps() if settings.INCLUDE_PATHS: modules = set(list(modules) + settings.INCLUDE_PATHS) def iter_tb_frames(tb): while tb: yield tb.tb_frame tb = tb.tb_next def contains(iterator, value): for k in iterator: if value.startswith(k): return True return False # We iterate through each frame looking for an app in INSTALLED_APPS # When one is found, we mark it as last "best guess" (best_guess) and then # check it against SENTRY_EXCLUDE_PATHS. If it isnt listed, then we # use this option. If nothing is found, we use the "best guess". best_guess = None view = None for frame in iter_tb_frames(exc_traceback): try: view = '.'.join( [frame.f_globals['__name__'], frame.f_code.co_name]) except: continue if contains(modules, view): if not (contains(settings.EXCLUDE_PATHS, view) and best_guess): best_guess = view elif best_guess: break if best_guess: view = best_guess if view: kwargs['view'] = view data = kwargs.pop('data', {}) or {} if hasattr(exc_type, '__class__'): exc_module = exc_type.__class__.__module__ else: exc_module = None data['__sentry__'] = { 'exc': map(transform, [exc_module, exc_value.args, frames]), } if (isinstance(exc_value, TemplateSyntaxError) and \ isinstance(getattr(exc_value, 'source', None), (tuple, list)) and isinstance(exc_value.source[0], LoaderOrigin)): origin, (start, end) = exc_value.source data['__sentry__'].update({ 'template': (origin.reload(), start, end, origin.name), }) kwargs['view'] = origin.loadname tb_message = '\n'.join( traceback.format_exception(exc_type, exc_value, exc_traceback)) kwargs.setdefault('message', transform(force_unicode(exc_value))) return self.process(class_name=exc_type.__name__, traceback=tb_message, data=data, **kwargs)
def handle(self, exc_info=None): # TODO: remove Django specifics from django.template import TemplateSyntaxError from django.views.debug import ExceptionReporter if exc_info is None: exc_info = sys.exc_info() exc_type, exc_value, exc_traceback = exc_info result = { 'tags': [('level', 'error')], } reporter = ExceptionReporter(None, exc_type, exc_value, exc_traceback) exc_frames = varmap(shorten, reporter.get_traceback_frames()) # This should be cached # modules = get_installed_apps() modules = [] if app.config['INCLUDE_PATHS']: modules = set(list(modules) + app.config['INCLUDE_PATHS']) def iter_tb_frames(tb): while tb: yield tb.tb_frame tb = tb.tb_next def contains(iterator, value): for k in iterator: if value.startswith(k): return True return False # We iterate through each frame looking for an app in INSTALLED_APPS # When one is found, we mark it as last "best guess" (best_guess) and then # check it against SENTRY_EXCLUDE_PATHS. If it isnt listed, then we # use this option. If nothing is found, we use the "best guess". best_guess = None view = None for frame in iter_tb_frames(exc_traceback): try: view = '.'.join([frame.f_globals['__name__'], frame.f_code.co_name]) except: continue if contains(modules, view): if not (contains(app.config['EXCLUDE_PATHS'], view) and best_guess): best_guess = view elif best_guess: break if best_guess: view = best_guess if view: result['tags'].append(('view', view)) if hasattr(exc_type, '__class__'): exc_module = exc_type.__class__.__module__ if exc_module == '__builtin__': exc_type = exc_type.__name__ else: exc_type = '%s.%s' % (exc_module, exc_type.__name__) else: exc_module = None exc_type = exc_type.__name__ if isinstance(exc_value, TemplateSyntaxError) and hasattr(exc_value, 'source'): origin, (start, end) = exc_value.source result['exc_template'] = (origin.reload(), start, end, origin.name) result['tags'].append(('template', origin.loadname)) result['exc_value'] = transform(exc_value) result['exc_type'] = exc_type result['exc_frames'] = self.get_traceback_frames(exc_traceback) return result
def process(self, tags=[], date=None, time_spent=None, request=None, **data): "Processes the message before passing it on to the server" if request: data.update(dict( s_meta=request.META, s_post=request.POST, s_get=request.GET, s_cookies=request.COOKIES, )) tags.append(('url', request.build_absolute_uri())) tags.append(('server', app.config['NAME'])) versions = get_versions() data['s_versions'] = versions if data.get('s_view'): # get list of modules from right to left parts = data['s_view'].split('.') module_list = ['.'.join(parts[:idx]) for idx in xrange(1, len(parts)+1)][::-1] version = None module = None for m in module_list: if m in versions: module = m version = versions[m] # data['s_view'] = view # store our "best guess" for application version if version: data.update({ 's_version': version, 's_module': module, }) # TODO: Cache should be handled by the db backend by default (as we expect a fast access backend) # if app.config['THRASHING_TIMEOUT'] and app.config['THRASHING_LIMIT']: # cache_key = 'sentry:%s:%s' % (kwargs.get('class_name') or '', checksum) # added = cache.add(cache_key, 1, app.config['THRASHING_TIMEOUT']) # if not added: # try: # thrash_count = cache.incr(cache_key) # except (KeyError, ValueError): # # cache.incr can fail. Assume we aren't thrashing yet, and # # if we are, hope that the next error has a successful # # cache.incr call. # thrash_count = 0 # if thrash_count > app.config['THRASHING_LIMIT']: # return # for filter_ in get_filters(): # kwargs = filter_(None).process(kwargs) or kwargs # create ID client-side so that it can be passed to application event_id = uuid.uuid4().hex # Make sure all data is coerced data = transform(data) app.client.send(type=self.get_id(), tags=tags, data=data, date=date, time_spent=time_spent, event_id=event_id) return event_id
def capture(self, event_type, tags=None, data=None, date=None, time_spent=None, event_id=None, extra=None, culprit=None, **kwargs): """ Captures and processes an event and pipes it off to SentryClient.send. To use structured data (interfaces) with capture: >>> capture('Message', message='foo', data={ >>> 'sentry.interfaces.Http': { >>> 'url': '...', >>> 'data': {}, >>> 'querystring': '...', >>> 'method': 'POST', >>> }, >>> }) The finalized ``data`` structure contains the following (some optional) builtin values: >>> { >>> 'culprit': 'full.module.name', # or /arbitrary/path >>> # the culprit version information >>> 'version': ('full.module.name', 'version string'), >>> # all detectable installed modules >>> 'modules': { >>> 'full.module.name': 'version string', >>> }, >>> # arbitrary data provided by user >>> 'extra': { >>> 'key': 'value', >>> } >>> } :param event_type: the module path to the Event class. Builtins can use shorthand class notation and exclude the full module path. :param tags: a list of tuples (key, value) specifying additional tags for event :param data: the data base, useful for specifying structured data interfaces. Any key which contains a '.' will be assumed to be a data interface. :param date: the datetime of this event :param time_spent: a float value representing the duration of the event :param event_id: a 32-length unique string identifying this event :param extra: a dictionary of additional standard metadata :param culprit: a string representing the cause of this event (generally a path to a function) :return: a 32-length string identifying this event """ if data is None: data = {} if tags is None: tags = [] if extra is None: extra = {} if date is None: date = datetime.datetime.now() if '.' not in event_type: # Assume it's a builtin event_type = 'sentry.events.%s' % event_type handler = self.module_cache[event_type]() result = handler.capture(**kwargs) tags = list(tags) + result.pop('tags', []) if not culprit: culprit = result.pop('culprit') for k, v in result.iteritems(): if k not in data: data[k] = v else: data[k].update(v) for k, v in data.iteritems(): if '.' not in k: continue interface = self.module_cache[k] data[k] = interface(**v).serialize() tags.append(('server', app.config['NAME'])) versions = get_versions() data['modules'] = versions if culprit: data['culprit'] = culprit # get list of modules from right to left parts = culprit.split('.') module_list = [ '.'.join(parts[:idx]) for idx in xrange(1, len(parts) + 1) ][::-1] version = None module = None for m in module_list: if m in versions: module = m version = versions[m] # store our "best guess" for application version if version: data['version'] = (module, version), # TODO: Cache should be handled by the db backend by default (as we expect a fast access backend) # if app.config['THRASHING_TIMEOUT'] and app.config['THRASHING_LIMIT']: # cache_key = 'sentry:%s:%s' % (kwargs.get('class_name') or '', checksum) # added = cache.add(cache_key, 1, app.config['THRASHING_TIMEOUT']) # if not added: # try: # thrash_count = cache.incr(cache_key) # except (KeyError, ValueError): # # cache.incr can fail. Assume we aren't thrashing yet, and # # if we are, hope that the next error has a successful # # cache.incr call. # thrash_count = 0 # if thrash_count > app.config['THRASHING_LIMIT']: # return # for filter_ in filters.all(): # kwargs = filter_(None).process(kwargs) or kwargs # create ID client-side so that it can be passed to application event_id = uuid.uuid4().hex # Run the data through processors for processor in processors.all(): data.update(self.module_cache[processor].process(data)) # Make sure all data is coerced data = transform(data) self.send(event_type=event_type, tags=tags, data=data, date=date, time_spent=time_spent, event_id=event_id) return event_id
def _postWithKey(self, data): resp = self.client.post(reverse('sentry-store'), { 'data': base64.b64encode(pickle.dumps(transform(data))), 'key': settings.KEY, }) return resp
def process(self, **kwargs): "Processes the message before passing it on to the server" from sentry.utils import get_filters if kwargs.get("data"): # Ensure we're not changing the original data which was passed # to Sentry kwargs["data"] = kwargs["data"].copy() else: kwargs["data"] = {} if "__sentry__" not in kwargs["data"]: kwargs["data"]["__sentry__"] = {} request = kwargs.pop("request", None) if isinstance(request, HttpRequest): if not request.POST and request.body: post_data = request.body else: post_data = request.POST kwargs["data"].update(dict(META=request.META, POST=post_data, GET=request.GET, COOKIES=request.COOKIES)) if hasattr(request, "user"): if request.user.is_authenticated(): user_info = { "is_authenticated": True, "id": request.user.pk, "username": request.user.username, "email": request.user.email, } else: user_info = {"is_authenticated": False} kwargs["data"]["__sentry__"]["user"] = user_info if not kwargs.get("url"): kwargs["url"] = request.build_absolute_uri() kwargs.setdefault("level", logging.ERROR) kwargs.setdefault("server_name", settings.NAME) versions = get_versions() kwargs["data"]["__sentry__"]["versions"] = versions # Shorten lists/strings for k, v in kwargs["data"].iteritems(): if k == "__sentry__": continue kwargs["data"][k] = shorten(v) # if we've passed frames, lets try to fetch the culprit if not kwargs.get("view") and kwargs["data"]["__sentry__"].get("frames"): # This should be cached modules = get_installed_apps() if settings.INCLUDE_PATHS: modules = set(list(modules) + settings.INCLUDE_PATHS) def contains(iterator, value): for k in iterator: if value.startswith(k): return True return False # We iterate through each frame looking for an app in INSTALLED_APPS # When one is found, we mark it as last "best guess" (best_guess) and then # check it against SENTRY_EXCLUDE_PATHS. If it isnt listed, then we # use this option. If nothing is found, we use the "best guess". best_guess = None view = None for frame in kwargs["data"]["__sentry__"]["frames"]: try: view = ".".join([frame["module"], frame["function"]]) except: continue if contains(modules, view): if not (contains(settings.EXCLUDE_PATHS, view) and best_guess): best_guess = view elif best_guess: break if best_guess: view = best_guess if view: kwargs["view"] = view # try to fetch the current version if kwargs.get("view"): # get list of modules from right to left parts = kwargs["view"].split(".") module_list = [".".join(parts[:idx]) for idx in xrange(1, len(parts) + 1)][::-1] version = None module = None for m in module_list: if m in versions: module = m version = versions[m] # store our "best guess" for application version if version: kwargs["data"]["__sentry__"].update({"version": version, "module": module}) if "checksum" not in kwargs: checksum = construct_checksum(**kwargs) else: checksum = kwargs["checksum"] (is_thrashing, message_id) = self.check_throttle(checksum) if is_thrashing: if request and message_id: # attach the sentry object to the request request.sentry = {"id": message_id, "thrashed": True} return message_id for filter_ in get_filters(): kwargs = filter_(None).process(kwargs) or kwargs # create ID client-side so that it can be passed to application message_id = uuid.uuid4().hex kwargs["message_id"] = message_id # Make sure all data is coerced kwargs["data"] = transform(kwargs["data"]) if "timestamp" not in kwargs: kwargs["timestamp"] = now_with_tz_if_supported() self.send(**kwargs) if request: # attach the sentry object to the request request.sentry = {"id": message_id, "thrashed": False} # store the last message_id incase we hit thrashing limits self.set_last_message_id(checksum, message_id) return message_id
def test_transform_model_instance(self): instance = DuplicateKeyModel(foo='foo') result = transform(instance) self.assertEquals(result, '<DuplicateKeyModel: foo>')
def capture(self, event_type, tags=[], data={}, date=None, time_spent=None, event_id=None, **kwargs): "Captures and processes an event and pipes it off to SentryClient.send." if not date: date = datetime.datetime.now() if '.' not in event_type: # Assume it's a builtin event_type = 'sentry.events.%s' % event_type module, class_name = event_type.rsplit('.', 1) handler = getattr(__import__(module, {}, {}, [class_name], -1), class_name)() result = handler.capture(**kwargs) tags = list(tags) + result['tags'] data['__event__'] = result['data'] # if request: # data.update(dict( # s_meta=request.META, # s_post=request.POST, # s_get=request.GET, # s_cookies=request.COOKIES, # )) # tags.append(('url', request.build_absolute_uri())) tags.append(('server', app.config['NAME'])) versions = get_versions() if '__sentry__' not in data: data['__sentry__'] = {} data['__sentry__']['versions'] = versions # TODO: view should probably be passable via kwargs if data['__sentry__'].get('culprit'): # get list of modules from right to left parts = data['__sentry__']['culprit'].split('.') module_list = ['.'.join(parts[:idx]) for idx in xrange(1, len(parts)+1)][::-1] version = None module = None for m in module_list: if m in versions: module = m version = versions[m] # store our "best guess" for application version if version: data['__sentry__'].update({ 'version': version, 'module': module, }) # TODO: Cache should be handled by the db backend by default (as we expect a fast access backend) # if app.config['THRASHING_TIMEOUT'] and app.config['THRASHING_LIMIT']: # cache_key = 'sentry:%s:%s' % (kwargs.get('class_name') or '', checksum) # added = cache.add(cache_key, 1, app.config['THRASHING_TIMEOUT']) # if not added: # try: # thrash_count = cache.incr(cache_key) # except (KeyError, ValueError): # # cache.incr can fail. Assume we aren't thrashing yet, and # # if we are, hope that the next error has a successful # # cache.incr call. # thrash_count = 0 # if thrash_count > app.config['THRASHING_LIMIT']: # return # for filter_ in get_filters(): # kwargs = filter_(None).process(kwargs) or kwargs # create ID client-side so that it can be passed to application event_id = uuid.uuid4().hex # Make sure all data is coerced data = transform(data) self.send(event_type=event_type, tags=tags, data=data, date=date, time_spent=time_spent, event_id=event_id) return event_id
def create_from_exception(self, exc_info=None, **kwargs): """ Creates an error log from an exception. """ if not exc_info: exc_info = sys.exc_info() exc_type, exc_value, exc_traceback = exc_info reporter = ExceptionReporter(None, exc_type, exc_value, exc_traceback) frames = varmap(shorten, reporter.get_traceback_frames()) if not kwargs.get('view'): # This should be cached modules = get_installed_apps() if settings.INCLUDE_PATHS: modules = set(list(modules) + settings.INCLUDE_PATHS) def iter_tb_frames(tb): while tb: yield tb.tb_frame tb = tb.tb_next def contains(iterator, value): for k in iterator: if value.startswith(k): return True return False # We iterate through each frame looking for an app in INSTALLED_APPS # When one is found, we mark it as last "best guess" (best_guess) and then # check it against SENTRY_EXCLUDE_PATHS. If it isnt listed, then we # use this option. If nothing is found, we use the "best guess". best_guess = None view = None for frame in iter_tb_frames(exc_traceback): try: view = '.'.join([frame.f_globals['__name__'], frame.f_code.co_name]) except: continue if contains(modules, view): if not (contains(settings.EXCLUDE_PATHS, view) and best_guess): best_guess = view elif best_guess: break if best_guess: view = best_guess if view: kwargs['view'] = view data = kwargs.pop('data', {}) or {} if hasattr(exc_type, '__class__'): exc_module = exc_type.__class__.__module__ else: exc_module = None data['__sentry__'] = { 'exc': map(transform, [exc_module, exc_value.args, frames]), } if (isinstance(exc_value, TemplateSyntaxError) and \ isinstance(getattr(exc_value, 'source', None), (tuple, list)) and isinstance(exc_value.source[0], LoaderOrigin)): origin, (start, end) = exc_value.source data['__sentry__'].update({ 'template': (origin.reload(), start, end, origin.name), }) kwargs['view'] = origin.loadname tb_message = '\n'.join(traceback.format_exception(exc_type, exc_value, exc_traceback)) kwargs.setdefault('message', transform(force_unicode(exc_value))) return self.process( class_name=exc_type.__name__, traceback=tb_message, data=data, **kwargs )
def capture(self, event_type, tags=None, data=None, date=None, time_spent=None, event_id=None, extra=None, culprit=None, **kwargs): """ Captures and processes an event and pipes it off to SentryClient.send. To use structured data (interfaces) with capture: >>> capture('Message', message='foo', data={ >>> 'sentry.interfaces.Http': { >>> 'url': '...', >>> 'data': {}, >>> 'querystring': '...', >>> 'method': 'POST', >>> }, >>> }) The finalized ``data`` structure contains the following (some optional) builtin values: >>> { >>> 'culprit': 'full.module.name', # or /arbitrary/path >>> # the culprit version information >>> 'version': ('full.module.name', 'version string'), >>> # all detectable installed modules >>> 'modules': { >>> 'full.module.name': 'version string', >>> }, >>> # arbitrary data provided by user >>> 'extra': { >>> 'key': 'value', >>> } >>> } :param event_type: the module path to the Event class. Builtins can use shorthand class notation and exclude the full module path. :param tags: a list of tuples (key, value) specifying additional tags for event :param data: the data base, useful for specifying structured data interfaces. Any key which contains a '.' will be assumed to be a data interface. :param date: the datetime of this event :param time_spent: a float value representing the duration of the event :param event_id: a 32-length unique string identifying this event :param extra: a dictionary of additional standard metadata :param culprit: a string representing the cause of this event (generally a path to a function) :return: a 32-length string identifying this event """ if data is None: data = {} if tags is None: tags = [] if extra is None: extra = {} if date is None: date = datetime.datetime.now() if '.' not in event_type: # Assume it's a builtin event_type = 'sentry.events.%s' % event_type handler = self.module_cache[event_type]() result = handler.capture(**kwargs) tags = list(tags) + result.pop('tags', []) if not culprit: culprit = result.pop('culprit') for k, v in result.iteritems(): if k not in data: data[k] = v else: data[k].update(v) for k, v in data.iteritems(): if '.' not in k: continue interface = self.module_cache[k] data[k] = interface(**v).serialize() tags.append(('server', app.config['NAME'])) versions = get_versions() data['modules'] = versions if culprit: data['culprit'] = culprit # get list of modules from right to left parts = culprit.split('.') module_list = ['.'.join(parts[:idx]) for idx in xrange(1, len(parts)+1)][::-1] version = None module = None for m in module_list: if m in versions: module = m version = versions[m] # store our "best guess" for application version if version: data['version'] = (module, version), # TODO: Cache should be handled by the db backend by default (as we expect a fast access backend) # if app.config['THRASHING_TIMEOUT'] and app.config['THRASHING_LIMIT']: # cache_key = 'sentry:%s:%s' % (kwargs.get('class_name') or '', checksum) # added = cache.add(cache_key, 1, app.config['THRASHING_TIMEOUT']) # if not added: # try: # thrash_count = cache.incr(cache_key) # except (KeyError, ValueError): # # cache.incr can fail. Assume we aren't thrashing yet, and # # if we are, hope that the next error has a successful # # cache.incr call. # thrash_count = 0 # if thrash_count > app.config['THRASHING_LIMIT']: # return # for filter_ in filters.all(): # kwargs = filter_(None).process(kwargs) or kwargs # create ID client-side so that it can be passed to application event_id = uuid.uuid4().hex # Run the data through processors for processor in processors.all(): data.update(self.module_cache[processor].process(data)) # Make sure all data is coerced data = transform(data) self.send(event_type=event_type, tags=tags, data=data, date=date, time_spent=time_spent, event_id=event_id) return event_id
def capture(self, event_type, tags=[], data={}, date=None, time_spent=None, event_id=None, extra={}, culprit=None, http={}, **kwargs): "Captures and processes an event and pipes it off to SentryClient.send." # TODO: http should be some kind of pluggable interface so others can be added if not date: date = datetime.datetime.now() if '.' not in event_type: # Assume it's a builtin event_type = 'sentry.events.%s' % event_type handler = self.module_cache[event_type]() result = handler.capture(**kwargs) tags = list(tags) + result['tags'] data['extra'] = extra data['event'] = result['data'] if not culprit: culprit = result.get('culprit') for k, v in kwargs.iteritems(): if k.startswith('interface:'): interface_name = k.split('interface:', 1)[1] if '.' not in interface_name: # Assume it's a builtin interface_name = 'sentry.interfaces.%s' % interface_name interface = self.module_cache[interface_name] data[k] = interface(**v).serialize() tags.append(('server', app.config['NAME'])) versions = get_versions() data['modules'] = versions if culprit: data['culprit'] = culprit # get list of modules from right to left parts = culprit.split('.') module_list = ['.'.join(parts[:idx]) for idx in xrange(1, len(parts)+1)][::-1] version = None module = None for m in module_list: if m in versions: module = m version = versions[m] # store our "best guess" for application version if version: data['version'] = (module, version), # TODO: Cache should be handled by the db backend by default (as we expect a fast access backend) # if app.config['THRASHING_TIMEOUT'] and app.config['THRASHING_LIMIT']: # cache_key = 'sentry:%s:%s' % (kwargs.get('class_name') or '', checksum) # added = cache.add(cache_key, 1, app.config['THRASHING_TIMEOUT']) # if not added: # try: # thrash_count = cache.incr(cache_key) # except (KeyError, ValueError): # # cache.incr can fail. Assume we aren't thrashing yet, and # # if we are, hope that the next error has a successful # # cache.incr call. # thrash_count = 0 # if thrash_count > app.config['THRASHING_LIMIT']: # return # for filter_ in get_filters(): # kwargs = filter_(None).process(kwargs) or kwargs # create ID client-side so that it can be passed to application event_id = uuid.uuid4().hex # Make sure all data is coerced data = transform(data) self.send(event_type=event_type, tags=tags, data=data, date=date, time_spent=time_spent, event_id=event_id) return event_id
def process(self, **kwargs): "Processes the message before passing it on to the server" from sentry.utils import get_filters if kwargs.get('data'): # Ensure we're not changing the original data which was passed # to Sentry kwargs['data'] = kwargs['data'].copy() else: kwargs['data'] = {} if '__sentry__' not in kwargs['data']: kwargs['data']['__sentry__'] = {} request = kwargs.pop('request', None) if isinstance(request, HttpRequest): try: post_data = not request.POST and request.raw_post_data or request.POST except: post_data = request.POST kwargs['data'].update(dict( META=request.META, POST=post_data, GET=request.GET, COOKIES=request.COOKIES, )) if hasattr(request, 'user'): if request.user.is_authenticated(): user_info = { 'is_authenticated': True, 'id': request.user.pk, 'username': request.user.username, 'email': request.user.email, } else: user_info = { 'is_authenticated': False, } kwargs['data']['__sentry__']['user'] = user_info if not kwargs.get('url'): kwargs['url'] = request.build_absolute_uri() kwargs.setdefault('level', logging.ERROR) kwargs.setdefault('server_name', settings.NAME) versions = get_versions() kwargs['data']['__sentry__']['versions'] = versions # Shorten lists/strings for k, v in kwargs['data'].iteritems(): if k == '__sentry__': continue kwargs['data'][k] = shorten(v) # if we've passed frames, lets try to fetch the culprit if not kwargs.get('view') and kwargs['data']['__sentry__'].get('frames'): # This should be cached modules = get_installed_apps() if settings.INCLUDE_PATHS: modules = set(list(modules) + settings.INCLUDE_PATHS) def contains(iterator, value): for k in iterator: if value.startswith(k): return True return False # We iterate through each frame looking for an app in INSTALLED_APPS # When one is found, we mark it as last "best guess" (best_guess) and then # check it against SENTRY_EXCLUDE_PATHS. If it isnt listed, then we # use this option. If nothing is found, we use the "best guess". best_guess = None view = None for frame in kwargs['data']['__sentry__']['frames']: try: view = '.'.join([frame['module'], frame['function']]) except: continue if contains(modules, view): if not (contains(settings.EXCLUDE_PATHS, view) and best_guess): best_guess = view elif best_guess: break if best_guess: view = best_guess if view: kwargs['view'] = view # try to fetch the current version if kwargs.get('view'): # get list of modules from right to left parts = kwargs['view'].split('.') module_list = ['.'.join(parts[:idx]) for idx in xrange(1, len(parts) + 1)][::-1] version = None module = None for m in module_list: if m in versions: module = m version = versions[m] # store our "best guess" for application version if version: kwargs['data']['__sentry__'].update({ 'version': version, 'module': module, }) if 'checksum' not in kwargs: checksum = construct_checksum(**kwargs) else: checksum = kwargs['checksum'] (is_thrashing, message_id) = self.check_throttle(checksum) if is_thrashing: if request and message_id: # attach the sentry object to the request request.sentry = { 'id': '%s$%s' % (message_id, checksum), 'thrashed': True, } return message_id for filter_ in get_filters(): kwargs = filter_(None).process(kwargs) or kwargs # create ID client-side so that it can be passed to application message_id = uuid.uuid4().hex kwargs['message_id'] = message_id # Make sure all data is coerced kwargs['data'] = transform(kwargs['data']) if 'timestamp' not in kwargs: kwargs['timestamp'] = datetime.datetime.now() self.send(**kwargs) if request: # attach the sentry object to the request request.sentry = { 'id': '%s$%s' % (message_id, checksum), 'thrashed': False, } # store the last message_id incase we hit thrashing limits self.set_last_message_id(checksum, message_id) return message_id
def process(self, **kwargs): "Processes the message before passing it on to the server" from sentry.utils import get_filters if kwargs.get('data'): # Ensure we're not changing the original data which was passed # to Sentry kwargs['data'] = kwargs['data'].copy() else: kwargs['data'] = {} if '__sentry__' not in kwargs['data']: kwargs['data']['__sentry__'] = {} request = kwargs.pop('request', None) if isinstance(request, HttpRequest): try: post_data = not request.POST and request.raw_post_data or request.POST except: post_data = request.POST kwargs['data'].update( dict( META=request.META, POST=post_data, GET=request.GET, COOKIES=request.COOKIES, )) if hasattr(request, 'user'): if request.user.is_authenticated(): user_info = { 'is_authenticated': True, 'id': request.user.pk, 'username': request.user.username, 'email': request.user.email, } else: user_info = { 'is_authenticated': False, } kwargs['data']['__sentry__']['user'] = user_info if not kwargs.get('url'): kwargs['url'] = request.build_absolute_uri() kwargs.setdefault('level', logging.ERROR) kwargs.setdefault('server_name', settings.NAME) versions = get_versions() kwargs['data']['__sentry__']['versions'] = versions # Shorten lists/strings for k, v in kwargs['data'].iteritems(): if k == '__sentry__': continue kwargs['data'][k] = shorten(v) # if we've passed frames, lets try to fetch the culprit if not kwargs.get('view') and kwargs['data']['__sentry__'].get( 'frames'): # This should be cached modules = get_installed_apps() if settings.INCLUDE_PATHS: modules = set(list(modules) + settings.INCLUDE_PATHS) def contains(iterator, value): for k in iterator: if value.startswith(k): return True return False # We iterate through each frame looking for an app in INSTALLED_APPS # When one is found, we mark it as last "best guess" (best_guess) and then # check it against SENTRY_EXCLUDE_PATHS. If it isnt listed, then we # use this option. If nothing is found, we use the "best guess". best_guess = None view = None for frame in kwargs['data']['__sentry__']['frames']: try: view = '.'.join([frame['module'], frame['function']]) except: continue if contains(modules, view): if not (contains(settings.EXCLUDE_PATHS, view) and best_guess): best_guess = view elif best_guess: break if best_guess: view = best_guess if view: kwargs['view'] = view # try to fetch the current version if kwargs.get('view'): # get list of modules from right to left parts = kwargs['view'].split('.') module_list = [ '.'.join(parts[:idx]) for idx in xrange(1, len(parts) + 1) ][::-1] version = None module = None for m in module_list: if m in versions: module = m version = versions[m] # store our "best guess" for application version if version: kwargs['data']['__sentry__'].update({ 'version': version, 'module': module, }) if 'checksum' not in kwargs: checksum = construct_checksum(**kwargs) else: checksum = kwargs['checksum'] (is_thrashing, message_id) = self.check_throttle(checksum) if is_thrashing: if request and message_id: # attach the sentry object to the request request.sentry = { 'id': '%s$%s' % (message_id, checksum), 'thrashed': True, } return message_id for filter_ in get_filters(): kwargs = filter_(None).process(kwargs) or kwargs # create ID client-side so that it can be passed to application message_id = uuid.uuid4().hex kwargs['message_id'] = message_id # Make sure all data is coerced kwargs['data'] = transform(kwargs['data']) if 'timestamp' not in kwargs: kwargs['timestamp'] = datetime.datetime.now() self.send(**kwargs) if request: # attach the sentry object to the request request.sentry = { 'id': '%s$%s' % (message_id, checksum), 'thrashed': False, } # store the last message_id incase we hit thrashing limits self.set_last_message_id(checksum, message_id) return message_id
def get_prep_value(self, value): if value is None: return return base64.b64encode(pickle.dumps(transform(value)).encode("zlib"))
def test_bad_string(self): x = 'The following character causes problems: \xd4' result = transform(x) self.assertEquals(result, '(Error decoding value)')
def process(self, **kwargs): "Processes the message before passing it on to the server" from sentry.utils import get_filters if kwargs.get('data'): # Ensure we're not changing the original data which was passed # to Sentry kwargs['data'] = kwargs['data'].copy() request = kwargs.pop('request', None) if request: if not kwargs.get('data'): kwargs['data'] = {} if not request.POST and request.raw_post_data: post_data = request.raw_post_data else: post_data = request.POST kwargs['data'].update(dict( META=request.META, POST=post_data, GET=request.GET, COOKIES=request.COOKIES, )) if not kwargs.get('url'): kwargs['url'] = request.build_absolute_uri() kwargs.setdefault('level', logging.ERROR) kwargs.setdefault('server_name', settings.NAME) # save versions of all installed apps if 'data' not in kwargs or '__sentry__' not in (kwargs['data'] or {}): if kwargs.get('data') is None: kwargs['data'] = {} kwargs['data']['__sentry__'] = {} versions = get_versions() kwargs['data']['__sentry__']['versions'] = versions # Shorten lists/strings for k, v in kwargs['data'].iteritems(): if k == '__sentry__': continue kwargs['data'][k] = shorten(v) if kwargs.get('view'): # get list of modules from right to left parts = kwargs['view'].split('.') module_list = ['.'.join(parts[:idx]) for idx in xrange(1, len(parts)+1)][::-1] version = None module = None for m in module_list: if m in versions: module = m version = versions[m] # store our "best guess" for application version if version: kwargs['data']['__sentry__'].update({ 'version': version, 'module': module, }) if 'checksum' not in kwargs: checksum = construct_checksum(**kwargs) else: checksum = kwargs['checksum'] (is_thrashing, message_id) = self.check_throttle(checksum) if is_thrashing: if request and message_id: # attach the sentry object to the request request.sentry = { 'id': message_id, 'thrashed': True, } return message_id for filter_ in get_filters(): kwargs = filter_(None).process(kwargs) or kwargs # create ID client-side so that it can be passed to application message_id = uuid.uuid4().hex kwargs['message_id'] = message_id # Make sure all data is coerced kwargs['data'] = transform(kwargs['data']) if 'timestamp' not in kwargs: kwargs['timestamp'] = datetime.datetime.now() self.send(**kwargs) if request: # attach the sentry object to the request request.sentry = { 'id': message_id, 'trashed': False, } # store the last message_id incase we hit thrashing limits self.set_last_message_id(checksum, message_id) return message_id
def test_model_instance(self): instance = DuplicateKeyModel(foo='foo') result = transform(instance) self.assertEquals(result, '<DuplicateKeyModel: foo>')