diff --git a/README.rst b/README.rst
index 76ca7c17..b1141f1c 100644
--- a/README.rst
+++ b/README.rst
@@ -311,6 +311,11 @@ access-log-custom
Like `event-log-custom`, a custom section for the access logger, to be able
to use another event logger than `logfile`. Used for ZServer only, not WSGI.
+sentry_dsn
+ Provide a Sentry DSN here to enable basic Sentry logging documented
+ in ``_.
+ Available for WSGI only.
+
Load non-setuptools compatible Python libraries
-----------------------------------------------
diff --git a/setup.py b/setup.py
index fd0ceee7..0a675774 100644
--- a/setup.py
+++ b/setup.py
@@ -50,6 +50,9 @@
'test': [
'zope.testrunner',
],
+ 'sentry': [
+ 'sentry-sdk',
+ ]
},
zip_safe=False,
entry_points={
@@ -60,5 +63,8 @@
'paste.server_factory': [
'main=plone.recipe.zope2instance.ctl:server_factory',
],
+ 'paste.filter_factory': [
+ 'sentry=plone.recipe.zope2instance.sentry:sdk_init',
+ ],
},
)
diff --git a/src/plone/recipe/zope2instance/recipe.py b/src/plone/recipe/zope2instance/recipe.py
index 58d87b93..95489324 100644
--- a/src/plone/recipe/zope2instance/recipe.py
+++ b/src/plone/recipe/zope2instance/recipe.py
@@ -743,12 +743,21 @@ def build_wsgi_ini(self):
'access-log-level',
options.get('z2-log-level', 'INFO'))
+ pipeline = []
if accesslog_name.lower() == 'disable':
- pipeline = '\n '.join(['egg:Zope#httpexceptions', 'zope'])
+ pipeline = [
+ 'egg:Zope#httpexceptions']
event_handlers = ''
else:
- pipeline = '\n '.join(
- ['translogger', 'egg:Zope#httpexceptions', 'zope'])
+ pipeline = [
+ 'translogger', 'egg:Zope#httpexceptions']
+
+ sentry_dsn = options.get('sentry_dsn', '')
+ if sentry_dsn:
+ pipeline.append('sentry')
+
+ pipeline.append('zope')
+ pipeline = '\n '.join(pipeline)
options = {
'location': options['location'],
'http_address': listen,
@@ -761,6 +770,7 @@ def build_wsgi_ini(self):
'pipeline': pipeline,
'eventlog_level': eventlog_level,
'accesslog_level': accesslog_level,
+ 'sentry_dsn': sentry_dsn,
}
wsgi_ini = wsgi_ini_template % options
with open(wsgi_ini_path, 'w') as f:
@@ -1302,6 +1312,10 @@ def render_file_storage(self, file_storage, blob_storage,
use = egg:Paste#translogger
setup_console_handler = False
+[filter:sentry]
+use = egg:plone.recipe.zope2instance#sentry
+dsn = %(sentry_dsn)s
+
[pipeline:main]
pipeline =
%(pipeline)s
diff --git a/src/plone/recipe/zope2instance/sentry.py b/src/plone/recipe/zope2instance/sentry.py
new file mode 100644
index 00000000..cf82d081
--- /dev/null
+++ b/src/plone/recipe/zope2instance/sentry.py
@@ -0,0 +1,8 @@
+import sentry_sdk
+
+
+def sdk_init(global_conf, dsn):
+ def filter(app):
+ sentry_sdk.init(dsn=dsn)
+ return app
+ return filter
diff --git a/src/plone/recipe/zope2instance/tests/wsgi.txt b/src/plone/recipe/zope2instance/tests/wsgi.txt
index 86a5ae9a..d11bbc2e 100644
--- a/src/plone/recipe/zope2instance/tests/wsgi.txt
+++ b/src/plone/recipe/zope2instance/tests/wsgi.txt
@@ -90,7 +90,7 @@ The buildout has also created an INI file containing the waitress configuration:
[filter:translogger]
use = egg:Paste#translogger
setup_console_handler = False
-
+ ...
[pipeline:main]
pipeline =
translogger
@@ -372,3 +372,56 @@ The buildout has updated our INI file:
handlers =
qualname = waitress
...
+
+Sentry support
+==============
+
+We want to sent logging events to Sentry.
+Let's create a buildout:
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = instance
+ ... find-links = %(sample_buildout)s/eggs
+ ...
+ ... [instance]
+ ... recipe = plone.recipe.zope2instance
+ ... eggs =
+ ... plone.recipe.zope2instance[sentry]
+ ... user = me:me
+ ... sentry_dsn = https://f00ba4ba2@my.sentry.server/9999
+ ... ''' % options)
+
+Let's run it::
+
+ >>> print(system(join('bin', 'buildout'))),
+ Uninstalling instance.
+ Installing instance.
+ Getting distribution for 'sentry-sdk'.
+ ...
+ Generated script '.../sample-buildout/bin/instance'.
+ ...
+
+The buildout has updated our INI file:
+
+ >>> instance = os.path.join(sample_buildout, 'parts', 'instance')
+ >>> wsgi_ini = open(os.path.join(instance, 'etc', 'wsgi.ini')).read()
+ >>> print(wsgi_ini)
+ [server:main]
+ paste.server_factory = plone.recipe.zope2instance:main
+ use = egg:plone.recipe.zope2instance#main
+ ...
+ [filter:sentry]
+ use = egg:plone.recipe.zope2instance#sentry
+ dsn = https://f00ba4ba2@my.sentry.server/9999
+
+ [pipeline:main]
+ pipeline =
+ translogger
+ egg:Zope#httpexceptions
+ sentry
+ zope
+
+ [loggers]
+ ...