Skip to main content
The SDK logs every failure once, with a machine-readable error code and full context, to a local JSON file — no setup required. This page covers where logs go, what a line looks like, and how to customize them. To package logs into something you can send to Bioptimus support, see Support bundles.

What the logging layer gives you

Logging is a small, dependency-light layer with a handful of distinct jobs. Each has its own section below — start with whichever capability you need. Every helper on this page imports from the package root:

Zero setup

Logs rotate daily and are kept for 14 days by default. Change this with configure_logging(backup_count=...).
The first emitted event auto-installs a daily-rotating JSON log file under the system temp directory — nothing to configure.
On first use, if logging falls back to the temp directory, the SDK emits a single log_file_in_temp_dir warning — that location can be wiped on reboot or by a temp-file cleaner. Point logging at a durable path (see Customize logging) to silence it and keep a lasting record.
The log file is not anonymized at rest — it keeps full detail so you can debug locally, and it never contains slide images, tile pixels, or omics values, only pipeline metadata (paths, IDs, error codes, tracebacks). Anonymization of that metadata is applied only when you export a support bundle or query /diagnostics (see Support bundles).

Log destinations

Default log format

Every line is a single JSON object:

Emit your own log entries

Wrap your own pipeline functions to get the same one-record-per-failure behavior with log_failures, or emit a one-off structured event with log_event — both write to the log file that’s already active, no configure_logging call required (see Zero setup). In every example below, run_slide is your function and inference is a client you built earlier (for example inference = Inference(...) or model = Backbone(...)) — swap in your own call:
Each path writes one JSON line to the log file. A failure caught by log_failures looks like:
and the manual log_event call above writes:
log_event also accepts error_code= — which auto-attaches the matching remediation — and exc_info=exc to record a caught exception’s traceback on the line. Bind correlation fields once and every log line emitted inside the block is tagged with them automatically — so all lines for a run, a slide, or a request share a trace_id/slide_id you can filter on. Fields whose value is None are ignored, and the previous context is restored on exit (even on error):
new_trace_id() returns a 26-character, time-sortable ID. bind_context accepts any field name — trace_id, run_id, slide_id, and stage are the ones the SDK’s own pipeline sets and surface in the format table above, but you can bind your own (for example batch_id) the same way.

Read your logs back in Python

Read the log file without parsing JSON yourself. Both readers anonymize by default — pass anonymize=False for full local detail:
Each entry is a plain dict with the fields shown in Default log format. read_recent_logs tails the active file only; read_usage_records spans the active file and all its rotations, so it is the full request history rather than a recent tail.

Customize logging

Call configure_logging to override any default — for a custom path, console output, level, or retention:
With console=True, matching events also print a compact, human-readable line to stderr instead of the JSON payload above — for example the slide_skipped event from the manual log_event call:
Set the rotation period and retention with when and backup_count. Rotated files are suffixed with their UTC date (bioptimus.log.2026-06-24), so each maps cleanly to a support window:
For a policy those two parameters can’t express — size-based rotation, say — pass your own handler through file_handler_factory. The SDK still owns the path, the JSON format, and correlation-context injection:

Can I change the log format?

The on-disk format is structured JSON by design — it is the contract the readers, support bundles, and a server’s /diagnostics all parse — so there is no format parameter. For human-readable output, pass console=True (shown above). To emit SDK logs in your own format to your own destination, attach your own handler. SDK records propagate up Python’s logging hierarchy, so a handler on the bioptimus logger (or the root logger) receives every event:

Redact sensitive fields at rest

By default the log file keeps full detail so you can debug locally, and PII is redacted only when you export — when you build a support bundle or query a server’s /diagnostics. To redact the on-disk file itself, turn on redaction at rest:
With redaction on, every emitted record is scrubbed before it is written:
  • Identifier and path fields (slide_id, patient_id, wsi_id, slide_path, output_dir, …) are replaced by a stable hash — the same value keeps the same pseudonym, so “which slide failed” still correlates across every line.
  • Free text — any path, s3:///file:// URI, or slide file name embedded in a message or traceback under a field the policy doesn’t recognize — is redacted outright instead; the user name in a home-directory path is masked.
The recognized fields and the per-field strategy (hash, redact, or keep verbatim) are configurable — see Support bundles → Customize what gets redacted for the full policy and the anonymize reference.
Redaction is stable pseudonymization for support triage, not encryption. Review what a bundle contains before sharing logs outside your environment.

Handle errors in your code

Every failure the SDK raises is a BioptimusError carrying a machine-readable code, structured context, and an actionable remediation. Catch the base class to handle any SDK failure and read those fields directly:
Existing except handlers keep working. Each typed error subclasses the built-in it replaces, so code that already catches the standard exception is unaffected: BioptimusImportError names the exact extra to install, so a missing optional dependency is self-explanatory:

Error codes

Every BioptimusError carries a stable, hyphen-delimited code such as BIO-WSI-002, grouped by domain: Each code has a fixed remediation string, the same one attached to its error_code field above and surfaced by verify_endpoint’s failure reports. The full list of codes and remediations is in the API reference.