--- myst: html_meta: "description": "" "property=og:description": "" "property=og:title": "" "keywords": "" --- # Debugging Plone on WSGI When debugging Plone behind a WSGI server, there are a couple of things to remember. The `wsgitraining.site` package contained in our buildout comes with a debugging view that sets a breakpoint: ```{code-block} python :emphasize-lines: 5 class DebuggingView(BrowserView): def __call__(self): self.msg = _(u'A small message') import pdb; pdb.set_trace() return self.index() ``` It is available at [http://localhost:8080/Plone/debugging-view](http://localhost:8080/Plone/debugging-view). It also provides a very basic view that raises an `AttributeError` available at [http://localhost:8080/Plone/attribute-error-view](http://localhost:8080/Plone/attribute-error-view). ## Debugging with waitress Debugging with waitresss doesn't feel different from what you know from ZServer. Limiting to one worker thread might make debugging easer for you. The widely used `pdbpp`, `Products.PdbDebugMode`, `plone.app.debugtoolbar` and `Products.enablesettrace` add-ons also work as expected. ## werkzeug debugging ````{sidebar} Build now Run buildout for this section: ```shell buildout -c werkzeugdebugger.cfg ``` ```` The werkzeug WSGI server provides an additional [debugging WSGI middleware](https://werkzeug.palletsprojects.com/en/latest/debug/) that renders tracebacks and also provides an interactive debug console. To quote from this [blog post](https://labs.detectify.com/2015/10/02/how-patreon-got-hacked-publicly-exposed-werkzeug-debugger/) this is basically remote remote code execution by design, so never use the werkzeug debugger in production (although the original vulnerability has been mitigated by introducing a debugger PIN). To get this working for Plone we first have to disable our standard exception views. We can do this by means of an additional option in the `[instance]` buildout part: ```{code-block} ini :emphasize-lines: 9 ... [instance] recipe = plone.recipe.zope2instance user = admin:admin zeo-client = on zeo-address = 8100 shared-blob = on blob-storage = ${buildout:directory}/var/blobstorage debug-exceptions = on eggs = Plone Pillow wsgitraining.site dataflake.wsgi.werkzeug wsgi-ini-template = ${buildout:directory}/templates/werkzeugdebugger.ini.in ``` We also have to specify the correct entry point in the `ini` template to use the werkzeug debugger: ```{code-block} ini :emphasize-lines: 2 [server:main] use = egg:dataflake.wsgi.werkzeug#debugger hostname = 127.0.0.1 port = 8080 ... ``` Start the instance with `bin/instance fg`. Use the `attribute-error-view` from the training add-on to see a traceback rendered by werkzeug debugger. If you move the mouse to the right of a stack frame, you will see a symbol of a terminal. Click on it. You will be prompted for a PIN once. Enter the PIN provided on the console where you started the instance. You will see an interactive console in the browser where you can enter arbitrary Python code. ## Debugging uWSGI When trying to open the debugging view in uWSGI, you will see an error message in both console and the browser instead of the pdb prompt. ```shell > /home/thomas/devel/plone/wsgitraining_buildout/src/wsgitraining.site/src/wsgitraining/site/views/debugging_view.py(18)__call__() -> return self.index() (Pdb) ERROR:Zope.SiteErrorLog:1570455986.90912460.23307293753294422 http://localhost:8080/Plone/debugging-view Traceback (innermost last): Module ZPublisher.WSGIPublisher, line 155, in transaction_pubevents Module ZPublisher.WSGIPublisher, line 337, in publish_module Module ZPublisher.WSGIPublisher, line 255, in publish Module ZPublisher.mapply, line 85, in mapply Module ZPublisher.WSGIPublisher, line 61, in call_object Module wsgitraining.site.views.debugging_view, line 18, in __call__ Module wsgitraining.site.views.debugging_view, line 18, in __call__ Module bdb, line 88, in trace_dispatch Module bdb, line 113, in dispatch_line bdb.BdbQuit ``` To make uWSGI stop at the `pdb.set_trace()` you need to start it with the `honour-stdin` flag set to `true`. [This flag](https://uwsgi-docs.readthedocs.io/en/latest/Options.html#honour-stdin) will prevent uWSGI from redirecting `stdin` to `/dev/null`, which is the default behaviour. You can do so by modifying the inline template in the `[uwsgiini]` part and rerun buildout. ```{code-block} ini :emphasize-lines: 11,14 ... [uwsgiini] recipe = collective.recipe.template input = inline: [uwsgi] http-socket = 0.0.0.0:8080 socket = 127.0.0.1:8081 chdir = ${buildout:directory}/bin module = wsgi:application master = false honour-stdin = true enable-threads = true processes = 1 threads = 1 output = ${buildout:directory}/etc/uwsgi.ini ... ``` After running buildout and starting your instance with `bin/uwsgi-instance` you will see an interactive console and uWSGI will not serve any requests at first (the browser will hang forever instead of showing a page). ```console *** Operational MODE: single process *** Class Products.CMFFormController.ControllerPythonScript.ControllerPythonScript has a security declaration for nonexistent method 'ZPythonScriptHTML_changePrefs' Class Products.CMFFormController.ControllerValidator.ControllerValidator has a security declaration for nonexistent method 'ZPythonScriptHTML_changePrefs' /home/thomas/.buildout/eggs/cp37m/pyScss-1.3.5-py3.7-linux-x86_64.egg/scss/selector.py:54: FutureWarning: Possible nested set at position 329 ''', re.VERBOSE | re.MULTILINE) WARNING:plone.behavior:Specifying 'for' in behavior 'Tiles' if no 'factory' is given has no effect and is superfluous. >>> ``` Press `Ctrl+D` to continue the instance startup: ```console >>> now exiting InteractiveConsole... WSGI app 0 (mountpoint='') ready in 52 seconds on interpreter 0x55fe3f766d30 pid: 7018 (default app) *** uWSGI is running in multiple interpreter mode *** spawned uWSGI worker 1 (and the only) (pid: 7018, cores: 1) ... ``` Now if you open the `debugging-view` again you will see the `pdb` prompt. All looks fine now, however you will not be able to terminate the instance with `Ctrl+C`. Instead you can press `Ctrl+Z` to send the instance to the background and then kill it with `kill %1` (or whatever job number you're seeing on the console). This behaviour is the reason why we don't put `honour-stdin` in the `.ini` template by default.