def testAlternateDatabase(self): settings.DATABASE_USING = 'default' try: Message.objects.get(id=999999979) except Message.DoesNotExist, exc: SentryClient.create_from_exception(exc)
def sentry_exception_handler(sender, request=None, **kwargs): try: exc_type, exc_value, exc_traceback = sys.exc_info() if not settings.CATCH_404_ERRORS \ and issubclass(exc_type, Http404): return if dj_settings.DEBUG or getattr(exc_type, 'skip_sentry', False): return if transaction.is_dirty(): transaction.rollback() # kudos to Tapz for this idea modules = get_installed_apps() # only retrive last 10 lines tb = traceback.extract_tb(exc_traceback) # retrive final file and line number where the exception occured file, line_number = tb[-1][:2] # tiny hack to get the python path from filename for (filename, line, function, text) in reversed(tb): for path in sys.path: if filename.startswith(path): view = '%s.%s' % (filename[len(path)+1:].replace('/', '.').replace('.py', ''), function) break if view.split('.')[0] in modules: break else: view = '%s.%s' % (exc_traceback.tb_frame.f_globals['__name__'], tb[-1][2]) if request: data = dict( META=request.META, POST=request.POST, GET=request.GET, COOKIES=request.COOKIES, ) else: data = dict() extra = dict( url=request and request.build_absolute_uri() or None, data=data, view=view, ) if settings.USE_LOGGING: logger.critical(exc_value, exc_info=sys.exc_info(), extra=extra) else: SentryClient.create_from_exception(**extra) except Exception, exc: try: logger.exception(u'Unable to process log entry: %s' % (exc,)) except Exception, exc: warnings.warn(u'Unable to process log entry: %s' % (exc,))
def testThrashing(self): settings.THRASHING_LIMIT = 10 settings.THRASHING_TIMEOUT = 60 Message.objects.all().delete() GroupedMessage.objects.all().delete() for i in range(0, 50): SentryClient.create_from_text('hi') self.assertEquals(Message.objects.count(), settings.THRASHING_LIMIT)
def testNoThrashing(self): prev = settings.THRASHING_LIMIT settings.THRASHING_LIMIT = 0 Message.objects.all().delete() GroupedMessage.objects.all().delete() for i in range(0, 50): SentryClient.create_from_text('hi') self.assertEquals(Message.objects.count(), 50) settings.THRASHING_LIMIT = prev
def testProcess(self): self.start_test_server() message_id = SentryClient().process(message='hello') self.assertTrue(message_id) instance = Message.objects.all().order_by('-id')[0] self.assertEquals(instance.message, 'hello') self.stop_test_server()
def fetch_new_content(self, **options): update_horizon = datetime.utcnow() - timedelta(minutes=15) last_viewed_horizon = datetime.utcnow() - timedelta(days=5) users = User.objects.all() for user in users: try: person = user.person except Person.DoesNotExist: continue # Don't update accounts if someone hasn't viewed the site in some days. if person.last_viewed_home < last_viewed_horizon: logging.getLogger(__name__).debug( "User %s hasn't viewed the site in a while; skipping", user.username) continue for account in person.accounts.all(): log = logging.getLogger('%s.%s' % (__name__, account.service)) try: poller = pollers[account.service] except KeyError: log.debug("Account service %s has no poller, skipping", account.service) continue if options['service'] and account.service != options['service']: log.debug( "Account is for service %s but we're only polling %s, skipping", account.service, options['service']) continue if not options[ 'force'] and account.last_updated > update_horizon: log.debug( "Account %s %s was updated fewer than 15 minutes ago, skipping", account.service, account.display_name) continue # Mark the account as updated even if the update fails later. log.debug("Polling account %s %s", account.service, account.display_name) account.last_updated = datetime.utcnow() account.save() try: poller(account) except Exception, exc: log.exception(exc) SentryClient().create_from_exception( view='%s.%s' % (__name__, account.service)) else: account.last_success = datetime.utcnow() account.save()
def testTimestamp(self): timestamp = datetime.datetime.now() - datetime.timedelta(hours=1) self.start_test_server() message_id = SentryClient().process(message='hello', timestamp=timestamp) self.stop_test_server() self.assertTrue(message_id) instance = Message.objects.all().order_by('-id')[0] self.assertEquals(instance.message, 'hello') self.assertEquals(instance.datetime, timestamp) group = instance.group self.assertEquals(group.first_seen, timestamp) self.assertEquals(group.last_seen, timestamp)
def testCorrectUnicode(self): self.setUpHandler() cnt = Message.objects.count() value = 'רונית מגן'.decode('utf-8') error = SentryClient.create_from_text(value) self.assertEquals(Message.objects.count(), cnt+1) self.assertEquals(error.message, value) logging.info(value) self.assertEquals(Message.objects.count(), cnt+2) x = TestModel.objects.create(data={'value': value}) logging.warn(x) self.assertEquals(Message.objects.count(), cnt+3) try: raise SyntaxMessage(value) except Exception, exc: logging.exception(exc) logging.info('test', exc_info=sys.exc_info())
def testLongURLs(self): # Fix: #6 solves URLs > 200 characters error = SentryClient.create_from_text('hello world', url='a'*210) self.assertEquals(error.url, 'a'*200) self.assertEquals(error.data['url'], 'a'*210)
def testAPI(self): try: Message.objects.get(id=999999989) except Message.DoesNotExist, exc: SentryClient.create_from_exception(exc)
self.assertEquals(last.class_name, 'DoesNotExist') self.assertEquals(last.level, logging.ERROR) self.assertEquals(last.message, smart_unicode(exc)) def testAPI(self): try: Message.objects.get(id=999999989) except Message.DoesNotExist, exc: SentryClient.create_from_exception(exc) else: self.fail('Unable to create `Message` entry.') try: Message.objects.get(id=999999989) except Message.DoesNotExist, exc: error = SentryClient.create_from_exception() self.assertTrue(error.data.get('__sentry__', {}).get('exc')) else: self.fail('Unable to create `Message` entry.') cur = (Message.objects.count(), GroupedMessage.objects.count()) self.assertEquals(cur, (2, 2), 'Assumed logs failed to save. %s' % (cur,)) last = Message.objects.all().order_by('-id')[0:1].get() self.assertEquals(last.logger, 'root') self.assertEquals(last.class_name, 'DoesNotExist') self.assertEquals(last.level, logging.ERROR) self.assertEquals(last.message, smart_unicode(exc)) SentryClient.create_from_text('This is an error', level=logging.DEBUG)
def send(data): return SentryClient().send(**data)
def handle_noargs(self, **options): try: self.fetch_new_content(**options) except Exception, exc: logging.exception(exc) SentryClient().create_from_exception(view=__name__)
log.debug("Creating a UserStream row for object %r which is a %s by %r", obj, why_verb, actor) streamitem, created = UserStream.objects.get_or_create(user=user, obj=obj, defaults={'why_account': actor, 'why_verb': why_verb, 'time': orig_obj.time}) # Now walk up again creating UserReplyStream rows as necessary reply_obj = orig_obj while reply_obj.in_reply_to is not None: UserReplyStream.objects.get_or_create(user=user, root=obj, reply=reply_obj, defaults={'root_time': streamitem.time, 'reply_time': reply_obj.time}) reply_obj = reply_obj.in_reply_to except Exception, exc: from sentry.client.base import SentryClient SentryClient().create_from_exception(view=__name__) def account_for_facebook_user(fb_user, person=None): try: account = Account.objects.get(service='facebook.com', ident=fb_user["id"]) except Account.DoesNotExist: if person is None: avatar = Media( width=50, height=50, image_url='http://graph.facebook.com/%s/picture' % fb_user["id"] ) avatar.save() person = Person( display_name=fb_user["name"],
def emit(self, record): from sentry.client.base import SentryClient SentryClient.create_from_record(record)
def poll_twitter(account): user = account.person.user if user is None: return authtoken = account.authinfo if not authtoken: return # Get that twitter user's home timeline. csr = oauth.Consumer(*settings.TWITTER_CONSUMER) token = oauth.Token(*authtoken.split(':', 1)) client = oauth.Client(csr, token) try: resp, content = client.request( 'http://api.twitter.com/1/statuses/home_timeline.json?include_entities=true&count=50', 'GET') except httplib.IncompleteRead: log.info( "Twitter returned an incomplete response asking for %s's feed", account.ident) return if resp.status in (500, 502, 503): # Can't get Twitter results right now. Let's try again later. log.info( "Twitter returned a server error status %d asking for %s's feed (Twitter's down?)", resp.status, account.ident) return if resp.status == 401: # The token may be invalid. Have we successfully scanned this account recently? if account.last_success > datetime.utcnow() - timedelta(days=2): raise ValueError( "Token for Twitter user %s came back as invalid (possibly temporary)" % account.ident) # The token is now invalid (maybe they revoked the app). Stop updating this account. account.authinfo = '' account.save() raise ValueError( "Token for Twitter user %s came back as invalid (probably permanent, so deleted authinfo)" % account.ident) if resp.status != 200: raise ValueError( "Unexpected %d %s response fetching %s's twitter timeline" % (resp.status, resp.reason, account.ident)) tl = json.loads(content) for orig_tweetdata in reversed(tl): try: # TODO: filter based on source? tweetdata = orig_tweetdata why_verb = 'post' try: tweetdata = orig_tweetdata['retweeted_status'] except KeyError: pass else: why_verb = 'share' really_a_share, tweet = raw_object_for_tweet(tweetdata, client) if tweet is None: continue if really_a_share: why_verb = 'share' if why_verb == 'share': why_account = account_for_twitter_user(orig_tweetdata['user']) else: why_account = tweet.author # CASES: # real reply to... # real retweet of... # tweet with just a link # tweet with a link and custom text # tweet with a link and the link's target page title (found how?) if why_verb == 'post' and tweet.in_reply_to is not None: why_verb = 'reply' root = tweet while root.in_reply_to is not None: log.debug('Walking up from %r to %r', root, root.in_reply_to) root = root.in_reply_to streamitem, created = UserStream.objects.get_or_create( user=user, obj=root, # TODO: is tweet.time the right time here or do we need the "why time" from orig_tweetdata? defaults={ 'why_account': why_account, 'why_verb': why_verb, 'time': tweet.time }) # Now add a reply for each tweet in the thread along the way. supertweet = tweet while supertweet.in_reply_to is not None: UserReplyStream.objects.get_or_create(user=user, root=root, reply=supertweet, defaults={ 'root_time': streamitem.time, 'reply_time': supertweet.time }) supertweet = supertweet.in_reply_to except Exception, exc: from sentry.client.base import SentryClient SentryClient().create_from_exception(view=__name__)