feat(logging): rotate the sandbox file log into a Logs/ subdirectory#5
Merged
Conversation
Replaces the previous "single append-only <tag>.log" with a size-bounded
rotation scheme so a long-running consumer can't fill the sandbox with a
multi-GB log.
- Files now live under <base>/Logs/ (created on init), so an operator
can grab the whole rotated set (<tag>.log + <tag>.{1..N-1}.log) by
copying one directory instead of fishing individual files out.
PascalCase matches iOS's own sandbox conventions (Documents/,
Library/, etc.). Base stays the existing tmp/ or, with
IPA_LOG_TO_DOCUMENTS=1, the Files.app-visible Documents/.
- IPALogPath samples the current size every IPA_LOG_ROTATE_CHECK_EVERY
writes (default 64) and, once it crosses IPA_LOG_MAX_BYTES (default
4 MiB), shifts <tag>.log -> <tag>.1.log -> ... -> <tag>.{N-1}.log,
dropping anything past N-1. N defaults to 3 generations (so up to
12 MiB on disk per consumer at the default ceiling).
- All three knobs (max bytes, generation count, sample interval) are
-D overridable at build time so a noisy consumer can dial up retention
without forking this file.
Best-effort throughout: failed mkdir falls back to writing into the
parent directory, and rotation runs without an explicit lock — the
sampling cadence already keeps two concurrent writers from both
rotating in the same window, and a stray double-rotate just produces
an empty .1.log instead of corrupting data.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the iOS sandbox file logger to prevent unbounded growth by introducing size-based log rotation and consolidating all log files under a dedicated Logs/ subdirectory (under either tmp/ or Documents/, depending on build flags).
Changes:
- Add size-bounded rotation for the sandbox file log with configurable max size, generation count, and sampling interval (
IPA_LOG_MAX_BYTES,IPA_LOG_GENERATIONS,IPA_LOG_ROTATE_CHECK_EVERY). - Move the sandbox log destination into
<base>/Logs/, creating the directory on init with best-effort fallback to the base directory. - Introduce an atomic write counter to amortize filesystem size checks.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+112
to
+115
| static void IPAMaybeRotate(NSString *path) { | ||
| unsigned n = atomic_fetch_add_explicit(&g_writeCount, 1, memory_order_relaxed); | ||
| if ((n % IPA_LOG_ROTATE_CHECK_EVERY) != 0) return; | ||
|
|
Comment on lines
+69
to
+72
| static os_log_t g_log = NULL; | ||
| static NSString *g_logSandbox = nil; | ||
| static NSString *g_tag = @"tweak"; | ||
| static atomic_uint g_writeCount __attribute__((unused)) = 0; |
Comment on lines
+23
to
+38
| // File destination layout — every flavor writes into a `Logs/` subdirectory | ||
| // of its base path (created on init), so operators can grab the whole | ||
| // directory at once instead of fishing individual files out of tmp/ or | ||
| // Documents/. The directory name follows iOS's own sandbox PascalCase | ||
| // convention (sibling to `Documents/`, `Library/`). The default base is | ||
| // NSTemporaryDirectory() (which resolves to | ||
| // /var/mobile/Containers/Data/Application/<UUID>/tmp/), so the full path is | ||
| // .../tmp/Logs/<tag>.log | ||
| // | ||
| // When IPA_LOG_TO_DOCUMENTS=1 is defined at build time, the file destination | ||
| // is moved to <sandbox>/Documents/<tag>.log instead. That directory is | ||
| // exposed through Files.app once the host app's Info.plist carries | ||
| // UIFileSharingEnabled + LSSupportsOpeningDocumentsInPlace — typical for | ||
| // the statically-patched / sideload-injected distribution flavor where the | ||
| // operator has no SSH access. The same log can then be read over the | ||
| // Files app on a non-jailbroken device. | ||
| // When IPA_LOG_TO_DOCUMENTS=1 is defined at build time, the base moves to | ||
| // <sandbox>/Documents/ instead. That directory is exposed through Files.app | ||
| // once the host app's Info.plist carries UIFileSharingEnabled + | ||
| // LSSupportsOpeningDocumentsInPlace — typical for the statically-patched / | ||
| // sideload-injected distribution flavor where the operator has no SSH | ||
| // access. The same log can then be read over the Files app on a | ||
| // non-jailbroken device. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Supersedes #4 (`style:` type isn't in the commitlint enum — that PR is now closed and the same change is squashed into one clean commit here).
What
Replaces the previous "single append-only `.log`" with a size-bounded rotation scheme so a long-running consumer can't fill the sandbox with a multi-GB log.
Why
Bridge in particular has been emitting tens of MB per session (an 80 MB `.log` was seen in the field). The Files-app delivery path makes pulling such a file painful, and tail-only reads miss earlier context.
Design notes
Test plan
🤖 Generated with Claude Code