Browse Source

Sentry now fully supports defining its own search site (requires Disqus fork of Haystack).

David Cramer 14 years ago
parent
commit
cf79a737ef
6 changed files with 72 additions and 36 deletions
  1. 28 0
      docs/config.rst
  2. 9 5
      example_project/settings.py
  3. 4 0
      sentry/conf.py
  4. 10 2
      sentry/search_indexes.py
  5. 0 6
      sentry/search_sites.py
  6. 21 23
      sentry/views.py

+ 28 - 0
docs/config.rst

@@ -83,6 +83,34 @@ be seen as the same message within Sentry::
 	logging.error('There was some %s error', 'fun')
 	logging.error('There was some %s error', 'fun')
 	logging.error('There was some %s error', 1)
 	logging.error('There was some %s error', 1)
 
 
+Integration with ``haystack`` (Search)
+--------------------------------------
+
+(This support is still under development)
+
+Note: You will to install a forked version of Haystack which supports additional configuration. It can be obtained on [GitHub](http://github.com/disqus/django-haystack).
+
+Start by configuring your Sentry search backend::
+
+	# By default Sentry looks for HAYSTACK_* options
+	SENTRY_SEARCH_BACKEND = 'solr'
+	SENTRY_SEARCH_OPTIONS = {
+	    'url': 'http://127.0.0.1:8983/solr'
+	}
+
+Or if you want to use Whoosh (you shouldn't)::
+
+	SENTRY_SEARCH_BACKEND = 'whoosh'
+	SENTRY_SEARCH_OPTIONS = {
+	    'path': os.path.join(PROJECT_ROOT, 'sentry_index')
+	}
+
+Now ensure you've added ``haystack`` to the ``INSTALLED_APPS`` on Sentry's server::
+
+	INSTALLED_APPS = INSTALLED_APPS + ('haystack',)
+
+Enjoy!
+
 Other Settings
 Other Settings
 --------------
 --------------
 
 

+ 9 - 5
example_project/settings.py

@@ -105,7 +105,7 @@ INSTALLED_APPS = (
     'sentry.plugins.sentry_servers',
     'sentry.plugins.sentry_servers',
     'sentry.plugins.sentry_sites',
     'sentry.plugins.sentry_sites',
     'sentry.plugins.sentry_urls',
     'sentry.plugins.sentry_urls',
-    # 'haystack',
+    'haystack',
     'paging',
     'paging',
     'south',
     'south',
     'indexer',
     'indexer',
@@ -121,12 +121,16 @@ SENTRY_TESTING = True
 SENTRY_SITE = 'example'
 SENTRY_SITE = 'example'
 SENTRY_PUBLIC = False
 SENTRY_PUBLIC = False
 
 
-HAYSTACK_WHOOSH_PATH = os.path.join(PROJECT_ROOT, 'sentry_index')
+# just to test
 HAYSTACK_SEARCH_ENGINE = 'whoosh'
 HAYSTACK_SEARCH_ENGINE = 'whoosh'
-# HAYSTACK_SOLR_URL = 'http://127.0.0.1:8983/solr'
-# HAYSTACK_SEARCH_ENGINE = 'solr'
 
 
-HAYSTACK_SITECONF = 'sentry.search_sites'
+SENTRY_SEARCH_ENGINE = 'whoosh'
+SENTRY_SEARCH_OPTIONS = {
+    'path': os.path.join(PROJECT_ROOT, 'sentry_index'),
+}
+
+# This shouldn't be needed, but bleh
+HAYSTACK_SITECONF = 'sentry.search_indexes'
 
 
 try:
 try:
     import debug_toolbar
     import debug_toolbar

+ 4 - 0
sentry/conf.py

@@ -25,6 +25,10 @@ FILTERS = getattr(settings, 'SENTRY_FILTERS', filter(None, (
     'sentry.filters.SiteFilter',
     'sentry.filters.SiteFilter',
 )))
 )))
 
 
+# Sentry allows you to specify an alternative search backend for itself
+SEARCH_ENGINE = getattr(settings, 'SENTRY_SEARCH_ENGINE', getattr(settings, 'HAYSTACK_SEARCH_ENGINE', None))
+SEARCH_OPTIONS = getattr(settings, 'SENTRY_SEARCH_OPTIONS', {})
+
 KEY = getattr(settings, 'SENTRY_KEY', md5_constructor(settings.SECRET_KEY).hexdigest())
 KEY = getattr(settings, 'SENTRY_KEY', md5_constructor(settings.SECRET_KEY).hexdigest())
 
 
 LOG_LEVELS = (
 LOG_LEVELS = (

+ 10 - 2
sentry/search_indexes.py

@@ -1,9 +1,17 @@
 import haystack
 import haystack
 from haystack.indexes import *
 from haystack.indexes import *
+from haystack.sites import SearchSite
 
 
-from sentry.search_sites import site
+from sentry import conf
 from sentry.models import GroupedMessage
 from sentry.models import GroupedMessage
 
 
+backend = haystack.load_backend(conf.SEARCH_ENGINE)
+
+class SentrySearchSite(SearchSite): pass
+
+site = SentrySearchSite()
+site.backend = backend.SearchBackend(site, **conf.SEARCH_OPTIONS)
+
 class GroupedMessageIndex(RealTimeSearchIndex):
 class GroupedMessageIndex(RealTimeSearchIndex):
     text = CharField(document=True, stored=False)
     text = CharField(document=True, stored=False)
     status = CharField(stored=False, null=True)
     status = CharField(stored=False, null=True)
@@ -17,4 +25,4 @@ class GroupedMessageIndex(RealTimeSearchIndex):
     def prepare_text(self, instance):
     def prepare_text(self, instance):
         return '\n'.join(filter(None, [instance.message, instance.class_name, instance.traceback, instance.view]))
         return '\n'.join(filter(None, [instance.message, instance.class_name, instance.traceback, instance.view]))
 
 
-site.register(GroupedMessage, GroupedMessageIndex)
+site.register(GroupedMessage, GroupedMessageIndex)

+ 0 - 6
sentry/search_sites.py

@@ -1,6 +0,0 @@
-import haystack
-from haystack.sites import SearchSite
-
-haystack.autodiscover()
-
-site = SearchSite()

+ 21 - 23
sentry/views.py

@@ -6,7 +6,6 @@ except ImportError:
 import datetime
 import datetime
 import zlib
 import zlib
 
 
-from django.conf import settings
 from django.core.context_processors import csrf
 from django.core.context_processors import csrf
 from django.core.urlresolvers import reverse
 from django.core.urlresolvers import reverse
 from django.db.models import Q
 from django.db.models import Q
@@ -25,25 +24,24 @@ from sentry.plugins import GroupActionProvider
 from sentry.templatetags.sentry_helpers import with_priority
 from sentry.templatetags.sentry_helpers import with_priority
 from sentry.reporter import ImprovedExceptionReporter
 from sentry.reporter import ImprovedExceptionReporter
 
 
-# HACK
-if 'sentry.filters.SearchFilter' in conf.FILTERS:
-    try:
-        from haystack.query import SearchQuerySet
-        from sentry.search_sites import site as SentrySearchSite
-    except ImportError:
-        SentrySearchQuerySet = None
-    else:
-        class SentrySearchQuerySet(SearchQuerySet):
-            "Returns actual instances rather than search results."
+def get_search_query_set(query):
+    from haystack.query import SearchQuerySet
+    from sentry.search_indexes import site, backend
 
 
-            def __getitem__(self, k):
-                result = []
-                for r in super(SentrySearchQuerySet, self).__getitem__(k):
-                    r.object.score = r.score
-                    result.append(r.object)
-                return result
-else:
-    SentrySearchQuerySet = None
+    class SentrySearchQuerySet(SearchQuerySet):
+        "Returns actual instances rather than search results."
+
+        def __getitem__(self, k):
+            result = []
+            for r in super(SentrySearchQuerySet, self).__getitem__(k):
+                r.object.score = r.score
+                result.append(r.object)
+            return result
+    
+    return SentrySearchQuerySet(
+        site=site,
+        query=backend.SearchQuery(backend=site.backend),
+    ).filter(content=query)
 
 
 def login_required(func):
 def login_required(func):
     def wrapped(request, *args, **kwargs):
     def wrapped(request, *args, **kwargs):
@@ -97,10 +95,10 @@ def index(request):
         page = 1
         page = 1
 
 
     query = request.GET.get('content')
     query = request.GET.get('content')
-    is_search = query and SentrySearchQuerySet
+    is_search = query
 
 
     if is_search:
     if is_search:
-        message_list = SentrySearchQuerySet().filter(content=query)
+        message_list = get_search_query_set(query)
     else:
     else:
         message_list = GroupedMessage.objects.extra(
         message_list = GroupedMessage.objects.extra(
             select={
             select={
@@ -157,10 +155,10 @@ def ajax_handler(request):
             filters.append(filter_(request))
             filters.append(filter_(request))
 
 
         query = request.GET.get('content')
         query = request.GET.get('content')
-        is_search = query and SentrySearchQuerySet
+        is_search = query
 
 
         if is_search:
         if is_search:
-            message_list = SentrySearchQuerySet(site=SentrySearchSite).filter(content=query)
+            message_list = get_search_query_set(query)
         else:
         else:
             message_list = GroupedMessage.objects.extra(
             message_list = GroupedMessage.objects.extra(
                 select={
                 select={