Skip to content

[simulator] Record and upload selected Android logs - #4795

Draft
vonovak wants to merge 7 commits into
mainfrom
codex/android-session-logs
Draft

vonovak wants to merge 7 commits into
mainfrom
codex/android-session-logs

Conversation

@vonovak

@vonovak vonovak commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Why

Android session diagnostics disappear with the worker. This PoC makes selected emulator and device logs downloadable after the session, while bounding each retained raw snapshot and reducing artifact storage with gzip.

How

Summary

session selects sources before boot
  default: emulator-output; explicit opt-in: device-logcat
  record only selected sources, across retries and clones
cleanup (always): registered directories for this workflow only
  snapshot the last <=100 MiB of each selected raw log
  gzip level 6 -> complete .log.gz -> existing artifact upload API
  successful upload -> unlink raw log; remove temporary gzip snapshot

The Linux-only eas/collect_android_emulator_logs step accepts sources; an empty list disables both. The CLI exposes repeatable --android-log-source flags. Selection replaces the default and reaches both Android startup paths; legacy build workflows retain their existing capture defaults.

Artifacts retain unique launch names and kind simulator-log. Metadata reports compression: gzip, original, retained, compressed, and omitted byte counts. Truncation is visible in the artifact name. The 100 MiB limit applies before compression; compressed bytes are never truncated. A fixed snapshot makes Content-Length and retries identical even while the source grows. Each file has a 30-second budget and two attempts; failures remain nonfatal. Successful uploads stay deduplicated even if file cleanup fails, and failed uploads retain their raw source for another attempt.

The previous 20 MiB raw-tail policy was introduced in this branch, not imposed by storage. Three ordinary startup samples compressed by about 88–89%; extrapolated rates put ten minutes at about 100–111 MiB raw / 11–14 MiB gzip. The human selected a 100 MiB cap, corresponding to roughly 9–10 minutes at those rates, not a guaranteed duration. Synthetic smoke-test logs compress much more and are excluded from those estimates.

Deploy the worker supporting sources and android_log_sources before Universe #32975, then the API before distributing the CLI flag. The API artifact contract does not need a compression change.

Test Plan

Evidence

Codex ran on 2026-11-02:

  • 40 tests across androidEmulatorLogs, androidEmulatorLogsUpload, collectAndroidEmulatorLogs, and uploadWithSignedUrl: passed.
  • Repository type checks across 15 packages passed during this change; build-tools type checking was rerun after the final changes and passed. Repository lint, formatting, and git diff --check: passed.
  • Real gzip round trips for both sources; a 100 MiB incompressible retained tail survives an early HTTP 503 and decompresses byte-for-byte. This verifies that compressed output larger than the raw cap is never cut.
  • Identical compressed retry bodies, growing/emptied source files, compression/read/upload failures, selective capture, deduplication, raw-file removal only after upload success, and cleanup failures.

Earlier capture-selection revision: Codex ran 123 worker tests, 31 host-orchestration tests, and four live staging cases for default, both, logcat-only, and disabled sources. Those passed, but predate compression and do not validate the final gzip downloads. Earlier CLI tests passed (139); this follow-up does not change CLI code.

Current gzip staging validation

Codex validated worker 83e4abb9f with API df70d516cb on 2026-11-02. The Linux worker build passed; all 516 packaged build-tools source files matched the worker commit, and the compiled collector contained gzip, the 100 MiB raw cap, and post-upload raw cleanup.

Case Retained raw bytes Downloaded gzip bytes
Default Argent: emulator only 10,304 3,726
Both sources, agent-device: emulator 10,347 3,737
Same session: device logcat 36,500,585 1,476,297

Both sessions reached FINISHED/STOPPED. Every gzip downloaded and decompressed successfully; size metadata matched, and the logcat fixture's final marker was present. The default session produced no logcat artifact. These files were below the raw cap; truncation at 100 MiB and raw-file deletion were verified locally, not through worker filesystem inspection. The fixture emits repetitive synthetic data, so its compression ratio is not a production estimate.

The prior capture-capable staging worker was restored with a generation precondition and verified checksum. The existing manual preview remains active; the temporary gzip worker deployment has been rolled back. No gzip-validation sessions remain running. The original manual setup's guarded rollback journal was updated to the restored generation.

Current-head review

Automated correctness/security review completed for 83e4abb9f: ready for human review with one warning about default-source propagation. Codex traced the companion API's explicit emulator-only normalization and test coverage and documented why the worker fallback is not used for API-created sessions. No compression/upload defect was reported. Node 22/24 CI passed. Human inspection and approval remain pending.

Merge Danger

Door: two-way for future collection. Disabling collection stops new artifacts; rollback does not recall prior downloads or delete existing artifacts.

Blast Radius: Android diagnostics.

  • Device-wide verbose logcat stays opt-in; emulator output stays the default. Both are unredacted and can contain sensitive values. Existing session/project permissions protect discovery, while anyone holding a download link can access it.
  • Existing artifact lifecycle deletes these files after 90 days; this PoC adds no shorter log-specific policy. Retention configuration.
  • Only gzip artifacts are uploaded. Raw files are unlinked after successful upload; an emulator still holding the file open releases disk space when its writer closes. Failed uploads keep raw files for retry.
  • The cap is per raw snapshot, not a total session storage or live worker-disk limit. Local capture can grow before cleanup; earlier history beyond the cap is omitted. There is no rotation, periodic upload, filtering, or guarantee of collection after worker loss.
  • Roll out the worker before the API workflow; do not roll back to a worker that rejects new inputs while queued/running sessions require them.

Authorship and LLM use

The human initially scoped post-session diagnostics, then required source selection to prevent recording unselected logs. Codex authored and tested those changes; earlier agent review found cleanup, truncation-label, retry, and descriptor-ownership issues that were addressed. Those reviews predate this diff. The human then approved a gzip PoC with existing retention and opt-in semantics, asked about ten-minute coverage, and requested keeping only compressed artifacts. The human selected a 100 MiB raw cap after discussing the measured rates; Codex implemented compression, metadata, and raw-file cleanup. Codex ran the checks and live staging validation listed above and responded to the automated default-source warning with cross-repository evidence. Human inspection and approval of the final diff remain pending.

@codecov

codecov Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 76.50%. Comparing base (6f0f90b) to head (83e4abb).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4795      +/-   ##
==========================================
+ Coverage   76.38%   76.50%   +0.13%     
==========================================
  Files        1093     1097       +4     
  Lines       54720    55035     +315     
  Branches    12026    12126     +100     
==========================================
+ Hits        41794    42101     +307     
- Misses      11939    11946       +7     
- Partials      987      988       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@vonovak vonovak changed the title [build-tools] Upload Android simulator session logs [simulator] Upload selected Android logs with logcat opt-in Nov 2, 2026
@vonovak vonovak changed the title [simulator] Upload selected Android logs with logcat opt-in [simulator] Record and upload selected Android logs Nov 2, 2026
@vonovak

vonovak commented Nov 2, 2026

Copy link
Copy Markdown
Contributor Author

/review correctness security

@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown

✅ Thank you for adding a changeset!

@vonovak

vonovak commented Nov 2, 2026

Copy link
Copy Markdown
Contributor Author

/review correctness security

@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown

🤖 AI code review

Decision: Ready for human review (with comments)

Overall PR risk: Medium. The change modifies existing behavior: Android emulator and device-session build steps that always captured both emulator-output and logcat now gate capture and upload behind a sources list, and raw logs get deleted from the worker after upload instead of staying in place. The one reported warning shows a default mismatch between the device-session start step (defaults to both sources) and the collect step (defaults to emulator-output only), which could record device-wide logcat by default against the PR's stated opt-in design and leave orphaned, uncleaned logcat files on disk. The blast radius covers all Android build and simulator-session jobs that touch logcat capture; the change is additive at the API surface (new optional CLI flag, new optional step inputs) and is reversible by adjusting the default, but any in-flight jobs relying on prior always-present logcat behavior would need identification first.

🔎 Reviewer summaries (3)
  • correctness: Reviewed new Android emulator log collection/upload code and CLI flag plumbing for logic errors, error handling, and type safety; traced truncation, retry, and abort-signal paths against their tests and found no concrete correctness defect in the files in scope.
  • security: Reviewed the Android emulator log capture and upload change for credential exposure, injection, and artifact leak paths. The new source gate defaults to emulator-output only, and no secret reaches logs or stdout. No security findings. Reviewed the new Android emulator log capture, snapshot, and upload path plus the new --android-log-source CLI flag. Found no credential exposure, no command injection, and no trust-boundary gap; device-logcat upload stays opt-in.
  • cross-cutting: no summary

