Skip to content
Browse files

Updating patch to semi-work

  • Loading branch information...
2 parents 8dffa29 + 0c5e7cc commit 28dc0d42e425771c0785c4a9e810e0b09aeb45e5 @dcramer dcramer committed Mar 20, 2013
Showing with 12,748 additions and 1,798 deletions.
  1. +2 −0 .coveragerc
  2. +2 −1 .gitignore
  3. +7 −1 .jshintignore
  4. +4 −7 .travis.yml
  5. +134 −3 CHANGES
  6. +1 −1 MANIFEST.in
  7. +23 −12 Makefile
  8. +2 −0 README.rst
  9. +10 −0 conftest.py
  10. +1 −1 docs/buffer/index.rst
  11. +5 −3 docs/client/index.rst
  12. +6 −59 docs/config/index.rst
  13. +36 −2 docs/contributing/index.rst
  14. +36 −12 docs/developer/client/index.rst
  15. BIN docs/images/dashboard.png
  16. BIN docs/images/event.png
  17. BIN docs/images/group_list.png
  18. +1 −1 docs/index.rst
  19. +5 −1 docs/plugins/index.rst
  20. +1 −5 docs/queue/index.rst
  21. +50 −8 docs/quickstart/index.rst
  22. +20 −3 docs/upgrading/index.rst
  23. +2 −1 example/templates/layout.html
  24. +2 −2 package.json
  25. +46 −22 setup.py
  26. +36 −3 src/sentry/__init__.py
  27. +4 −3 src/sentry/app.py
  28. +1 −1 src/sentry/buffer/__init__.py
  29. +14 −3 src/sentry/buffer/base.py
  30. +37 −18 src/sentry/buffer/redis.py
  31. +1 −1 src/sentry/conf/__init__.py
  32. +11 −20 src/sentry/conf/defaults.py
  33. +84 −24 src/sentry/conf/server.py
  34. +1 −4 src/sentry/conf/settings.py
  35. +0 −4 src/sentry/conf/urls.py
  36. +73 −7 src/sentry/constants.py
  37. +109 −35 src/sentry/coreapi.py
  38. +1 −1 src/sentry/filters/__init__.py
  39. +14 −7 src/sentry/filters/base.py
  40. +1 −1 src/sentry/filters/builtins.py
  41. +1 −1 src/sentry/filters/helpers.py
  42. +3 −3 src/sentry/filters/widgets.py
  43. +352 −145 src/sentry/interfaces.py
  44. BIN src/sentry/locale/en/LC_MESSAGES/django.mo
  45. +1,072 −678 src/sentry/locale/en/LC_MESSAGES/django.po
  46. +1 −1 src/sentry/management/__init__.py
  47. +5 −12 src/sentry/management/commands/cleanup.py
  48. +10 −6 src/sentry/management/commands/repair.py
  49. +24 −4 src/sentry/management/commands/send_fake_data.py
  50. +220 −168 src/sentry/manager.py
  51. +1 −1 src/sentry/middleware.py
  52. +2 −4 src/sentry/migrations/0067_auto__add_field_group_platform__add_field_event_platform.py
  53. +255 −0 src/sentry/migrations/0068_auto__add_field_projectkey_user_added__add_field_projectkey_date_added.py
  54. +260 −0 src/sentry/migrations/0069_auto__add_lostpasswordhash.py
  55. +250 −0 src/sentry/migrations/0070_projectoption_key_length.py
  56. +257 −0 src/sentry/migrations/0071_auto__add_field_group_users_seen.py
  57. +280 −0 src/sentry/migrations/0072_auto__add_affecteduserbygroup__add_unique_affecteduserbygroup_project_.py
  58. +268 −0 src/sentry/migrations/0073_auto__add_field_project_platform.py
  59. +264 −0 src/sentry/migrations/0074_correct_filtervalue_index.py
  60. +262 −0 src/sentry/migrations/0075_add_groupbookmark_index.py
  61. +264 −0 src/sentry/migrations/0076_add_groupmeta_index.py
  62. +291 −0 src/sentry/migrations/0077_auto__add_trackeduser__add_unique_trackeduser_project_ident.py
  63. +279 −0 src/sentry/migrations/0078_auto__add_field_affecteduserbygroup_tuser.py
  64. +292 −0 src/sentry/migrations/0079_auto__del_unique_affecteduserbygroup_project_ident_group__add_unique_a.py
  65. +278 −0 src/sentry/migrations/0080_auto__chg_field_affecteduserbygroup_ident.py
  66. +306 −0 src/sentry/migrations/0081_fill_trackeduser.py
  67. +319 −0 src/sentry/migrations/0082_auto__add_activity__add_field_group_num_comments__add_field_event_num_.py
  68. +397 −0 src/sentry/migrations/0083_migrate_dupe_groups.py
  69. +301 −0 src/sentry/migrations/0084_auto__del_unique_group_project_checksum_logger_culprit__add_unique_gro.py
  70. +299 −0 src/sentry/migrations/0085_auto__del_unique_project_slug__add_unique_project_slug_team.py
  71. +296 −0 src/sentry/migrations/0086_auto__add_field_team_date_added.py
  72. +291 −0 src/sentry/migrations/0087_auto__del_messagefiltervalue__del_unique_messagefiltervalue_project_ke.py
  73. +290 −0 src/sentry/migrations/0088_auto__del_messagecountbyminute__del_unique_messagecountbyminute_projec.py
  74. +344 −0 src/sentry/migrations/0089_auto__add_accessgroup__add_unique_accessgroup_team_name.py
  75. +364 −0 src/sentry/migrations/0090_auto__add_grouptagkey__add_unique_grouptagkey_project_group_key__add_f.py
  76. +336 −0 src/sentry/migrations/0091_auto__add_alert.py
  77. +348 −0 src/sentry/migrations/0092_auto__add_alertrelatedgroup__add_unique_alertrelatedgroup_group_alert.py
  78. +506 −84 src/sentry/models.py
  79. +34 −1 src/sentry/permissions.py
  80. +1 −1 src/sentry/plugins/__init__.py
  81. +16 −4 src/sentry/plugins/base.py
  82. +22 −3 src/sentry/plugins/bases/issue.py
  83. +16 −7 src/sentry/plugins/bases/notify.py
  84. +2 −1 src/sentry/plugins/bases/tag.py
  85. +1 −1 src/sentry/plugins/helpers.py
  86. +1 −1 src/sentry/plugins/sentry_interface_types/__init__.py
  87. +2 −1 src/sentry/plugins/sentry_interface_types/models.py
  88. +1 −1 src/sentry/plugins/sentry_mail/__init__.py
  89. +36 −30 src/sentry/plugins/sentry_mail/models.py
  90. +1 −1 src/sentry/plugins/sentry_servers/__init__.py
  91. +2 −1 src/sentry/plugins/sentry_servers/models.py
  92. +1 −1 src/sentry/plugins/sentry_sites/__init__.py
  93. +1 −1 src/sentry/plugins/sentry_sites/models.py
  94. +1 −1 src/sentry/plugins/sentry_urls/__init__.py
  95. +3 −2 src/sentry/plugins/sentry_urls/models.py
  96. +2 −2 src/sentry/plugins/sentry_user_emails/__init__.py
  97. +2 −2 src/sentry/plugins/sentry_user_emails/models.py
  98. +1 −1 src/sentry/plugins/sentry_useragents/__init__.py
  99. +2 −1 src/sentry/plugins/sentry_useragents/models.py
  100. +1 −1 src/sentry/pool/base.py
  101. +5 −5 src/sentry/pool/redis.py
  102. +1 −1 src/sentry/processors/__init__.py
  103. +1 −1 src/sentry/processors/base.py
  104. +1 −1 src/sentry/processors/console.py
  105. +1 −1 src/sentry/services/__init__.py
  106. +1 −1 src/sentry/services/base.py
  107. +2 −1 src/sentry/services/http.py
  108. +1 −1 src/sentry/services/udp.py
  109. +23 −2 src/sentry/signals.py
  110. BIN src/sentry/static/sentry/fonts/sentry.eot
  111. BIN src/sentry/static/sentry/images/client_security.png
  112. BIN src/sentry/static/sentry/images/dashboard/sidebar-bg-left.png
  113. BIN src/sentry/static/sentry/images/dashboard/sidebar-bg-right.png
  114. BIN src/sentry/static/sentry/images/dashboard/sidebar-bg.png
  115. BIN src/sentry/static/sentry/images/favicon.png
  116. BIN src/sentry/static/sentry/images/github-icons.png
  117. BIN src/sentry/static/sentry/images/platforms/ios48x48.png
  118. +50 −0 src/sentry/static/sentry/less/fonts.less
  119. +86 −0 src/sentry/static/sentry/less/github.less
  120. +62 −0 src/sentry/static/sentry/less/pygments.less
  121. +460 −0 src/sentry/static/sentry/less/select2.less
  122. +1,202 −293 src/sentry/static/sentry/less/sentry.less
  123. +50 −0 src/sentry/static/sentry/less/simple-slider.less
  124. +1 −1 src/sentry/static/sentry/less/wall.less
  125. +269 −36 src/sentry/static/sentry/scripts/app.js
  126. +8 −4 src/sentry/static/sentry/scripts/charts.js
  127. +0 −1 src/sentry/static/sentry/scripts/collections.js
  128. +2 −0 src/sentry/static/sentry/scripts/global.min.js
  129. +2 −0 src/sentry/static/sentry/scripts/legacy.min.js
