Debugging

Logging

Set GWS_LOG_LEVEL=DEBUG in the container environment, or server.log.level "DEBUG" in the configuration. At the debug level, every log line shows its source file and line. CLI commands log at the debug level with -v.

Log with log, available as gws.log:

gws.log.debug(f'loading {path=}')
gws.log.exception()

gws.log.exception() logs the current exception with its chain of causes.

debug, available as gws.debug, has helpers for quick inspection. They log at the debug level:

FunctionPurpose
gws.debug.p(obj, d=3)log the structure of an object, up to the depth d
gws.debug.p(stack=True)log the call stack
gws.debug.time_start(label), gws.debug.time_end()log the time between the two calls

Developer options

The developer section of the configuration turns on debugging features. Write keys with dots in quotes:

developer {
    "request.log_all" true
    "db.engine_echo" true
}
OptionEffect
request.log_allwrite every request to /gws-var/debug
db.engine_echolog all SQL statements
server.auto_reloadrestart the uWSGI workers when a Python module changes
template.always_reloadreload templates on every use
template.raise_errorsraise template errors instead of logging them
web.reload_bundlesreload client bundles on every request

The server logs a warning on start if developer options are set.

Object inspector

The admin action provides an HTML page to browse the configured object tree of the running server. Add the action to the configuration:

actions+ { type "admin" }

Log in as a user with the admin role and open /_/adminInspector. The page shows the root and lets you follow attributes, dict keys and list items of every object. The path parameter addresses an object directly, starting with a node uid, for example /_/adminInspector?path=my_layer/provider. The search parameter lists objects with a property containing a text, or a specific property with prop=text.

The same action provides a viewer for cached tiles at /_/adminMapCache.

Configuration dumps

When the server reads a configuration file, it writes intermediate results to /gws-var/config: the rendered cx template (.src.slon), the parsed file (.src.json) and the validated configuration (.parsed.json). Use them to check how templates and includes were resolved.

Scripts

Code under uWSGI is hard to step through. Reproduce a problem in a script instead: configure an application, then send requests to it without the web server.

import werkzeug.test

import gws
import gws.config
import gws.base.web.wsgi_app

gws.u.ensure_system_dirs()

cr = gws.config.configure(config_path='/data/config.cx')
gws.config.log_report(cr)
root = gws.activate_root(cr.root)

client = werkzeug.test.Client(gws.base.web.wsgi_app.make_application(root))
res = client.post('/_/projectInfo', json={'projectUid': 'hello'})
print(res.status, res.get_json())

Run it in the container as the server user:

docker exec gws-container gws -p /data/debug.py

root gives access to the whole tree, for example root.get('my_layer') returns the node with the uid my_layer. The functions for configuring and loading are in loader.

Debugger

To step through a script with VS Code, install debugpy in the container, publish a port and start the script under the debugger:

docker exec gws-container pip install debugpy
docker exec -it --user 1000:1000 --env PYTHONPATH=/gws-app gws-container python3 -m debugpy --listen 0.0.0.0:5678 --wait-for-client /data/debug.py

Attach from VS Code with a launch.json configuration that maps the container paths to your working copy:

{
    "name": "Attach to GWS",
    "type": "debugpy",
    "request": "attach",
    "connect": {"host": "localhost", "port": 5678},
    "pathMappings": [
        {"localRoot": "/path/to/gbd-websuite/app", "remoteRoot": "/gws-app"}
    ]
}

The port 5678 must be published in the compose file. Use the GWS_UID and GWS_GID of the server for --user.