🔗 Review setup:

  • .expo-code-review/config.jsonc: policy.includeSuggestions is deprecated. Replace it with "minSeverity": "design".

⚠️ Warning (1)

  • Device session Android log default contradicts its own stated contract — packages/build-tools/src/steps/functions/startAgentDeviceSession.ts:76 (correctness) · id:af7e72ddf61b
    Confidence: Medium — the default conflicts with an explicit comment in a file the PR adds.
    Impact if shipped: Medium — device-wide logcat may record by default for simulator sessions, against the stated opt-in design.
    Suggested remediation: Give startAgentDeviceSession.ts its own default of ['emulator-output'] (matching collectAndroidEmulatorLogs.ts), and reserve LEGACY_ANDROID_EMULATOR_LOG_SOURCES for startAndroidEmulator.ts's pre-existing build workflows.

    Evidence and reasoning

    packages/build-tools/src/utils/androidEmulatorLogSources.ts states: "Existing build workflows capture both sources. Device sessions pass their selection explicitly." But startAgentDeviceSession.ts, the function that runs device sessions, sets defaultValue: [...LEGACY_ANDROID_EMULATOR_LOG_SOURCES] for its android_log_sources input. This is the same "both sources" default used for plain build workflows in startAndroidEmulator.ts.

    At the same time, packages/eas-cli/src/commands/simulator/index.ts omits logSources from the createDeviceRunSessionAsync mutation input when the user does not pass --android-log-source. A test confirms this: "leaves Android log sources omitted unless explicitly requested." So a simulator session started without the flag does not pass its selection explicitly. It relies on the downstream default.

    A sibling test in deviceSessionHost-orchestration.test.ts shows that omitted step inputs use their declared defaults. This means startAgentDeviceSession.ts falls back to LEGACY (both sources). AndroidEmulatorUtils.startAsync then records device-wide device-logcat to disk. This happens even though the CLI flag description says this needs explicit opt-in: "Device-wide device-logcat may contain credentials and personal data... opt in to recording and uploading." The changeset also says: "Unselected sources are not recorded by simulator sessions."

    The separate collectAndroidEmulatorLogs.ts step defaults its own sources to ['emulator-output'] only. So the logcat file would not upload by default, but it would still write to disk and never clean up. Cleanup only runs after a successful upload in androidEmulatorLogs.ts. This contradicts the stated default-off behavior for device-wide logcat capture.


This review is advisory — it never blocks a merge and never auto-approves.

@vonovak

vonovak commented Nov 2, 2026

Copy link
Copy Markdown
Contributor Author

Review response for af7e72ddf61b (worker head 83e4abb9f): the CLI omission does not become an omitted worker step input. The companion API resolves it to emulator-output before constructing the workflow.

  • createAsync validates androidLogSources ?? ['emulator-output'] and passes the resulting list to workflow construction: DeviceRunSessionUtils.ts:1282, line 1535.
  • buildStepsForType independently resolves the omitted AGENT_DEVICE selection to emulator-output: line 1109. buildStartVirtualDeviceStep puts that list into android_log_sources (line 481), and buildAgentDeviceStartSteps spreads those inputs into eas/start_agent_device_session (line 703).
  • Tests assert explicit emulator-only startup and collection inputs for every session type, including AGENT_DEVICE: default-input assertion. Current API CI passed these tests. Today's gzip staging default-Argent check also uploaded only emulator output; that live case does not independently inspect worker files.

The worker's legacy default preserves existing workflows that omit the new input. That is separate from the new API-created session default, which is always supplied explicitly. I am retaining the compatibility default and recording this warning as disputed on that evidence. Human review remains pending.

@vonovak
vonovak marked this pull request as ready for review November 2, 2026 15:56
@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown

Subscribed to pull request

File Patterns Mentions
packages/eas-cli/** @douglowder

Generated by CodeMention

Warning: The preamble and epilogue options in commentConfiguration are deprecated. Use template instead.

@vonovak
vonovak marked this pull request as draft November 2, 2026 19:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant