Skip to content

SDK | Structured logging and progress bars#306

Merged
lucaspar merged 19 commits into
masterfrom
lp/uploads-monitoring
Jul 1, 2026
Merged

SDK | Structured logging and progress bars#306
lucaspar merged 19 commits into
masterfrom
lp/uploads-monitoring

Conversation

@lucaspar

@lucaspar lucaspar commented Jun 29, 2026

Copy link
Copy Markdown
Member

Structured logging, test coverage, and progress bars

  • Structured logging — JSONL sink (loguru) with core fields (ts, pid, lvl, cat, msg), LogContext / set_persistent_log_context, boot message (SDK version, OS, Python), category binding, XDG-compliant path
  • Increased test coverage — upload modes (metadata-only, skip), download (File instance, skip contents, error cleanup, cleanup trigger), dataset flows (get, list_captures, list_artifact_files, revoke permissions) with dry-run + gateway variants, success_or_raise edge cases (no status code, JSON decode/HTML fallbacks, 3xx), structured logging lifecycle
  • Progress bars tracking bytes — replacing previous file-based tracking
    image
    image
  • Logged upload and download session summaries — summaries with average speeds are now part of structured logging
  • API/client hardeningdownload_dataset filter branches, upload_capture error handling, upload_multichannel_drf_capture with empty channels, download_file accepts File instances + skip_contents, temp file cleanup on failure, extract_error_details_from_html, pagination fixes
  • Docs — new structured-logging mkdocs page, changelog + dev-notes updated
  • Removed — early version disclaimer from __init__.py

Note

Medium Risk
Touches all transfer and logging paths (Client init, gateway I/O, concurrent uploads); behavior changes include quieter default stderr and new on-disk logs, but core API remains backward compatible aside from optional log_file and progress callbacks.

Overview
Adds structured JSONL logging for the SDK: daily files under XDG state (or log_file / Client(log_file=…)), categories via log.bind(cat=…), persistent and scoped context, and session summaries for uploads/downloads (counts, bytes, avg speed). Client turns this on at construction; default Loguru stderr is removed so sinks are explicit (enable_logging vs structured file).

Upload/download progress moves from file-count bars to byte totals with throttled chunk callbacks through gateway (_ProgressFileReader), sds_files, client, and UploadWorkload (thread-safe bar updates, periodic upload progress logs, credit_unstreamed_file_bytes for skips/metadata-only).

New progress_log_period_secs config, demo-progress-bars script + just target, mkdocs for structured logging, and broad test coverage. Gateway dev-notes/changelog are whitespace-only.

Reviewed by Cursor Bugbot for commit 1fca4e7. Bugbot is set up for automated code reviews on this repo. Configure here.

@lucaspar lucaspar self-assigned this Jun 29, 2026
@lucaspar lucaspar added feature New feature or request sdk SDK component labels Jun 29, 2026
@semanticdiff-com

semanticdiff-com Bot commented Jun 29, 2026

Copy link
Copy Markdown

Review changes with  SemanticDiff

Changed Files
File Status
  sdk/src/spectrumx/api/datasets.py  84% smaller
  sdk/src/spectrumx/api/captures.py  83% smaller
  sdk/src/spectrumx/ops/pagination.py  63% smaller
  sdk/src/spectrumx/api/sds_files.py  47% smaller
  sdk/src/spectrumx/gateway.py  41% smaller
  sdk/src/spectrumx/config.py  41% smaller
  sdk/src/spectrumx/ops/network.py  35% smaller
  sdk/src/spectrumx/api/uploads.py  28% smaller
  sdk/src/spectrumx/client.py  17% smaller
  sdk/src/spectrumx/__init__.py  12% smaller
  sdk/tests/test_uploads_workflow.py  1% smaller
  gateway/config/settings/base.py  0% smaller
  gateway/docs/changelog.md Unsupported file format
  gateway/docs/dev-notes.md Unsupported file format
  gateway/sds_gateway/monitoring/tests/test_celery_schedule.py  0% smaller
  sdk/docs/README.md Unsupported file format
  sdk/docs/mkdocs.yaml  0% smaller
  sdk/docs/mkdocs/advanced/structured-logging.md Unsupported file format
  sdk/justfile Unsupported file format
  sdk/pyproject.toml Unsupported file format
  sdk/scripts/demo_progress_bars.py  0% smaller
  sdk/src/spectrumx/ops/files.py  0% smaller
  sdk/src/spectrumx/utils.py  0% smaller
  sdk/tests/ops/test_datasets.py  0% smaller
  sdk/tests/ops/test_files.py  0% smaller
  sdk/tests/ops/test_network.py  0% smaller
  sdk/tests/test_client.py  0% smaller
  sdk/tests/test_structured_logging.py  0% smaller
  sdk/tests/test_uploads_persistence.py  0% smaller
  sdk/tests/test_utils.py  0% smaller