Sorry, we could not display the entire diff because too many files (378) changed.
View
2 .coveragerc
@@ -0,0 +1,2 @@
+[report]
+omit=src/sentry/migrations/*
View
3 .gitignore
@@ -8,11 +8,12 @@
*.db
*.pid
pip-log.txt
+/htmlcov
/cover
/build
/dist
/node_modules/
-/src/sentry/static/CACHE/
/docs/html
/docs/doctrees
example/db.sqlite
+/src/sentry/static/CACHE/
View
8 .jshintignore
@@ -1,4 +1,10 @@
tests/
src/sentry/static/sentry/bootstrap/
src/sentry/static/sentry/scripts/lib/
-src/sentry/static/CACHE/
+src/sentry/static/CACHE/
+src/sentry/static/sentry/scripts/legacy.min.js
+src/sentry/static/sentry/scripts/legacy.map.js
+src/sentry/static/sentry/scripts/global.min.js
+src/sentry/static/sentry/scripts/global.map.js
+src/sentry/static/sentry/scripts/lib.min.js
+src/sentry/static/sentry/scripts/lib.map.js
View
11 .travis.yml
@@ -3,17 +3,14 @@ python:
- "2.6"
- "2.7"
env:
- - DJANGO=1.4.2 DB=sqlite
- - DJANGO=1.4.2 DB=mysql
- - DJANGO=1.4.2 DB=postgres
+ - DB=sqlite
+ - DB=mysql
+ - DB=postgres
before_script:
- mysql -e 'create database sentry;'
- psql -c 'create database sentry;' -U postgres
install:
- - pip install mysql-python
- - pip install psycopg2
- - pip install -q Django==$DJANGO --use-mirrors
- - make develop
+ - make develop dev-postgres dev-mysql
script:
- make test
notifications:
View
137 CHANGES
@@ -1,3 +1,134 @@
+Version 5.4.0
+-------------
+
+SENTRY_PUBLIC now dictates whether or not a Sentry install should be considered
+accessible by all users or not. This should better solve the use-case of companies
+hosting a Sentry instance internally and not necessarily needing the permissions
+that teams give you.
+
+If enabled, all teams and all projects will be accessible all members (for any endpoint
+which does not require a certain level of access). Project.is_public now dictates the
+implied state of all events, but will not allow a non-member to browse events.
+
+Additionally, this includes the following other items:
+
+- [Important!] The URLS for social authentication have been moved.
+- Improved rendering of data values in all interfaces.
+- django-compressor was replaced with django-static-compiler.
+- A better defined public view of events (which removes several items from being visible).
+- Improved SourceMap discovery.
+- Most events will no longer cause a hard error when validation fails. Instead we attempt
+ to drop any non-required data so that at least a partial event is stored.
+- MessageCountByMinute was renamed to GroupCountByMinute.
+- MessageFilterValue was renamed to GroupTag.
+- Syntax highlighting was disabled (pending performance solutions).
+- Added Team.date_added column.
+
+
+Version 5.3.0
+-------------
+
+A brand new Sentry design has landed.
+
+Some things of note:
+
+- An improved dashboard.
+- Improved activity feeds on aggregate details.
+- Similar event navigation links on aggregate details.
+- Redesigned team management flow.
+
+Additionally:
+
+- Aggregates now happen on (project, checksum), which means events that have different
+ levels or culprits can now be grouped together.
+- Sentry now requires authentication for all pages.
+- SENTRY_PUBLIC behavior has changed to signify the default state of projects.
+- Project slug's are now only unique within a team.
+
+Version 5.2.2
+-------------
+
+- [New] The dashboard will now stream updates to the new and trending event components.
+
+Version 5.2.1
+-------------
+
+- [Fix] Trends sorting options on the Stream page work correctly again.
+
+Version 5.2.0
+-------------
+
+- [New] A new activity stream exists on event pages.
+- [New] Syntax highlighting now exists on all context frames.
+- [New] Support for JavaScript Sourcemaps now exists.
+- [New] The server will now fetch remote source files for JavaScript events.
+- [New] Sentry will now ask for your project's platform.
+- [Fix] Resolved -> Regressed state change is now atomic.
+- [Fix] ``cleanup`` now runs with lower resource overhead.
+- [Fix] Cookies will now be coerced to dicts if possible.
+- More (storage only still) work on user tracking.
+- Several indexes were added to speed up various queries.
+- Removed savepoint use in plugin hooks (99% of the time there were no queries).
+
+Additional, the following client protocol changes are part of this release:
+
+- Cookies should be not be sent by default.
+- POST data should not be sent by default.
+- Recommended values regexp for sanitizing credit cards was updated.
+- ``colno`` was added to the Stacktrace spec.
+- Timestamps that are more than one minute in the future are now discarded.
+- (Undocumented) Client-side support now exists for GET + Referrer store requests.
+
+We've also reduced the test suite time down to 25% of what it originally was (thanks, Alex!)
+
+Version 5.1.3
+-------------
+
+A new user's affected-tracking mechanism is present (storage only). This will
+become available in the UX in a future version, and relies on the existing HTTP
+and User interface datas.
+
+- [Fix] Correct a bug with search queries.
+- [New] Group.users_seen will now track unique users when possible.
+- [New] Team and project owners can now change ownership (non-superuser).
+- [Fix] Counts are now formatted correctly when number of visible digits > 3.
+
+Version 5.1.2
+-------------
+
+- [Fix] Option schema (key length = 64).
+- [Fix] Template interface now renders correctly.
+- [Fix] Update design on admin status pages.
+- [New] iOS client documentation.
+- Improved client documentation visuals.
+
+Version 5.1.1
+-------------
+
+Several schema changes are made in this upgrade:
+
+- A new model: LostPasswordHash
+- Two new fields on ProjectKey: date_added and user_added
+- A new field on Event: platform
+- A new field on Group: platform
+
+The following changes are also part of this release:
+
+- [New] A new plugin, sentry-interface-types will now automatically tag the available interface
+ types in an event.
+- [New] The platform value sent with an event is now recorded in the database.
+- [New] A recover account flow has been added.
+- [New] There is now a sticky nav on the event details pages.
+- [New] getting started page now shows if there's no data for a project.
+- [New] An API key management page now exists (under project settings).
+- [New] A tag overview page now exists for group details pages.
+- [Fix] Line numbers now show correctly in collapsed source context.
+- [Fix] Pending members now show correctly on the team management page (under project settings).
+- [Fix] The time since value now updates correctly when events change.
+- Various changes to how event details components render.
+- Various fixes for breadcrumbs and header styles.
+- Most plugins will no longer default to enabled on new projects.
+
Version 5.1.0
-------------
@@ -213,14 +344,14 @@ now __init__(request, project).
Version 3.6.0
-------------
-This version focuses on improving mebership control. It includes the ability to revoke
+This version focuses on improving membership control. It includes the ability to revoke
project members access temporarily (by suspending them), as well as revoking users
(outside of Sentry) via the user.is_active flag.
Additionally, it brings the beginnings of an "invite user" flow, allowing you to
invite users who may or may not already have accounts in the system. We plan to improve
this flow in an upcoming release to allow invitees easy registration within the system
-if they dont already have an account.
+if they don't already have an account.
Version 3.5.0
-------------
@@ -408,7 +539,7 @@ Version 2.2.1
Version 2.2.0
-------------
-* Sentry has a brand new design utlizing Bootstrap 2.
+* Sentry has a brand new design utilizing Bootstrap 2.
* Superusers can now create projects for users.
Version 2.1.3
View
2 MANIFEST.in
@@ -1,6 +1,6 @@
include setup.py README.rst MANIFEST.in LICENSE AUTHORS
recursive-include src/sentry/templates *
recursive-include src/sentry/locale *
-recursive-include src/sentry/static *
+recursive-include src/sentry/static/sentry *
recursive-include src/sentry/plugins/*/templates *
global-exclude *~
View
35 Makefile
@@ -4,14 +4,24 @@ STATIC_DIR = src/sentry/static/sentry
BOOTSTRAP_JS = ${STATIC_DIR}/scripts/lib/bootstrap.js
BOOTSTRAP_JS_MIN = ${STATIC_DIR}/scripts/lib/bootstrap.min.js
UGLIFY_JS ?= node_modules/uglify-js/bin/uglifyjs
-LESS = node_modules/less/bin/lessc
develop: update-submodules
npm install
- pip install "flake8>=1.6" --use-mirrors
+ pip install "file://`pwd`#egg=sentry[dev]"
+ pip install "file://`pwd`#egg=sentry[tests]"
pip install -e . --use-mirrors
-build: static locale
+dev-postgres:
+ pip install "file://`pwd`#egg=sentry[dev]"
+ pip install "file://`pwd`#egg=sentry[postgres]"
+ pip install -e . --use-mirrors
+
+dev-mysql:
+ pip install "file://`pwd`#egg=sentry[dev]"
+ pip install "file://`pwd`#egg=sentry[mysql]"
+ pip install -e . --use-mirrors
+
+build: locale
clean:
rm -r src/sentry/static/CACHE
@@ -24,16 +34,18 @@ compile-bootstrap-js:
@cat src/bootstrap/js/bootstrap-transition.js src/bootstrap/js/bootstrap-alert.js src/bootstrap/js/bootstrap-button.js src/bootstrap/js/bootstrap-carousel.js src/bootstrap/js/bootstrap-collapse.js src/bootstrap/js/bootstrap-dropdown.js src/bootstrap/js/bootstrap-modal.js src/bootstrap/js/bootstrap-tooltip.js src/bootstrap/js/bootstrap-popover.js src/bootstrap/js/bootstrap-scrollspy.js src/bootstrap/js/bootstrap-tab.js src/bootstrap/js/bootstrap-typeahead.js src/bootstrap/js/bootstrap-affix.js ${STATIC_DIR}/scripts/bootstrap-datepicker.js > ${BOOTSTRAP_JS}
${UGLIFY_JS} -nc ${BOOTSTRAP_JS} > ${BOOTSTRAP_JS_MIN};
-
-static:
- ${LESS} --strict-imports ${STATIC_DIR}/less/sentry.less ${STATIC_DIR}/styles/sentry.css
- @echo "Static assets successfully built! - `date`";
+install-test-requirements:
+ pip install "file://`pwd`#egg=sentry[tests]"
update-submodules:
git submodule init
git submodule update
-test: lint test-js test-python
+test: install-test-requirements lint test-js test-python
+
+testloop: install-test-requirements
+ pip install pytest-xdist --use-mirrors
+ py.test tests -f
test-js:
@echo "Running JavaScript tests"
@@ -49,17 +61,16 @@ lint: lint-python lint-js
lint-python:
@echo "Linting Python files"
- flake8 --exclude=migrations --ignore=E501,E225,E121,E123,E124,E125,E127,E128 --exit-zero src/sentry || exit 1
+ flake8 --exclude=migrations,src/sentry/static/CACHE/* --ignore=E501,E225,E121,E123,E124,E125,E127,E128 src/sentry
@echo ""
lint-js:
@echo "Linting JavaScript files"
@${NPM_ROOT}/jshint/bin/hint src/sentry/ || exit 1
@echo ""
-coverage:
- cd src && coverage run --include=sentry/* setup.py test && \
- coverage html --omit=*/migrations/* -d cover
+coverage: install-test-requirements
+ py.test --cov=src/sentry --cov-report=html
.PHONY: build
View
2 README.rst
@@ -16,6 +16,8 @@ Screenshots
.. image:: https://github.com/getsentry/sentry/raw/master/docs/images/event.png
+.. image:: https://github.com/getsentry/sentry/raw/master/docs/images/dashboard.png
+
Sentry is a Server
------------------
View
10 conftest.py
@@ -5,6 +5,9 @@
def pytest_configure(config):
+ import warnings
+ warnings.filterwarnings('error', '', Warning, r'(sentry|raven)')
+
if not settings.configured:
os.environ['DJANGO_SETTINGS_MODULE'] = 'sentry.conf.server'
@@ -30,9 +33,16 @@ def pytest_configure(config):
'NAME': ':memory:',
})
+ # Disable static compiling in tests
+ settings.STATIC_BUNDLES = {}
+
# override a few things with our test specifics
settings.INSTALLED_APPS = tuple(settings.INSTALLED_APPS) + (
'tests',
)
settings.SENTRY_KEY = base64.b64encode(os.urandom(40))
settings.SENTRY_PUBLIC = False
+ # This speeds up the tests considerably, pbkdf2 is by design, slow.
+ settings.PASSWORD_HASHERS = [
+ 'django.contrib.auth.hashers.MD5PasswordHasher',
+ ]
View
2 docs/buffer/index.rst
@@ -36,7 +36,7 @@ The first thing you will need to do is install two additional required packages:
::
- pip install redis nydus
+ pip install redis hiredis nydus
Finally, configure the buffer options:
View
8 docs/client/index.rst
@@ -11,13 +11,15 @@ protocol:
- Chef (`chef-sentry-handler <https://github.com/coderanger/chef-sentry-handler>`_)
- JavaScript (`raven-js <https://github.com/getsentry/raven-js>`_)
- Node.js (`raven-node <https://github.com/mattrobenolt/raven-node>`_)
+- iOS / Objective-C (`raven-objc <https://github.com/getsentry/raven-objc>`_)
+- C# (`raven-csharp <https://github.com/getsentry/raven-csharp>`_)
-Additionally, the following experimental clients are availabe:
+Additionally, the following experimental clients are available:
-- CFML (`raven-cfml <https://github.com/jmacul2/raven-cfml>`_)
-- R (`logging <http://logging.r-forge.r-project.org/>`_)
- Action Script 3 (`raven-as3 <https://github.com/skitoo/raven-as3>`_)
+- CFML (`raven-cfml <https://github.com/jmacul2/raven-cfml>`_)
- Erlang (`raven-erlang <https://github.com/soundrop/raven-erlang>`_)
+- R (`logging <http://logging.r-forge.r-project.org/>`_)
Client Criteria
---------------
View
65 docs/config/index.rst
@@ -3,7 +3,7 @@ Configuration
This document describes additional configuration options available to the Sentry server. If you are looking for documentation for the client, it is maintained in the `Raven <http://github.com/getsentry/raven-python>`_ project.
-.. note:: While the options below are labeled without the ``SENTRY_`` prefix, when you are configuring them via your ``settings.py`` you **must* specify the prefix.
+.. note:: While the options below are labeled without the ``SENTRY_`` prefix, when you are configuring them via your ``settings.py`` you **must** specify the prefix.
.. data:: sentry.conf.KEY
:noindex:
@@ -60,7 +60,6 @@ This document describes additional configuration options available to the Sentry
'sentry.filters.StatusFilter',
'sentry.filters.LoggerFilter',
'sentry.filters.LevelFilter',
- 'sentry.filters.ServerNameFilter',
)
.. data:: sentry.conf.LOG_LEVELS
@@ -96,9 +95,12 @@ Authentication
.. data:: sentry.conf.PUBLIC
:noindex:
- Should Sentry be protected by a username and password (using @login_required) or be publicly accessible.
+ Should Sentry make all data publicly accessible? This should **only** be
+ used if you're installing Sentry behind your company's firewall.
- Defaults to ``False`` (password protection).
+ Users will still need to have an account to view any data.
+
+ Defaults to ``False``.
::
@@ -159,18 +161,6 @@ Authentication
.. _Mozilla developer docs: https://developer.mozilla.org/En/HTTP_access_control#Simple_requests
-.. data:: sentry.conf.USE_JS_CLIENT
- :noindex:
-
- Instructs Sentry to install its JavaScript error handler to catch internal errors in the
- Sentry client-side code.
-
- Defaults to ``False``.
-
- ::
-
- SENTRY_USE_JS_CLIENT = True
-
Notifications
-------------
@@ -182,49 +172,6 @@ notifications have been moved to the ``sentry.plugins.sentry_mail``. You'll need
The following settings now act as default values for the ``sentry_mail`` plugin, and can be overwritten per-project
by visiting the plugin configuration page for that project.
-.. data:: sentry.conf.MAIL_LEVEL
- :noindex:
-
- .. versionadded:: 1.10.0
-
- The threshold level to restrict emails to.
-
- Defaults to ``logging.DEBUG``.
-
- ::
-
- SENTRY_MAIL_LEVEL = logging.DEBUG
-
-.. data:: sentry.conf.MAIL_INCLUDE_LOGGERS
- :noindex:
-
- .. versionadded:: 1.10.0
-
- An explicit list of all logger names to restrict emails to.
-
- Defaults to ``None``, which means to "all loggers".
-
- ::
-
- SENTRY_MAIL_INCLUDE_LOGGERS = (
- 'my.custom.logger.name',
- )
-
-.. data:: sentry.conf.MAIL_EXCLUDE_LOGGERS
- :noindex:
-
- .. versionadded:: 1.10.0
-
- An explicit list of all logger names to exclude from emails.
-
- Defaults to ``[]``.
-
- ::
-
- SENTRY_MAIL_EXCLUDE_LOGGERS = (
- 'some.annoying.logger',
- )
-
.. data:: sentry.conf.EMAIL_SUBJECT_PREFIX
:noindex:
View
38 docs/contributing/index.rst
@@ -49,13 +49,20 @@ Create a default Sentry configation just as if this were a production instance:
sentry init
-Voila! You're all set to begin developing!
+You'll likely want to make some changes to the default configuration (we recommend developing against Postgres, for example). Once done, migrate your database using the following command:
+
+::
+
+ sentry upgrade
+
+
+.. note:: The ``upgrade`` shortcut is simply a combination of South's migrate, and Django's syncdb commands.
Coding Standards
----------------
-Sentry follows the guidelines layed out in `pep8 <http://www.python.org/dev/peps/pep-0008/>`_ with a little bit
+Sentry follows the guidelines laid out in `pep8 <http://www.python.org/dev/peps/pep-0008/>`_ with a little bit
of flexibility on things like line length. We always give way for the `Zen of Python <http://www.python.org/dev/peps/pep-0020/>`_. We also use strict mode for JavaScript, enforced by jshint.
You can run all linters with ``make lint``, or respectively ``lint-python`` or ``lint-js``.
@@ -78,6 +85,33 @@ You'll notice that the test suite is structured based on where the code lives, a
.. note:: We use py.test for the Python test suite, and a combination of phantomjs and jasmine for the JavaScript tests.
+Static Media
+------------
+
+Sentry uses a library that compiles it's static media assets (LESS and JS files) automatically. If you're developing using
+runserver you'll see changes happen not only in the original files, but also the minified or processed versions of the file.
+
+If you've made changes and need to compile them by hand for any reason, you can do so by running:
+
+::
+
+ sentry compilestatic
+
+The minified and processed files should be committed alongside the unprocessed changes.
+
+Developing with Django
+----------------------
+
+Because Sentry is just Django, you can use all of the standard Django functionality. The only difference is you'll be accessing commands that would normally go through manage.py using the ``sentry`` CLI helper instead.
+
+For example, you probably don't want to use ``sentry start`` for development, as it doesn't support anything like
+automatic reloading on code changes. For that you'd want to use the standard builtin ``runserver`` command:
+
+::
+
+ sentry runserver
+
+
Contributing Back Code
----------------------
View
48 docs/developer/client/index.rst
@@ -13,11 +13,18 @@ The following items are expected of production-ready clients:
* Scrubbing w/ processors
* Tag support
+Feature based support is required for the following:
+
+* If cookie data is available, it's not sent by default
+* If POST data is available, it's not sent by default
+
Additionally, the following features are highly encouraged:
* Automated error handling (e.g. default error handlers)
* Logging integration (to whatever standard solution is available)
* Non-blocking event submission
+* Basic data sanitization (e.g. filtering out values that look like passwords)
+
Client Usage (End-user)
-----------------------
@@ -29,7 +36,7 @@ almost identical no matter the language:
::
- var myClient = new RavenClient('http://public_key:secret_key@example.com/default');
+ var myClient = new RavenClient('http://public_key:secret_key@example.com/project-id');
2. Capturing an event
@@ -47,7 +54,7 @@ The constructor ideally allows several configuration methods. The first argument
always be the DSN value (if possible), followed by an optional secondary argument which is
a map of options::
- client = new RavenClient('http://public_key:secret_key@example.com/default', {
+ client = new RavenClient('http://public_key:secret_key@example.com/project-id', {
'tags': {'foo': 'bar'}
})
@@ -84,11 +91,11 @@ like the following::
Finally, provide a CLI to test your client's configuration. Python example::
- raven test http://public_key:secret_key@example.com/default
+ raven test http://public_key:secret_key@example.com/project-id
Ruby example::
- rake raven:test http://public_key:secret_key@example.com/default
+ rake raven:test http://public_key:secret_key@example.com/project-id
Parsing the DSN
---------------
@@ -102,18 +109,31 @@ allow the first argument as a DSN string. This string contains the following bit
For example, given the following constructor::
- new RavenClient('https://public:secret@example.com/sentry/default')
+ new RavenClient('https://public:secret@example.com/sentry/project-id')
You should parse the following settings:
-* URI = 'https://example.com/sentry/''
+* URI = 'https://example.com/sentry/'
* Public Key = 'public'
* Secret Key = 'secret'
-* Project ID = 'default'
+* Project ID = 'project-id'
If any of these values are not present, the client should notify the user immediately
that they've misconfigured the client.
+The final endpoint you'll be sending requests to is constructed per the following:
+
+::
+
+ '{URI}api/{PROJECT ID}/store/'
+
+So in this case, it would end up as:
+
+::
+
+ 'https://example.com/sentry/api/project-id/store/'
+
+
The protocol value may also include a transport option. For example, in the Python client several
transports are available on top of HTTP:
@@ -150,6 +170,8 @@ The following attributes are required for all events:
Hexadecimal string representing a uuid4 value.
+ Maximum length is 32 characters.
+
::
{
@@ -160,6 +182,8 @@ The following attributes are required for all events:
User-readable representation of this event
+ Maximum length is 1000 characters.
+
::
{
@@ -364,20 +388,20 @@ A Working Example
-----------------
When all is said and done, you should be sending an HTTP POST request to a Sentry webserver, where
-the path is the BASE_URI/api/store/. So given the following DSN::
+the path is the BASE_URI/api/PROJECT_ID/store/. So given the following DSN::
https://b70a31b3510c4cf793964a185cfe1fd0:b7d80b520139450f903720eb7991bf3d@example.com/1
The request body should then somewhat resemble the following::
- POST /api/store/
+ POST /api/project-id/store/
User-Agent: raven-python/1.0
X-Sentry-Auth: Sentry sentry_version=3, sentry_timestamp=1329096377,
sentry_key=b70a31b3510c4cf793964a185cfe1fd0, sentry_client=raven-python/1.0,
sentry_secret=b7d80b520139450f903720eb7991bf3d
{
- "project": "default",
+ "project": "project-id",
"event_id": "fc6d8c0c43fc4630ad850ee518f1b9d0",
"culprit": "my.module.function_name",
"timestamp": "2011-05-02T17:41:36",
@@ -442,7 +466,7 @@ For example, if you simply supported callbacks for processors, it might look lik
We recommend scrubbing the following values::
* Values where the keyname matches 'password', 'passwd', or 'secret'.
-* Values that match the regular expression of ``r'^\d{16}$'`` (credit card-like).
+* Values that match the regular expression of ``r'^(?:\d[ -]*?){13,16}$'`` (credit card-like).
* Session cookies.
* The Authentication header (HTTP).
@@ -471,7 +495,7 @@ And then you capture an event::
'tags': {'foo': 'baz'},
})
-The client should send the following usptream for ``tags``::
+The client should send the following upstream for ``tags``::
{
"tags": [
View
BIN docs/images/dashboard.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN docs/images/event.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN docs/images/group_list.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
2 docs/index.rst
@@ -28,6 +28,7 @@ Developers
.. toctree::
:maxdepth: 2
+ contributing/index
developer/client/index
developer/plugins/index
developer/interfaces/index
@@ -38,7 +39,6 @@ Reference
.. toctree::
:maxdepth: 1
- contributing/index
changelog/index
license/index
View
6 docs/plugins/index.rst
@@ -54,12 +54,16 @@ The following extensions are available and maintained by members of the Sentry c
* `sentry-irc <https://github.com/gisce/sentry-irc>`_
* `sentry-irccat <https://github.com/russss/sentry-irccat>`_
* `sentry-jira <https://github.com/thurloat/sentry-jira>`_
+* `sentry-notifico <https://github.com/lukegb/sentry-notifico>`_
* `sentry-phabricator <https://github.com/getsentry/sentry-phabricator>`_
* `sentry-pivotal <https://github.com/getsentry/sentry-pivotal>`_
* `sentry-pushover <https://github.com/dz0ny/sentry-pushover>`_
+* `sentry-searchbutton <https://github.com/timmyomahony/sentry-searchbutton>`_
* `sentry-sprintly <https://github.com/mattrobenolt/sentry-sprintly>`_
* `sentry-trello <https://github.com/DamianZaremba/sentry-trello>`_
* `sentry-webhooks <https://github.com/getsentry/sentry-webhooks>`_
+* `sentry-whatsapp <https://github.com/ecarreras/sentry-whatsapp>`_
+* `sentry-youtrack <https://github.com/bogdal/sentry-youtrack>`_
Have an extension that should be listed here? Submit a `pull request <https://github.com/getsentry/sentry>`_ and we'll
-get it added.
+get it added.
View
6 docs/queue/index.rst
@@ -17,7 +17,7 @@ which the worker manager process of the Celery library.
::
- sentry celeryd
+ sentry celeryd -B
Enable the Queue
----------------
@@ -34,8 +34,4 @@ like Redis will do just fine.
An example configuration using a local Redis server might look like this::
- CELERY_IGNORE_RESULT = True
- CELERY_SEND_EVENTS = False
- CELERY_RESULT_BACKEND = 'redis'
-
BROKER_URL = "redis://localhost:6379/0"
View
58 docs/quickstart/index.rst
@@ -41,7 +41,7 @@ Setting up an Environment
The first thing you'll need is the Python ``virtualenv`` package. You probably already
have this, but if not, you can install it with::
- easy_install -U virtualenv
+ easy_install -UZ virtualenv
Once that's done, choose a location for the environment, and create it with the ``virtualenv``
command. For our guide, we're going to choose ``/www/sentry/``::
@@ -61,17 +61,44 @@ Install Sentry
Once you've got the environment setup, you can install Sentry and all its dependencies with
the same command you used to grab virtualenv::
- easy_install -U sentry
+ easy_install -UZ sentry
Don't be worried by the amount of dependencies Sentry has. We have a philosophy of using the right tools for
the job, and not reinventing them if they already exist.
+Using MySQL or Postgres
+~~~~~~~~~~~~~~~~~~~~~~~
+
+We **highly** recommend using PostgreSQL for your database, or MySQL if you have no other choice. The default
+is sqlite and will handle very little load.
+
+These databases require additional packages, but Sentry provides a couple of meta packages to make things easier:
+
+::
+
+ # install sentry and its postgresql dependencies
+ easy_install -UZ sentry[postgres]
+
+ # or if you choose, mysql
+ easy_install -UZ sentry[mysql]
+
+
+Installing from Source
+~~~~~~~~~~~~~~~~~~~~~~
+
+If you're installing the Sentry source (e.g. from git), you'll simply need to run the ``make`` command to
+get all of the dependencies::
+
+ # all things should be this easy
+ make
+
Once everything's installed, you should be able to execute the Sentry CLI, via ``sentry``, and get something
like the following::
$ sentry
usage: sentry [--config=/path/to/settings.py] [command] [options]
+
Initializing the Configuration
------------------------------
@@ -114,6 +141,7 @@ is not a fully supported database and should not be used in production**.
SENTRY_WEB_PORT = 9000
SENTRY_WEB_OPTIONS = {
'workers': 3, # the number of gunicorn workers
+ 'secure_scheme_headers': {'X-FORWARDED-PROTO': 'https'}, # detect HTTPS mode from X-Forwarded-Proto header
}
@@ -131,7 +159,7 @@ standard implementation, using a simple SMTP server, you can simply configure th
Being that Django is a pluggable framework, you also have the ability to specify different mail backends. See the
`official Django documentation <https://docs.djangoproject.com/en/1.3/topics/email/?from=olddocs#email-backends>`_ for
-more information on alterantive backends.
+more information on alternative backends.
Running Migrations
------------------
@@ -194,6 +222,8 @@ Apache requires the use of mod_proxy for forwarding requests::
ProxyPass / http://localhost:9000/
ProxyPassReverse / http://localhost:9000/
+ ProxyPreserveHost On
+ RequestHeader set X-Forwarded-Proto "https" env=HTTPS
Proxying with Nginx
~~~~~~~~~~~~~~~~~~~
@@ -204,9 +234,10 @@ You'll use the builtin HttpProxyModule within Nginx to handle proxying::
proxy_pass http://localhost:9000;
proxy_redirect off;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
}
Running Sentry as a Service
@@ -337,9 +368,20 @@ following to your ``sentry.conf.py``::
'django_bcrypt',
)
+Configuring Memcache
+~~~~~~~~~~~~~~~~~~~~
+
You'll also want to consider configuring cache and buffer settings, which respectively require a cache server and a Redis
-server. While the Django configuration covers caching in great detail, Sentry allows you to specify a backend for its
-own internal purposes::
+server. You'll need to do two things, starting with installing the memcache dependencies:
+
+::
+
+ pip install python-memcached
+
+While the Django configuration covers caching in great detail, Sentry allows you to specify a backend for its
+own internal purposes:
+
+::
# You'll need to install django-pyblibmc for this example to work
CACHES = {
View
23 docs/upgrading/index.rst
@@ -30,6 +30,18 @@ To fake the migrations, run the following::
sentry migrate kombu.transport.django 0001 --fake
+Conflicts with social_auth
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A recent release of django-social-auth (0.7.18) added support for South migrations. This means that if you had an older
+version of the package installed, you'll need to "fake" the migrations, as they were already applied.
+
+**You should only do this is you actually receive an error while migrating.**
+
+To fake the migrations, run the following::
+
+ sentry migrate social_auth 0001 --fake
+
Upgrading from 1.x
~~~~~~~~~~~~~~~~~~
@@ -46,8 +58,13 @@ Upgrading to >= 5.1
~~~~~~~~~~~~~~~~~~~
Version 5.1 of Sentry includes a large set of changes including a new client protocol (version 3). It is
-fully compatible with version 2.0 of the protocl, but no longer supports several deprecated features, including
+fully compatible with version 2.0 of the protocol, but no longer supports several deprecated features, including
version 1.0.
-You should always upgrade your client first, but if you're upgrading from a very old version of Sentry, you may
-have a lapse in data during your upgrade process.
+If you're upgrading from a very old version of Sentry, you may have a lapse in data during your upgrade process.
+
+Upgrading from <= 5.3
+~~~~~~~~~~~~~~~~~~~~~
+
+If you were previously using social auth backends, take note that the callback URLs have been moved. They are now
+all prefixed with '/account/settings/social'.
View
3 example/templates/layout.html
@@ -1,3 +1,4 @@
+{% load url from future %}
{% load i18n %}
{% load sentry_helpers %}
@@ -6,7 +7,7 @@
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="robots" content="NONE,NOARCHIVE">
- <link href="{% url sentry-media "sentry" "styles/global.min.css" %}" rel="stylesheet" type="text/css"/>
+ <link href="{% url 'sentry-media' "sentry" "styles/global.min.css" %}" rel="stylesheet" type="text/css"/>
<title>{% block title %}Sentry{% endblock %}</title>
{% block meta %}
{% endblock %}
View
4 package.json
@@ -2,9 +2,9 @@
"name": "Sentry",
"version": "0.1.0",
"dependencies": {
- "less": "1.3.x",
+ "less": "1.3.3",
"phantomjs": "0.2.x",
"jshint": "0.9.x",
- "uglify-js": "2.2.1"
+ "uglify-js": "2.2.4"
}
}
View
68 setup.py
@@ -34,45 +34,64 @@
except ImportError:
pass
+dev_requires = [
+ 'flake8>=1.7.0',
+ 'pytest-cov>=1.4',
+]
+
tests_require = [
'exam>=0.5.1',
'eventlet',
'pytest',
'pytest-django',
'nydus',
'mock>=0.8.0',
+ 'mock-django>=0.6.4',
'redis',
'unittest2',
]
install_requires = [
- 'cssutils>=0.9.9',
- 'BeautifulSoup>=3.2.1',
- 'django-celery>=2.5.5',
- 'celery>=2.5.3',
- 'django-compressor>=1.2,<1.3',
- 'django-crispy-forms>=1.1.4',
- 'Django>=1.4.2,<=1.5',
- 'django-indexer>=0.3.0',
- 'django-paging>=0.2.4',
- 'django-picklefield>=0.2.0',
- 'django-templatetag-sugar>=0.1.0',
- 'gunicorn>=0.14.6',
- 'logan>=0.5.4',
- 'pynliner>=0.4.0',
+ 'cssutils>=0.9.9,<0.10.0',
+ 'BeautifulSoup>=3.2.1,<3.3.0',
+ 'django-celery>=3.0.11,<3.1.0',
+ 'celery>=3.0.15,<3.1.0',
+ 'django-crispy-forms>=1.2.3,<1.3.0',
+ 'Django>=1.4.5,<1.5',
+ 'django-indexer>=0.3.0,<0.4.0',
+ 'django-paging>=0.2.4,<0.3.0',
+ 'django-picklefield>=0.3.0,<0.4.0',
+ 'django-static-compiler>=0.3.0,<0.4.0',
+ 'django-templatetag-sugar>=0.1.0,<0.2.0',
+ 'gunicorn>=0.17.2,<0.18.0',
+ 'logan>=0.5.5,<0.6.0',
+ 'nydus>=0.10.0,<0.11.0',
+ 'Pygments>=1.6.0,<1.7.0',
+ 'pynliner>=0.4.0,<0.5.0',
'python-dateutil>=1.5.0,<2.0.0',
- 'raven>=2.0.12.2',
- 'simplejson>=2.1.6',
- 'South>=0.7.6',
- 'httpagentparser>=1.0.5',
- 'django-social-auth>=0.7.1,<1.0',
- 'django-social-auth-trello>=1.0.2',
+ 'raven>=3.1.17',
+ 'redis>2.7.0,<2.8.0',
+ 'simplejson>=3.1.0,<3.2.0',
+ 'South>=0.7.6,<0.8.0',
+ 'httpagentparser>=1.2.1,<1.3.0',
+ 'django-social-auth>=0.7.1,<0.8.0',
+ 'django-social-auth-trello>=1.0.3,<1.1.0',
+ 'setproctitle>=1.1.7,<1.2.0',
+]
+
+postgres_requires = [
+ 'psycopg2>=2.4.0,<2.5.0',
]
+mysql_requires = [
+ 'MySQL-python>=1.2.0,<1.3.0',
+]
+
+
setup(
name='sentry',
- version='5.1.0',
+ version='5.4.5',
author='David Cramer',
author_email='dcramer@gmail.com',
url='http://www.getsentry.com',
@@ -82,7 +101,12 @@
packages=find_packages('src'),
zip_safe=False,
install_requires=install_requires,
- tests_require=tests_require,
+ extras_require={
+ 'tests': tests_require,
+ 'dev': dev_requires,
+ 'postgres': install_requires + postgres_requires,
+ 'mysql': install_requires + mysql_requires,
+ },
test_suite='runtests.runtests',
license='BSD',
include_package_data=True,
View
39 src/sentry/__init__.py
@@ -2,15 +2,48 @@
sentry
~~~~~~
-:copyright: (c) 2010-2012 by the Sentry Team, see AUTHORS for more details.
+:copyright: (c) 2010-2013 by the Sentry Team, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
+import os
+import os.path
try:
VERSION = __import__('pkg_resources') \
.get_distribution('sentry').version
except Exception, e:
VERSION = 'unknown'
-# We store global interpreter state in here
-environment = {}
+
+def _get_git_revision(path):
+ revision_file = os.path.join(path, 'refs', 'heads', 'master')
+ if not os.path.exists(revision_file):
+ return None
+ fh = open(revision_file, 'r')
+ try:
+ return fh.read().strip()[:7]
+ finally:
+ fh.close()
+
+
+def get_revision():
+ """
+ :returns: Revision number of this branch/checkout, if available. None if
+ no revision number can be determined.
+ """
+ package_dir = os.path.dirname(__file__)
+ checkout_dir = os.path.normpath(os.path.join(package_dir, os.pardir, os.pardir))
+ path = os.path.join(checkout_dir, '.git')
+ if os.path.exists(path):
+ return _get_git_revision(path)
+ return None
+
+
+def get_version():
+ base = VERSION
+ if __build__:
+ base = '%s (%s)' % (base, __build__)
+ return base
+
+__build__ = get_revision()
+__docformat__ = 'restructuredtext en'
View
7 src/sentry/app.py
@@ -2,7 +2,7 @@
sentry.app
~~~~~~~~~~
-:copyright: (c) 2010-2012 by the Sentry Team, see AUTHORS for more details.
+:copyright: (c) 2010-2013 by the Sentry Team, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
@@ -13,11 +13,12 @@
class State(local):
request = None
+ data = {}
-def get_buffer(path, options):
+def get_instance(path, options):
cls = import_string(path)
return cls(**options)
-buffer = get_buffer(settings.BUFFER, settings.BUFFER_OPTIONS)
+buffer = get_instance(settings.BUFFER, settings.BUFFER_OPTIONS)
env = State()
View
2 src/sentry/buffer/__init__.py
@@ -2,7 +2,7 @@
sentry.buffer
~~~~~~~~~~~~~
-:copyright: (c) 2010-2012 by the Sentry Team, see AUTHORS for more details.
+:copyright: (c) 2010-2013 by the Sentry Team, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
View
17 src/sentry/buffer/base.py
@@ -2,13 +2,14 @@
sentry.buffer.base
~~~~~~~~~~~~~~~~~~
-:copyright: (c) 2010-2012 by the Sentry Team, see AUTHORS for more details.
+:copyright: (c) 2010-2013 by the Sentry Team, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
from django.db.models import F
-from sentry.utils.queue import maybe_async
+from sentry.signals import buffer_incr_complete
from sentry.tasks.process_buffer import process_incr
+from sentry.utils.queue import maybe_async
class Buffer(object):
@@ -42,7 +43,17 @@ def process(self, model, columns, filters, extra=None):
update_kwargs = dict((c, F(c) + v) for c, v in columns.iteritems())
if extra:
update_kwargs.update(extra)
- model.objects.create_or_update(
+
+ _, created = model.objects.create_or_update(
defaults=update_kwargs,
**filters
)
+
+ buffer_incr_complete.send_robust(
+ model=model,
+ columns=columns,
+ filters=filters,
+ extra=extra,
+ created=created,
+ sender=model,
+ )
View
55 src/sentry/buffer/redis.py
@@ -2,52 +2,71 @@
sentry.buffer.redis
~~~~~~~~~~~~~~~~~~~
-:copyright: (c) 2010-2012 by the Sentry Team, see AUTHORS for more details.
+:copyright: (c) 2010-2013 by the Sentry Team, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
from __future__ import with_statement
+from django.core.exceptions import ImproperlyConfigured
+
+for package in ('nydus', 'redis'):
+ try:
+ __import__(package, {}, {}, [], -1)
+ except ImportError:
+ raise ImproperlyConfigured('Missing %r package, which is required for Redis buffers' %
+ (package,))
+
from django.db import models
+from django.utils.encoding import smart_str
from hashlib import md5
from nydus.db import create_cluster
from sentry.buffer import Buffer
+from sentry.conf import settings
from sentry.utils.compat import pickle
class RedisBuffer(Buffer):
key_expire = 60 * 60 # 1 hour
- def __init__(self, hosts=None, router='nydus.db.routers.keyvalue.PartitionRouter', **options):
+ def __init__(self, **options):
+ if not options:
+ # inherit default options from REDIS_OPTIONS
+ options = settings.REDIS_OPTIONS
+
super(RedisBuffer, self).__init__(**options)
- if hosts is None:
- hosts = {
- 0: {} # localhost / default
- }
+ options.setdefault('hosts', {
+ 0: {},
+ })
+ options.setdefault('router', 'nydus.db.routers.keyvalue.PartitionRouter')
self.conn = create_cluster({
'engine': 'nydus.db.backends.redis.Redis',
- 'router': router,
- 'hosts': hosts,
+ 'router': options['router'],
+ 'hosts': options['hosts'],
})
- def _map_column(self, model, column, value):
+ def _coerce_val(self, value):
if isinstance(value, models.Model):
value = value.pk
- else:
- value = unicode(value)
- return value
+ return smart_str(value)
def _make_key(self, model, filters, column):
"""
Returns a Redis-compatible key for the model given filters.
"""
- return '%s:%s:%s' % (model._meta,
- md5('&'.join('%s=%s' % (k, self._map_column(model, k, v)) for k, v in sorted(filters.iteritems()))).hexdigest(),
- column)
+ return '%s:%s:%s' % (
+ model._meta,
+ md5(smart_str('&'.join('%s=%s' % (k, self._coerce_val(v))
+ for k, v in sorted(filters.iteritems())))).hexdigest(),
+ column,
+ )
def _make_extra_key(self, model, filters):
- return '%s:extra:%s' % (model._meta,
- md5('&'.join('%s=%s' % (k, self._map_column(model, k, v)) for k, v in sorted(filters.iteritems()))).hexdigest())
+ return '%s:extra:%s' % (
+ model._meta,
+ md5(smart_str('&'.join('%s=%s' % (k, self._coerce_val(v))
+ for k, v in sorted(filters.iteritems())))).hexdigest(),
+ )
def incr(self, model, columns, filters, extra=None):
with self.conn.map() as conn:
@@ -87,7 +106,7 @@ def process(self, model, columns, filters, extra=None):
continue
extra[key] = pickle.loads(str(value))
- # Filter out empty or zero'd results to avoid a potentially unnescesary update
+ # Filter out empty or zero'd results to avoid a potentially unnecessary update
results = dict((k, int(v)) for k, v in results.iteritems() if int(v or 0) > 0)
if not results:
return
View
2 src/sentry/conf/__init__.py
@@ -2,6 +2,6 @@
sentry.conf
~~~~~~~~~~~
-:copyright: (c) 2010-2012 by the Sentry Team, see AUTHORS for more details.
+:copyright: (c) 2010-2013 by the Sentry Team, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
View
31 src/sentry/conf/defaults.py
@@ -4,7 +4,7 @@
Represents the default values for all Sentry settings.
-:copyright: (c) 2010-2012 by the Sentry Team, see AUTHORS for more details.
+:copyright: (c) 2010-2013 by the Sentry Team, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
@@ -74,20 +74,6 @@
MAX_SAMPLE_TIME = 10000
-# Restrict emails to only ``messages >= this value``.
-MAIL_LEVEL = logging.DEBUG
-
-# A list of loggers to restrict emails to.
-MAIL_INCLUDE_LOGGERS = None
-
-# A list of loggers to exclude in emails.
-MAIL_EXCLUDE_LOGGERS = []
-
-# Normalize counts to the 15 minute marker. This value MUST be less than 60. A
-# value of 0 would store counts for every minute, and is the lowest level of
-# accuracy provided.
-MINUTE_NORMALIZATION = 15
-
# The number of events to display per page
MESSAGES_PER_PAGE = 15
@@ -148,21 +134,21 @@
# Default sort option for the search results
SEARCH_DEFAULT_SORT_OPTION = 'date'
-# Default project access when a project owner is created
-DEFAULT_PROJECT_ACCESS = 'MEMBER_OWNER'
-
# Default to not sending the Access-Control-Allow-Origin header on api/store
ALLOW_ORIGIN = None
-# Enable capturing of JavaScript errors (Sentry internal errors)
-USE_JS_CLIENT = False
+# Enable scraping of javascript context for source code
+SCRAPE_JAVASCRIPT_CONTEXT = True
# The alias for the cache backend (MUST be a compatible backend string for < 1.3)
CACHE_BACKEND = 'dummy://'
# The maximum number of events which can be requested as JSON
MAX_JSON_RESULTS = 1000
+# Redis connection information (see Nydus documentation)
+REDIS_OPTIONS = {}
+
# Buffer backend to use
BUFFER = 'sentry.buffer.Buffer'
BUFFER_OPTIONS = {}
@@ -176,3 +162,8 @@
'trello': ('TRELLO_API_KEY', 'TRELLO_API_SECRET'),
'bitbucket': ('BITBUCKET_CONSUMER_KEY', 'BITBUCKET_CONSUMER_SECRET'),
}
+
+
+# Default alerting threshold values
+DEFAULT_ALERT_PROJECT_THRESHOLD = (500, 100) # 500%, 100 events
+DEFAULT_ALERT_GROUP_THRESHOLD = (1000, 100) # 1000%, 100 events
View
108 src/sentry/conf/server.py
@@ -4,7 +4,7 @@
These settings act as the default (base) settings for the Sentry-provided web-server
-:copyright: (c) 2010-2012 by the Sentry Team, see AUTHORS for more details.
+:copyright: (c) 2010-2013 by the Sentry Team, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
@@ -17,7 +17,7 @@
import sys
import urlparse
-DEBUG = True
+DEBUG = False
TEMPLATE_DEBUG = True
ADMINS = ()
@@ -142,49 +142,30 @@
'django.contrib.sites',
'django.contrib.staticfiles',
- 'compressor',
'crispy_forms',
'djcelery',
'gunicorn',
'kombu.transport.django',
- 'raven.contrib.django',
+ 'raven.contrib.django.raven_compat',
'sentry',
'sentry.plugins.sentry_interface_types',
'sentry.plugins.sentry_mail',
'sentry.plugins.sentry_servers',
'sentry.plugins.sentry_urls',
- 'sentry.plugins.sentry_user_emails',
'sentry.plugins.sentry_useragents',
'social_auth',
'south',
+ 'static_compiler',
'django_social_auth_trello',
)
STATIC_ROOT = os.path.join(PROJECT_ROOT, 'static')
STATIC_URL = '/_static/'
-NPM_ROOT = os.path.abspath(os.path.join(PROJECT_ROOT, os.pardir, os.pardir, 'node_modules'))
-if os.path.exists(NPM_ROOT):
- LESS_BIN = os.path.join(NPM_ROOT, 'less', 'bin', 'lessc')
-else:
- LESS_BIN = None
-
-# XXX: There is a bug in django-compressor that causes it to incorrectly handle
-# relative URLs in precompiled files (less) when compression is disabled
-if LESS_BIN:
- COMPRESS_ENABLED = True
- COMPRESS_URL = STATIC_URL
- COMPRESS_OUTPUT_DIR = 'CACHE'
- COMPRESS_PRECOMPILERS = (
- ('text/less', '%s --strict-imports {infile} {outfile}' % (LESS_BIN,)),
- )
-else:
- COMPRESS_ENABLED = False
-
STATICFILES_FINDERS = (
"django.contrib.staticfiles.finders.FileSystemFinder",
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
- "compressor.finders.CompressorFinder",
+ "static_compiler.finders.StaticCompilerFinder",
)
LOCALE_PATHS = (
@@ -253,13 +234,32 @@
SOCIAL_AUTH_PROTECTED_USER_FIELDS = ['email']
# Queue configuration
+from kombu import Queue
BROKER_URL = "django://"
CELERY_IGNORE_RESULT = True
CELERY_SEND_EVENTS = False
CELERY_RESULT_BACKEND = None
CELERY_TASK_RESULT_EXPIRES = 1
+CELERY_DISABLE_RATE_LIMITS = True
+CELERY_DEFAULT_QUEUE = "default"
+CELERY_DEFAULT_EXCHANGE = "default"
+CELERY_DEFAULT_EXCHANGE_TYPE = "direct"
+CELERY_DEFAULT_ROUTING_KEY = "default"
+CELERY_CREATE_MISSING_QUEUES = True
+CELERY_QUEUES = (
+ Queue('default', routing_key='default'),
+ Queue('celery', routing_key='celery'),
+ Queue('alerts', routing_key='alerts'),
+ Queue('cleanup', routing_key='cleanup'),
+ Queue('sourcemaps', routing_key='sourcemaps'),
+ Queue('search', routing_key='search'),
+ Queue('counters', routing_key='counters'),
+ Queue('events', routing_key='events'),
+ Queue('triggers', routing_key='triggers'),
+)
+
# Sentry and Raven configuration
@@ -309,6 +309,66 @@
}
}
+NPM_ROOT = os.path.abspath(os.path.join(PROJECT_ROOT, os.pardir, os.pardir, 'node_modules'))
+
+# We only define static bundles if NPM has been setup
+if os.path.exists(NPM_ROOT):
+ STATIC_BUNDLES = {
+ "packages": {
+ "sentry/scripts/global.min.js": {
+ "src": [
+ "sentry/scripts/core.js",
+ "sentry/scripts/models.js",
+ "sentry/scripts/templates.js",
+ "sentry/scripts/utils.js",
+ "sentry/scripts/collections.js",
+ "sentry/scripts/charts.js",
+ "sentry/scripts/views.js",
+ "sentry/scripts/app.js",
+ ],
+ },
+ "sentry/scripts/legacy.min.js": {
+ "src": [
+ "sentry/scripts/sentry.core.js",
+ "sentry/scripts/sentry.charts.js",
+ "sentry/scripts/sentry.stream.js",
+ ],
+ },
+ "sentry/scripts/lib.min.js": {
+ "src": [
+ "sentry/scripts/lib/jquery.js",
+ "sentry/scripts/lib/jquery-migrate.js",
+ "sentry/scripts/lib/jquery.animate-colors.js",
+ "sentry/scripts/lib/jquery.clippy.min.js",
+ "sentry/scripts/lib/jquery.cookie.js",
+ "sentry/scripts/lib/jquery.flot.min.js",
+ "sentry/scripts/lib/simple-slider.js",
+ "sentry/scripts/lib/json2.js",
+ "sentry/scripts/lib/underscore.js",
+ "sentry/scripts/lib/backbone.js",
+ "sentry/scripts/lib/select2/select2.js",
+ "sentry/scripts/lib/bootstrap.js",
+ ],
+ },
+ "sentry/styles/global.min.css": {
+ "src": {
+ "sentry/less/sentry.less": "sentry/styles/sentry.css",
+ },
+ },
+ "sentry/styles/wall.min.css": {
+ "src": {
+ "sentry/less/wall.less": "sentry/styles/wall.css",
+ },
+ },
+ },
+ "postcompilers": {
+ "*.js": ["node_modules/uglify-js/bin/uglifyjs {input} --source-map-root={relroot}/ --source-map-url={name}.map{ext} --source-map={relpath}/{name}.map{ext} -o {output}"],
+ },
+ "preprocessors": {
+ "*.less": ["node_modules/less/bin/lessc {input} {output}"],
+ },
+ }
+
# Configure celery
import djcelery
djcelery.setup_loader()
View
5 src/sentry/conf/settings.py
@@ -2,7 +2,7 @@
sentry.conf.settings
~~~~~~~~~~~~~~~~~~~~
-:copyright: (c) 2010-2012 by the Sentry Team, see AUTHORS for more details.
+:copyright: (c) 2010-2013 by the Sentry Team, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
@@ -32,9 +32,6 @@
if locals().get('REMOTE_TIMEOUT'):
TIMEOUT = REMOTE_TIMEOUT
-if locals().get('DEFAULT_PROJECT_ACCESS') not in ('MEMBER_OWNER', 'MEMBER_USER', 'MEMBER_SYSTEM'):
- DEFAULT_PROJECT_ACCESS = 'MEMBER_OWNER'
-
def get_all_languages():
results = []
View
4 src/sentry/conf/urls.py
@@ -11,7 +11,6 @@
import os
from sentry.web.urls import urlpatterns as web_urlpatterns
-from sentry.web.frontend import generic
from django.conf.urls.defaults import patterns, url, include
from django.contrib import admin
from django.views.defaults import page_not_found
@@ -41,7 +40,4 @@ def handler500(request):
urlpatterns = patterns('',
url(r'^i18n/', include('django.conf.urls.i18n')),
url(r'^admin/', include(admin.site.urls)),
- url(r'^_admin_media/(?P<path>.*)$', generic.static_media,
- kwargs={'root': admin_media_dir},
- name='admin-media'),
) + web_urlpatterns
View
80 src/sentry/constants.py
@@ -4,7 +4,7 @@
These settings act as the default (base) settings for the Sentry-provided web-server
-:copyright: (c) 2010-2012 by the Sentry Team, see AUTHORS for more details.
+:copyright: (c) 2010-2013 by the Sentry Team, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
@@ -50,26 +50,92 @@
'new': 'UNIX_TIMESTAMP(sentry_groupedmessage.first_seen)',
})
+ORACLE_SORT_CLAUSES = SCORE_CLAUSES.copy()
+ORACLE_SORT_CLAUSES.update({
+ 'date': "(cast(sentry_groupedmessage.last_seen as date)-TO_DATE('01/01/1970 00:00:00', 'MM-DD-YYYY HH24:MI:SS')) * 24 * 60 * 60",
+ 'new': "(cast(sentry_groupedmessage.first_seen as date)-TO_DATE('01/01/1970 00:00:00', 'MM-DD-YYYY HH24:MI:SS')) * 24 * 60 * 60",
+})
+ORACLE_SCORE_CLAUSES = ORACLE_SORT_CLAUSES.copy()
+
+MSSQL_SORT_CLAUSES = SCORE_CLAUSES.copy()
+MSSQL_SORT_CLAUSES.update({
+ 'date': "DATEDIFF(s, '1970-01-01T00:00:00', sentry_groupedmessage.last_seen)",
+ 'new': "DATEDIFF(s, '1970-01-01T00:00:00', sentry_groupedmessage.first_seen)",
+})
+MSSQL_SCORE_CLAUSES = MSSQL_SORT_CLAUSES.copy()
+
SEARCH_SORT_OPTIONS = SortedDict((
('score', _('Score')),
('date', _('Last Seen')),
('new', _('First Seen')),
))
+STATUS_VISIBLE = 0
+STATUS_HIDDEN = 1
+
STATUS_UNRESOLVED = 0
STATUS_RESOLVED = 1
STATUS_MUTED = 2
STATUS_LEVELS = (
- (STATUS_UNRESOLVED, _('unresolved')),
- (STATUS_RESOLVED, _('resolved')),
- (STATUS_MUTED, _('muted')),
+ (STATUS_UNRESOLVED, _('Unresolved')),
+ (STATUS_RESOLVED, _('Resolved')),
+ (STATUS_MUTED, _('Muted')),
)
MEMBER_OWNER = 0
MEMBER_USER = 50
MEMBER_SYSTEM = 100
MEMBER_TYPES = (
- (MEMBER_OWNER, _('admin')),
- (MEMBER_USER, _('user')),
- (MEMBER_SYSTEM, _('system agent')),
+ (MEMBER_OWNER, _('Admin')),
+ (MEMBER_USER, _('User')),
+ (MEMBER_SYSTEM, _('System Agent')),
)
+
+# A list of values which represent an unset or empty password on
+# a User instance.
+EMPTY_PASSWORD_VALUES = ('!', '', '$')
+
+PLATFORM_LIST = (
+ 'csharp',
+ 'connect',
+ 'django',
+ 'express',
+ 'flask',
+ 'ios',
+ 'java',
+ 'javascript',
+ 'node.js',
+ 'php',
+ 'python',
+ 'r',
+ 'ruby',
+ 'rails3',
+ 'sidekiq',
+ 'sinatra',
+)
+
+PLATFORM_ROOTS = {
+ 'rails3': 'ruby',
+ 'sinatra': 'ruby',
+ 'sidekiq': 'ruby',
+ 'django': 'python',
+ 'flask': 'python',
+ 'express': 'node.js',
+ 'connect': 'node.js',
+}
+
+PLATFORM_TITLES = {
+ 'rails3': 'Rails 3 (Ruby)',
+ 'php': 'PHP',
+ 'ios': 'iOS',
+ 'express': 'Express (Node.js)',
+ 'connect': 'Connect (Node.js)',
+ 'django': 'Django (Python)',
+ 'flask': 'Flask (Python)',
+ 'csharp': 'C#',
+}
+
+# Normalize counts to the 15 minute marker. This value MUST be less than 60. A
+# value of 0 would store counts for every minute, and is the lowest level of
+# accuracy provided.
+MINUTE_NORMALIZATION = 15
View
144 src/sentry/coreapi.py
@@ -2,14 +2,14 @@
sentry.coreapi
~~~~~~~~~~~~~~
-:copyright: (c) 2010-2012 by the Sentry Team, see AUTHORS for more details.
+:copyright: (c) 2010-2013 by the Sentry Team, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
# TODO: We should make the API a class, and UDP/HTTP just inherit from it
# This will make it so we can more easily control logging with various
-# metadata (rather than generic log messages which arent useful).
+# metadata (rather than generic log messages which aren't useful).
-from datetime import datetime
+from datetime import datetime, timedelta
import base64
import logging
import uuid
@@ -18,17 +18,31 @@
from django.contrib.auth.models import User
from django.utils.encoding import smart_str
+from sentry.app import env
from sentry.conf import settings
-from sentry.exceptions import InvalidInterface, InvalidData, InvalidTimestamp
+from sentry.exceptions import InvalidTimestamp
from sentry.models import Project, ProjectKey, TeamMember, Team
from sentry.plugins import plugins
from sentry.tasks.store import store_event
from sentry.utils import is_float, json
from sentry.utils.auth import parse_auth_header
from sentry.utils.imports import import_string
from sentry.utils.queue import maybe_delay
+from sentry.utils.strings import decompress
-logger = logging.getLogger('sentry.errors.coreapi')
+
+logger = logging.getLogger('sentry.coreapi.errors')
+
+MAX_CULPRIT_LENGTH = 200
+MAX_MESSAGE_LENGTH = 5000
+
+INTERFACE_ALIASES = {
+ 'exception': 'sentry.interfaces.Exception',
+ 'request': 'sentry.interfaces.Http',
+ 'user': 'sentry.interfaces.User',
+ 'stacktrace': 'sentry.interfaces.Stacktrace',
+ 'template': 'sentry.interfaces.Template',
+}
RESERVED_FIELDS = (
'project',
@@ -74,13 +88,33 @@ class APITimestampExpired(APIError):
http_status = 410
+def client_metadata(client=None, exception=None, tags=None, extra=None):
+ if not extra:
+ extra = {}
+ if not tags:
+ tags = {}
+
+ extra['client'] = client
+ extra['request'] = env.request
+ extra['tags'] = tags
+
+ tags['client'] = client
+ if exception:
+ tags['exc_type'] = type(exception).__name__
+
+ result = {'extra': extra}
+ if exception:
+ result['exc_info'] = True
+ return result
+
+
def extract_auth_vars(request):
if request.META.get('HTTP_X_SENTRY_AUTH', '').startswith('Sentry'):
return parse_auth_header(request.META['HTTP_X_SENTRY_AUTH'])
elif request.META.get('HTTP_AUTHORIZATION', '').startswith('Sentry'):
return parse_auth_header(request.META['HTTP_AUTHORIZATION'])
else:
- return None
+ return request.GET
def project_from_auth_vars(auth_vars):
@@ -108,7 +142,7 @@ def project_from_auth_vars(auth_vars):
except TeamMember.DoesNotExist:
raise APIUnauthorized('Member does not have access to project')
- # We have to refetch this as it may have been catched
+ # We have to refetch this as it may have been caught
pk.user = User.objects.get(id=pk.user_id)
if not pk.user.is_active:
raise APIUnauthorized('Account is not active')
@@ -143,7 +177,7 @@ def project_from_api_key_and_id(api_key, project_id):
except TeamMember.DoesNotExist:
raise APIUnauthorized('Member does not have access to project')
- # We have to refetch this as it may have been catched
+ # We have to refetch this as it may have been caught
pk.user = User.objects.get(id=pk.user_id)
if not pk.user.is_active:
raise APIUnauthorized('Account is not active')
@@ -194,13 +228,13 @@ def project_from_id(request):
def decode_and_decompress_data(encoded_data):
try:
try:
- return base64.b64decode(encoded_data).decode('zlib')
+ return decompress(encoded_data)
except zlib.error:
return base64.b64decode(encoded_data)
except Exception, e:
# This error should be caught as it suggests that there's a
# bug somewhere in the client's code.
- logger.exception('Bad data received')
+ logger.info(e, **client_metadata(exception=e))
raise APIForbidden('Bad data decoding request (%s, %s)' % (
e.__class__.__name__, e))
@@ -211,19 +245,21 @@ def safely_load_json_string(json_string):