Files
feedBack/docs/plugin-logging.md
T
Miguel_LZPFandBret Mogilefsky 1214a6c0a0 docs: extract plugin contracts into modular docs and slim CLAUDE.md
CLAUDE.md had grown to 545 lines / 50 KB — most of it plugin-author
content that other AI tools (Cursor, Copilot, Codex, Aider) and humans
without AI never reach. Extract the plugin surface into 10 focused
docs and a JSON Schema for plugin.json, then slim CLAUDE.md to a
156-line navigable index.

New docs (~999 lines total, all self-contained):

  docs/PLUGIN_AUTHORING.md            — entry point and quickstart
  docs/plugin-manifest.md             — plugin.json field reference
  docs/plugin-visualization-contracts.md — setRenderer / overlay / note-state
  docs/plugin-audio-mixer.md          — fader registration
  docs/plugin-logging.md              — context["log"] + env vars
  docs/plugin-diagnostics.md          — server_files / callable
  docs/plugin-keyboard-shortcuts.md   — registerShortcut + scopes
  docs/plugin-sibling-imports.md      — load_sibling pattern
  docs/websocket-protocol.md          — /ws/highway message reference
  docs/testing-plugins.md             — pytest fixtures + Playwright

  schema/plugin.schema.json           — Draft 2020-12 schema for
                                        plugin.json; license enum
                                        mirrors CONTRIBUTING's curated
                                        allowlist. Backs CI validation
                                        and the plugin-validate skill.

CLAUDE.md slim (581 lines changed, -485):

  - Removed ~300 lines of plugin-author prose (now in docs/).
  - Kept architecture quick reference, running the app, testing,
    git workflow, versioning, song formats, frontend/backend
    conventions, plugin authoring INDEX (table → docs/), first-hour
    pitfalls, "For AI agents" footer.
  - Anchor stubs preserved next to the new index entries so deep
    links from specs/001-slopsmith-platform/analyze.md still resolve.

Verification:
  python -c "import json,glob,jsonschema; s=json.load(open('schema/plugin.schema.json')); [jsonschema.validate(json.load(open(p)), s) for p in sorted(glob.glob('plugins/*/plugin.json'))]"
  # ok — validates highway_3d, app_tour_library, app_tour_settings
Signed-off-by: Miguel_LZPF <mgcdreamer@gmail.com>
2026-06-18 00:38:50 -07:00

2.9 KiB

Plugin logging

Use context["log"] for all backend plugin output. It is a stdlib logging.Logger namespaced to slopsmith.plugin.<id>, pre-configured with the app-wide level, format (including JSON mode), and correlation IDs. Never use print() — it bypasses correlation context and log rotation.

Backend usage

def setup(app, context):
    log = context["log"]
    log.info("plugin ready")
    log.warning("optional dependency %r not found, feature disabled", dep)
    try:
        risky_init()
    except Exception:
        log.exception("unhandled error during setup")  # auto-captures traceback

CLI entry-point fallback

For helper scripts that also run as __main__, add a stdlib fallback so the logger works without the server pipeline:

if __name__ == "__main__":
    import logging
    logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s")

How it's wired

The plugin loader assigns each plugin a logger before calling setup():

context["log"] = logging.getLogger(f"slopsmith.plugin.{plugin_id}")

That logger inherits from the root slopsmith logger, which is configured by logging_setup.configure_logging() (called from main.py before uvicorn boots). It respects:

  • LOG_LEVELDEBUG | INFO | WARNING | ERROR (default INFO)
  • LOG_FORMATjson | text (default text — coloured console)
  • LOG_FILE — optional path for a persistent log file (e.g. /config/slopsmith.log)
  • Correlation IDs — each HTTP request carries X-Request-ID; structlog injects it into every log line during that request's lifetime. WebSocket sessions get their own ws_conn_id contextvar bound at accept time.

Verifying

Look for your plugin's logger name in the console output:

INFO     slopsmith.plugin.my_plugin: plugin ready

Switch LOG_FORMAT=json to see structured output:

{"timestamp": "...", "level": "info", "logger": "slopsmith.plugin.my_plugin", "event": "plugin ready", "request_id": "..."}

Common mistakes

  • Using print() inside setup() or request handlers. print() goes to stdout, bypasses level filtering, log rotation, JSON formatting, and correlation IDs. Audit your plugin with grep -nR 'print(' plugins/<id>/.
  • Creating a new logger with logging.getLogger("my_plugin"). This creates an unparented logger that doesn't inherit slopsmith's configuration. Always use the one passed via context["log"].
  • Logging at INFO in hot paths. Default LOG_LEVEL is INFO, so log.info(...) inside a per-frame or per-message loop floods output. Use log.debug(...) for hot-path tracing.