@lucaspar lucaspar changed the title sdk: structured logging SDK | Structured logging Jun 29, 2026
@lucaspar lucaspar requested a review from klpoland June 29, 2026 18:47
@lucaspar lucaspar marked this pull request as ready for review June 29, 2026 18:47
Comment thread sdk/src/spectrumx/client.py
Comment thread sdk/src/spectrumx/__init__.py
Comment thread sdk/src/spectrumx/utils.py
**kwargs,
)
with LogContext(http_status=response.status_code):
return response

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HTTP context dropped before errors

Medium Severity

GatewayClient._request wraps the call in LogContext(url=…) and nests LogContext(http_status=…) only around return response. Callers invoke success_or_raise after _request returns, so failed HTTP responses log under LogCategory.NETWORK without url or http_status in JSONL.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d459397. Configure here.

Comment thread sdk/src/spectrumx/utils.py
Comment thread sdk/src/spectrumx/client.py
@lucaspar lucaspar changed the title SDK | Structured logging SDK | Structured logging and progress bars Jun 30, 2026
@lucaspar

lucaspar commented Jul 1, 2026

Copy link
Copy Markdown
Member Author

bugbot review

Comment thread sdk/src/spectrumx/api/uploads.py Outdated
Comment thread sdk/src/spectrumx/api/uploads.py Outdated
cursoragent and others added 2 commits July 1, 2026 00:10
Byte-level progress bars and download avg_speed_bps only counted streamed
chunks, but totals included files whose contents were skipped or only
metadata was transferred. Credit remaining file bytes on successful
completion so progress bars and byte counters reach expected totals.

Co-authored-by: Lucas Parzianello <lucaspar@users.noreply.github.com>
Multiple asyncio.to_thread workers call _throttled_update on the same
shared progress bar. tqdm.update() increments its internal counter
without holding its display lock, so concurrent calls can lose bytes
or corrupt the display.

Add a threading.Lock guarding all mutations of _prog_uploaded_bytes,
including throttled byte updates, description changes, clear/close, and
total adjustments. Add a regression test for concurrent uploads.

Co-authored-by: Lucas Parzianello <lucaspar@users.noreply.github.com>
bytes_streamed=_file_streamed[0],
prog_bar=prog_bar,
bytes_accounted=bytes_downloaded_shared,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Failed download inflates byte metrics

Medium Severity

Progress bars and byte counts for both uploads and downloads can be inaccurate. When a file transfer fails after partial streaming, the bytes already transferred are still counted towards the total, leading to inflated bytes_downloaded/uploaded and avg_speed_bps in completion summaries. This also causes the progress bar to show more progress than actual successful transfers or to stall inconsistently.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9d81aaa. Configure here.

try:
with _current_log_path.open("a", encoding="utf-8") as f:
f.write(json.dumps(entry, default=str) + "\n")
except OSError:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlocked concurrent JSONL appends

Low Severity

The structured log sink appends JSON lines with a plain file open/write/close on each message. Concurrent upload workers and the asyncio progress logger can emit log records at the same time, which can interleave writes and produce corrupted or partial JSONL lines under load.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9d81aaa. Configure here.

- Replace magic-number assertions in credit_unstreamed_file_bytes tests
- Type progress_callback as Callable in concurrent upload test
- Annotate prog_bar parameter with tqdm[NoReturn]
- Apply ruff formatting fixes

Co-authored-by: Lucas Parzianello <lucaspar@users.noreply.github.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

There are 5 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1fca4e7. Configure here.

overwrite=overwrite,
verbose=verbose,
prefix=prefix,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Path downloads skip byte progress

Medium Severity

Byte-level download progress runs only when files_to_download is a list. The usual download(from_sds_path=...) path uses a Paginator from list_files, so it always takes _download_files_fallback with file-count tqdm and no byte totals or avg_speed_bps in the completion summary.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1fca4e7. Configure here.

f"Downloaded: {file_info.name}",
file_name=file_info.name,
file_size=file_info.size,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skipped downloads log as downloaded

Low Severity

New per-file structured logs emit "Downloaded: {name}" whenever download_single_file returns a successful Result, including skip_contents=True, local-file-exists skips, and BLAKE3-identical skips where no contents were transferred.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1fca4e7. Configure here.

@lucaspar lucaspar merged commit 556b704 into master Jul 1, 2026
5 checks passed
@lucaspar lucaspar deleted the lp/uploads-monitoring branch July 1, 2026 19:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request sdk SDK component

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants