├── .gitignore ├── .travis.yml ├── Procfile ├── README.rst ├── app.json ├── requirements.in ├── requirements.txt ├── runtime.txt ├── sentry.conf.py └── uwsgi.ini /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | include/ 3 | lib/ 4 | local 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: "2.7" 3 | sudo: false 4 | install: 5 | - pip install --upgrade pip 6 | - pip install -r requirements.txt 7 | script: python -m py_compile sentry.conf.py 8 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: uwsgi --ini=uwsgi.ini --http=0.0.0.0:$PORT 2 | worker: sentry --config=sentry.conf.py run worker --loglevel=INFO 3 | beat: sentry --config=sentry.conf.py run cron --loglevel=INFO 4 | release: sentry --config=sentry.conf.py upgrade --noinput 5 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Sentry on Heroku 2 | ================ 3 | 4 | Sentry_ is a realtime event logging and aggregation platform. At its core 5 | it specializes in monitoring errors and extracting all the information 6 | needed to do a proper post-mortem without any of the hassle of the 7 | standard user feedback loop. 8 | 9 | .. _Sentry: https://github.com/getsentry/sentry 10 | 11 | 12 | Quick setup 13 | ----------- 14 | 15 | Click the button below to automatically set up the Sentry in an app running on 16 | your Heroku account. 17 | 18 | .. image:: https://www.herokucdn.com/deploy/button.png 19 | :target: https://heroku.com/deploy 20 | :alt: Deploy 21 | 22 | Finally, you need to setup your first user:: 23 | 24 | heroku run "sentry --config=sentry.conf.py createuser" --app YOURAPPNAME 25 | 26 | 27 | Manual setup 28 | ------------ 29 | 30 | Follow the steps below to get Sentry up and running on Heroku: 31 | 32 | 1. Create a new Heroku application. Replace "APP_NAME" with your 33 | application's name:: 34 | 35 | heroku apps:create APP_NAME 36 | 37 | 2. Add PostgresSQL to the application:: 38 | 39 | heroku addons:create heroku-postgresql:hobby-dev 40 | 41 | 3. Add Redis to the application:: 42 | 43 | heroku addons:create heroku-redis:premium-0 44 | 45 | 4. Set Django's secret key for cryptographic signing and Sentry's shared secret 46 | for global administration privileges:: 47 | 48 | heroku config:set SECRET_KEY=$(python -c "import base64, os; print(base64.b64encode(os.urandom(40)).decode())") 49 | 50 | 5. Set the absolute URL to the Sentry root directory. The URL should not include 51 | a trailing slash. Replace the URL below with your application's URL:: 52 | 53 | heroku config:set SENTRY_URL_PREFIX=https://sentry-example.herokuapp.com 54 | 55 | 6. Deploy Sentry to Heroku:: 56 | 57 | git push heroku master 58 | 59 | 7. Sentry's database migrations are automatically run as part of the Heroku `release phase`_ :: 60 | 61 | heroku run "sentry --config=sentry.conf.py upgrade --noinput" 62 | 63 | 8. Create a user account for yourself:: 64 | 65 | heroku run "sentry --config=sentry.conf.py createuser" 66 | 67 | That's it! 68 | 69 | .. _release phase: https://devcenter.heroku.com/articles/release-phase 70 | 71 | 72 | 73 | Email notifications 74 | ------------------- 75 | 76 | Follow the steps below, if you want to enable Sentry's email notifications: 77 | 78 | 1. Add SendGrid add-on to your Heroku application:: 79 | 80 | heroku addons:create sendgrid 81 | 82 | 2. Set the reply-to email address for outgoing mail:: 83 | 84 | heroku config:set SERVER_EMAIL=sentry@example.com 85 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sentry", 3 | "description": "Sentry is a realtime event logging and aggregation platform", 4 | "repository": "https://github.com/fastmonkeys/sentry-on-heroku", 5 | "website": "http://getsentry.com", 6 | "keywords": [ 7 | "logging", 8 | "error" 9 | ], 10 | "addons": [ 11 | "heroku-postgresql:hobby-dev", 12 | "heroku-redis:premium-0", 13 | "sendgrid:starter" 14 | ], 15 | "env": { 16 | "SECRET_KEY": { 17 | "description": "A secret key for a particular Django installation. This is used to provide cryptographic signing, and should be set to a unique, unpredictable value.", 18 | "generator": "secret" 19 | }, 20 | "SENTRY_URL_PREFIX": { 21 | "description": "Your applications URL, no trailing slash", 22 | "value": "https://YOURAPPNAME.herokuapp.com" 23 | }, 24 | "SERVER_EMAIL": { 25 | "description": "Reply to address for outgoing email", 26 | "value": "root@localhost" 27 | }, 28 | "SENTRY_ADMIN_EMAIL": { 29 | "description": "The administrative email for this installation. Note: This will be reported back to getsentry.com as the point of contact. See the beacon documentation for more information.", 30 | "value": "", 31 | "required": false 32 | }, 33 | "AWS_ACCESS_KEY_ID": { 34 | "description": "Your Amazon Web Services access key", 35 | "value": "", 36 | "required": false 37 | }, 38 | "AWS_SECRET_ACCESS_KEY": { 39 | "description": "Your Amazon Web Services secret access key", 40 | "value": "", 41 | "required": false 42 | }, 43 | "AWS_STORAGE_BUCKET_NAME": { 44 | "description": "Your Amazon Web Services storage bucket name", 45 | "value": "", 46 | "required": false 47 | } 48 | }, 49 | "scripts": { 50 | "postdeploy": "sentry --config=sentry.conf.py upgrade --noinput" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /requirements.in: -------------------------------------------------------------------------------- 1 | boto 2 | dj-database-url 3 | django-bcrypt 4 | django-secure 5 | django-storages 6 | ndg-httpsclient # required by requests 7 | psycopg2 8 | sentry 9 | sentry-auth-google 10 | sentry-github 11 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile 3 | # To update, run: 4 | # 5 | # pip-compile --output-file requirements.txt requirements.in 6 | # 7 | amqp==1.4.9 # via kombu 8 | anyjson==0.3.3 # via kombu 9 | billiard==3.3.0.23 # via celery 10 | boto==2.43.0 11 | celery==3.1.18 # via sentry 12 | cffi==1.8.3 # via cryptography, symsynd 13 | click==6.6 # via sentry 14 | contextlib2==0.5.4 # via raven 15 | cryptography==1.5.2 # via pyopenssl, python-u2flib-server 16 | cssselect==1.0.0 # via toronado 17 | cssutils==0.9.10 # via sentry, toronado 18 | dj-database-url==0.4.1 19 | django-bcrypt==0.9.2 20 | django-bitfield==1.7.1 # via sentry 21 | django-crispy-forms==1.4.0 # via sentry 22 | django-debug-toolbar==1.3.2 # via sentry 23 | django-jsonfield==0.9.13 # via sentry 24 | django-picklefield==0.3.2 # via sentry 25 | django-secure==1.0.1 26 | django-storages==1.5.1 27 | django-sudo==2.1.0 # via sentry 28 | django-templatetag-sugar==1.0 # via sentry 29 | Django==1.6.11 # via django-bitfield, django-debug-toolbar, django-secure, sentry 30 | djangorestframework==2.3.14 # via sentry 31 | email-reply-parser==0.2.0 # via sentry 32 | enum34==1.1.6 # via cryptography, python-u2flib-server, sentry 33 | exam==0.10.6 # via sentry 34 | hiredis==0.1.6 # via sentry 35 | honcho==0.7.1 # via sentry 36 | httplib2==0.9.2 # via oauth2 37 | idna==2.1 # via cryptography 38 | ipaddress==1.0.17 # via cryptography, sentry 39 | kombu==3.0.35 # via celery, sentry 40 | lxml==3.6.4 # via sentry, toronado 41 | mock==1.0.1 # via exam, sentry 42 | ndg-httpsclient==0.4.2 43 | oauth2==1.9.0.post1 # via sentry 44 | percy==0.4.1 # via sentry 45 | petname==1.7 # via sentry 46 | Pillow==3.2.0 # via sentry 47 | progressbar2==3.10.1 # via sentry 48 | psycopg2==2.6.2 49 | py-bcrypt==0.4 # via django-bcrypt 50 | py==1.4.31 # via pytest 51 | pyasn1==0.1.9 # via cryptography, requests 52 | pycparser==2.16 # via cffi 53 | PyOpenSSL==16.2.0 # via ndg-httpsclient, requests 54 | pytest-django==2.9.1 # via sentry 55 | pytest-html==1.9.0 # via sentry 56 | pytest==2.6.4 # via pytest-django, pytest-html, sentry 57 | python-dateutil==2.5.3 # via sentry 58 | python-memcached==1.58 # via sentry 59 | python-openid==2.2.5 # via sentry 60 | python-u2flib-server==4.0.1 # via sentry 61 | python-utils==2.0.0 # via progressbar2 62 | pytz==2016.7 # via celery 63 | PyYAML==3.11 # via sentry 64 | qrcode==5.3 # via sentry 65 | raven==5.31.0 # via sentry 66 | rb==1.5 # via sentry 67 | redis==2.10.5 # via rb, sentry 68 | requests[security]==2.11.1 # via percy, sentry 69 | selenium==2.53.6 # via sentry 70 | sentry-auth-google==0.1.0 71 | sentry-github==0.1.2 72 | sentry==8.9.0 73 | setproctitle==1.1.10 # via sentry 74 | simplejson==3.8.2 # via sentry 75 | six==1.10.0 # via cryptography, django-bitfield, pyopenssl, python-dateutil, python-memcached, python-utils, qrcode, sentry, structlog 76 | South==1.0.1 # via sentry 77 | sqlparse==0.2.2 # via django-debug-toolbar 78 | statsd==3.1 # via sentry 79 | structlog==16.1.0 # via sentry 80 | symsynd==1.2.0 # via sentry 81 | toronado==0.0.11 # via sentry 82 | ua-parser==0.7.1 # via sentry 83 | urllib3==1.16 # via sentry 84 | uwsgi==2.0.14 # via sentry 85 | 86 | # The following packages are commented out because they are 87 | # considered to be unsafe in a requirements file: 88 | # setuptools # via cryptography 89 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-2.7.11 2 | -------------------------------------------------------------------------------- /sentry.conf.py: -------------------------------------------------------------------------------- 1 | # This file is just Python, with a touch of Django which means 2 | # you can inherit and tweak settings to your hearts content. 3 | import os.path 4 | 5 | import dj_database_url 6 | from sentry.conf.server import * 7 | 8 | CONF_ROOT = os.path.dirname(__file__) 9 | 10 | DATABASES = {'default': dj_database_url.config()} 11 | 12 | # You should not change this setting after your database has been created 13 | # unless you have altered all schemas first 14 | SENTRY_USE_BIG_INTS = True 15 | 16 | # If you're expecting any kind of real traffic on Sentry, we highly recommend 17 | # configuring the CACHES and Redis settings 18 | 19 | ########### 20 | # General # 21 | ########### 22 | 23 | # The administrative email for this installation. 24 | # Note: This will be reported back to getsentry.com as the point of contact. See 25 | # the beacon documentation for more information. This **must** be a string. 26 | 27 | SENTRY_OPTIONS['system.admin-email'] = os.environ.get('SENTRY_ADMIN_EMAIL', '') 28 | 29 | # Instruct Sentry that this install intends to be run by a single organization 30 | # and thus various UI optimizations should be enabled. 31 | SENTRY_SINGLE_ORGANIZATION = True 32 | 33 | # Should Sentry allow users to create new accounts? 34 | SENTRY_FEATURES['auth:register'] = False 35 | 36 | ######### 37 | # Redis # 38 | ######### 39 | 40 | # Generic Redis configuration used as defaults for various things including: 41 | # Buffers, Quotas, TSDB 42 | 43 | redis_url = urlparse(os.environ['REDIS_URL']) 44 | SENTRY_OPTIONS['redis.clusters'] = { 45 | 'default': { 46 | 'hosts': { 47 | 0: { 48 | 'host': redis_url.hostname, 49 | 'port': redis_url.port, 50 | 'password': redis_url.password, 51 | 'db': 0, 52 | } 53 | } 54 | } 55 | } 56 | 57 | ######### 58 | # Cache # 59 | ######### 60 | 61 | # If you wish to use memcached, install the dependencies and adjust the config 62 | # as shown: 63 | # 64 | # pip install python-memcached 65 | # 66 | # CACHES = { 67 | # 'default': { 68 | # 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 69 | # 'LOCATION': ['127.0.0.1:11211'], 70 | # } 71 | # } 72 | # 73 | # SENTRY_CACHE = 'sentry.cache.django.DjangoCache' 74 | 75 | SENTRY_CACHE = 'sentry.cache.redis.RedisCache' 76 | 77 | ######### 78 | # Queue # 79 | ######### 80 | 81 | # See https://docs.getsentry.com/on-premise/server/queue/ for more 82 | # information on configuring your queue broker and workers. Sentry relies 83 | # on a Python framework called Celery to manage queues. 84 | 85 | CELERY_ALWAYS_EAGER = False 86 | BROKER_URL = os.environ['REDIS_URL'] + '/0' 87 | 88 | ############### 89 | # Rate Limits # 90 | ############### 91 | 92 | # Rate limits apply to notification handlers and are enforced per-project 93 | # automatically. 94 | 95 | SENTRY_RATELIMITER = 'sentry.ratelimits.redis.RedisRateLimiter' 96 | 97 | ################## 98 | # Update Buffers # 99 | ################## 100 | 101 | # Buffers (combined with queueing) act as an intermediate layer between the 102 | # database and the storage API. They will greatly improve efficiency on large 103 | # numbers of the same events being sent to the API in a short amount of time. 104 | # (read: if you send any kind of real data to Sentry, you should enable buffers) 105 | 106 | SENTRY_BUFFER = 'sentry.buffer.redis.RedisBuffer' 107 | 108 | ########## 109 | # Quotas # 110 | ########## 111 | 112 | # Quotas allow you to rate limit individual projects or the Sentry install as 113 | # a whole. 114 | 115 | SENTRY_QUOTAS = 'sentry.quotas.redis.RedisQuota' 116 | 117 | ######## 118 | # TSDB # 119 | ######## 120 | 121 | # The TSDB is used for building charts as well as making things like per-rate 122 | # alerts possible. 123 | 124 | SENTRY_TSDB = 'sentry.tsdb.redis.RedisTSDB' 125 | 126 | ################ 127 | # File storage # 128 | ################ 129 | 130 | # Any Django storage backend is compatible with Sentry. For more solutions see 131 | # the django-storages package: https://django-storages.readthedocs.org/en/latest/ 132 | 133 | SENTRY_FILESTORE = 'storages.backends.s3boto.S3BotoStorage' 134 | SENTRY_FILESTORE_OPTIONS = {} 135 | 136 | AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID') 137 | AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY') 138 | AWS_STORAGE_BUCKET_NAME = os.environ.get('AWS_STORAGE_BUCKET_NAME') 139 | AWS_DEFAULT_ACL = 'private' 140 | 141 | ############## 142 | # Web Server # 143 | ############## 144 | 145 | # You MUST configure the absolute URI root for Sentry: 146 | SENTRY_OPTIONS['system.url-prefix'] = os.environ['SENTRY_URL_PREFIX'] 147 | 148 | SENTRY_WEB_HOST = '0.0.0.0' 149 | SENTRY_WEB_PORT = int(os.environ['PORT']) 150 | SENTRY_WEB_OPTIONS = { 151 | 'secure_scheme_headers': {'X-FORWARDED-PROTO': 'https'}, 152 | 'worker_class': 'gevent', 153 | 'workers': 3, 154 | } 155 | 156 | ############### 157 | # Mail Server # 158 | ############### 159 | 160 | # For more information check Django's documentation: 161 | # https://docs.djangoproject.com/en/1.3/topics/email/?from=olddocs#e-mail-backends 162 | 163 | SENTRY_OPTIONS['mail.backend'] = 'django.core.mail.backends.smtp.EmailBackend' 164 | 165 | if 'SENDGRID_USERNAME' in os.environ: 166 | SENTRY_OPTIONS['mail.host'] = 'smtp.sendgrid.net' 167 | SENTRY_OPTIONS['mail.username'] = os.environ['SENDGRID_USERNAME'] 168 | SENTRY_OPTIONS['mail.password'] = os.environ['SENDGRID_PASSWORD'] 169 | SENTRY_OPTIONS['mail.port'] = 587 170 | SENTRY_OPTIONS['mail.use-tls'] = True 171 | 172 | # The email address to send on behalf of 173 | SENTRY_OPTIONS['mail.from'] = os.environ.get('SERVER_EMAIL', 'root@localhost') 174 | 175 | # If you're using mailgun for inbound mail, set your API key and configure a 176 | # route to forward to /api/hooks/mailgun/inbound/ 177 | SENTRY_OPTIONS['mail.mailgun-api-key'] = os.environ.get('MAILGUN_API_KEY', '') 178 | 179 | ############ 180 | # Security # 181 | ############ 182 | 183 | INSTALLED_APPS += ('djangosecure',) 184 | MIDDLEWARE_CLASSES += ('djangosecure.middleware.SecurityMiddleware',) 185 | 186 | # If you're using a reverse proxy, you should enable the X-Forwarded-Proto 187 | # header and uncomment the following settings 188 | SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') 189 | 190 | # Whether to use HTTPOnly flag on the session cookie. If this is set to `True`, 191 | # client-side JavaScript will not to be able to access the session cookie. 192 | SESSION_COOKIE_HTTPONLY = True 193 | 194 | # Whether to use a secure cookie for the session cookie. If this is set to 195 | # `True`, the cookie will be marked as "secure," which means browsers may 196 | # ensure that the cookie is only sent under an HTTPS connection. 197 | SESSION_COOKIE_SECURE = True 198 | 199 | # If set to `True`, causes `SecurityMiddleware` to set the 200 | # `X-Content-Type-Options: nosniff` header on all responses that do not already 201 | # have that header. 202 | SECURE_CONTENT_TYPE_NOSNIFF = True 203 | 204 | # If set to `True`, causes `SecurityMiddleware` to set the 205 | # `X-XSS-Protection: 1; mode=block` header on all responses that do not already 206 | # have that header. 207 | SECURE_BROWSER_XSS_FILTER = True 208 | 209 | # If set to `True`, causes `SecurityMiddleware` to set the `X-Frame-Options: 210 | # DENY` header on all responses that do not already have that header 211 | SECURE_FRAME_DENY = True 212 | 213 | # If set to a non-zero integer value, causes `SecurityMiddleware` to set the 214 | # HTTP Strict Transport Security header on all responses that do not already 215 | # have that header. 216 | SECURE_HSTS_SECONDS = 31536000 217 | 218 | # If `True`, causes `SecurityMiddleware` to add the ``includeSubDomains`` tag 219 | # to the HTTP Strict Transport Security header. 220 | # 221 | # Has no effect unless ``SECURE_HSTS_SECONDS`` is set to a non-zero value. 222 | SECURE_HSTS_INCLUDE_SUBDOMAINS = True 223 | 224 | # If set to True, causes `SecurityMiddleware` to redirect all non-HTTPS 225 | # requests to HTTPS 226 | SECURE_SSL_REDIRECT = True 227 | 228 | ########## 229 | # Bcrypt # 230 | ########## 231 | 232 | INSTALLED_APPS += ('django_bcrypt',) 233 | 234 | # Enables bcrypt password migration on a ``check_password()`` call. 235 | # 236 | # The hash is also migrated when ``BCRYPT_ROUNDS`` changes. 237 | BCRYPT_MIGRATE = True 238 | 239 | ############### 240 | # Social Auth # 241 | ############### 242 | 243 | TWITTER_CONSUMER_KEY = os.environ.get('TWITTER_CONSUMER_KEY') 244 | TWITTER_CONSUMER_SECRET = os.environ.get('TWITTER_CONSUMER_SECRET') 245 | 246 | FACEBOOK_APP_ID = os.environ.get('FACEBOOK_APP_ID') 247 | FACEBOOK_API_SECRET = os.environ.get('FACEBOOK_API_SECRET') 248 | 249 | GOOGLE_CLIENT_ID = os.environ.get('GOOGLE_CLIENT_ID') 250 | GOOGLE_CLIENT_SECRET = os.environ.get('GOOGLE_CLIENT_SECRET') 251 | 252 | GITHUB_APP_ID = os.environ.get('GITHUB_APP_ID') 253 | GITHUB_API_SECRET = os.environ.get('GITHUB_API_SECRET') 254 | GITHUB_ORGANIZATION = os.environ.get('GITHUB_ORGANIZATION') 255 | GITHUB_EXTENDED_PERMISSIONS = ['repo'] 256 | 257 | BITBUCKET_CONSUMER_KEY = os.environ.get('BITBUCKET_CONSUMER_KEY') 258 | BITBUCKET_CONSUMER_SECRET = os.environ.get('BITBUCKET_CONSUMER_SECRET') 259 | 260 | ######## 261 | # etc. # 262 | ######## 263 | 264 | # If this file ever becomes compromised, it's important to regenerate your SECRET_KEY 265 | # Changing this value will result in all current sessions being invalidated 266 | SENTRY_OPTIONS['system.secret-key'] = os.environ['SECRET_KEY'] 267 | -------------------------------------------------------------------------------- /uwsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | env = SENTRY_CONF=sentry.conf.py 3 | module = sentry.wsgi 4 | 5 | ; spawn the master and 4 processes with 8 threads each 6 | master = true 7 | processes = 4 8 | threads = 8 9 | 10 | ; allow longer headers for raven.js if applicable 11 | ; default: 4096 12 | buffer-size = 32768 13 | 14 | ; allow large file uploads 15 | limit-post = 20971520 16 | 17 | ; various other explicit defaults 18 | post-buffering = 65536 19 | thunder-lock = true 20 | disable-logging = true 21 | enable-threads = true 22 | single-interpreter = true 23 | lazy-apps = true 24 | log-x-forwarded-for = true 25 | --------------------------------------------------------------------------------