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] + ...