Logbestand en diagnose voor release-builds (#2618) #2654

Merged
brenno merged 18 commits from fix/release-logbestand-2618 into main 2026-10-29 15:22:49 +00:00
Owner

Closes #2618. Eerste stap uit de prestatie-inspectie (EPIC #2637): eerst zichtbaar maken, dan pas repareren.

Waarom

Een release-build heeft geen VM-service, dus alle ~800 logError/logWarning-aanroepen verdwenen er spoorloos — ook de zone-handler en FlutterError.onError. Meldingen als "start niet" en "loopt vast" (Windows 10) waren daardoor niet te onderzoeken.

Wat er verandert

  • Logbestand (lib/services/diagnostics/log_file.dart): <app-support>/logs/ocideck.log, roteert naar ocideck.1.log bij 1 MiB, nooit meer dan twee bestanden. Tot het eerste frame wordt elke regel synchroon geschreven, want een start die vastloopt in synchroon werk houdt de event loop vast, en dan zouden juist de laatste mijlpalen ontbreken. Daarna gebundeld en asynchroon, met een bovengrens op wat er mag wachten. Het beamervenster krijgt beamer.log.

  • Privacy aan de bron (lib/utils/log.dart): redactPaths maakt van elk absoluut pad <pad#hash.ext>. logName() doet hetzelfde voor kale bestandsnamen en relatieve repo-paden. Die zijn op ~15 aanroepplekken ingezet, en de gebruikersnaam is uit de LibrePlan-melding gehaald. log_no_content_test bewaakt dit. Herhaalde meldingen worden na 20 keer bemonsterd; fouten en stacks worden afgekapt.

  • Opstartmijlpalen: main, mediastaging, privacylexicon, venster, eerste frame, toestemming, shell — elk met de milliseconden sinds main.

  • Sessiemarkering: ocideck.sessie verdwijnt bij een nette afsluiting (quitApp; op macOS ook het exit-verzoek van Cmd+Q). Ligt hij er bij de volgende start nog, dan staat er in het log dat de vorige sessie vastliep, werd beëindigd of met de computer mee uitging.

  • Stallmelder (ui_stall_watch.dart): tikt elke 250 ms. Komt een tik meer dan 500 ms te laat, dan volgt één regel met de duur en de werklabels (noteWork) uit dat venster. Labels staan op de zware paden uit de inspectie: gezichtsscan, documentexport, openen, Quill↔Markdown, split-runindex, privacyscan, kwaliteitscontrole.

  • Kaart Diagnose in Instellingen → Over OciDeck (alleen desktop):

    • Diagnose kopiëren zet Markdown op het klembord: versie, systeem en de laatste 200 regels in een codeblok, plakklaar voor een issue.
    • Logmap openen opent de map met de bestanden.

    Zes nieuwe teksten, in alle 30 andere talen.

  • Alles terugzetten wist ook de logbestanden; de markering van de lopende sessie blijft staan.

  • Windows-rookproef (release.yml): die keek alleen of het proces na 15 s leefde. Nu wacht hij tot 60 s op opstart: eerste frame in het logbestand en zet hij het log bij een fout in het releaselog. Een proces dat leeft maar nooit tekent, is voor de gebruiker "start niet".

  • Integratietest integration_test/diagnostics_log_test.dart: draait als echte app in de echte app-supportmap, lokaal groen op macOS. De spiegel-CI draait hem op macOS en Windows.

  • Docs:

    • PRIVACY.md: wat er in het log staat en wat niet.
    • TROUBLESHOOTING_GUIDE (en+nl): diagnose ophalen en de logmap per platform. Daarbij twee verouderde stellingen rechtgezet: "geen installatieprogramma" en "geen versie om te noemen".
    • USER_GUIDE (en+nl), PERFORMANCE_GUIDE, CHECKS, SOURCE_MAP, CHANGELOG.

Poort

  • make check groen op 05444e412 (vóór de merge van main) en op merge-commit 697348e64 (zie onder).
  • make check-secrets en make sast groen (0 lekken, 0 bevindingen).
  • DAST niet gedraaid: de webbundel is niet geraakt (het logbestand bestaat niet op web).
  • Mutatieproef: met de padsanering of de rotatie uitgezet worden de tests rood; met een kale ${file.name} in een logmelding valt log_no_content_test.

Bewust één bestandsplafond opgehoogd: settings_dialog.dart 1048 → 1050, voor de import van Diagnostics, zoals eerder bij #2429 en #2510.

Niet getoetst

Windows zelf. De integratietest en de strengere rookproef zijn geschreven voor Windows, maar hebben daar nog niet gedraaid. Het pad %APPDATA%\Stichting LibreKAT\ocideck\logs volgt uit de broncode van path_provider_windows, niet uit een meting. Een CI-run op de spiegel (workflow_dispatch op deze tak) bewijst het vóór de volgende release.

Bewaker — de afwegingen

Het .md en het formaat blijven ongemoeid. Er komt geen nieuwe partij of netwerkverkeer bij: delen gaat bewust via het klembord en niet via een vooringevulde issue-URL, want die stuurt het log over het netwerk voordat de gebruiker het heeft gezien. Drie botsingen tussen diagnose en privacy:

  1. Verbindingsnamen en serveradressen blijven in het log staan (bijv. "HTTP 401 for git.example.org"). Ze zijn diagnostisch waardevol en door de gebruiker zelf ingesteld. Van gedachten veranderen als blijkt dat gebruikers verbindingen naar personen of klanten noemen: dan ook door logName.
  2. Tijdstempels tonen wanneer iemand werkte. Begrensd door de rotatie (2 MiB) en weg met Alles terugzetten; gedocumenteerd in PRIVACY.md.
  3. Het log is niet versleuteld. Het leunt, net als de herstelbestanden, op de accountbescherming van het OS (op Linux chmod 700 via restrictToOwner). Versleutelen zou het juist onleesbaar maken voor de gebruiker die het wil delen.

Veiligheid gaat hier niet verloren: de app werkt ook zonder logbestand. Elke schijffout zet het log uit, nooit de app.

🤖 Generated with Claude Code

Closes #2618. Eerste stap uit de prestatie-inspectie (EPIC #2637): eerst zichtbaar maken, dan pas repareren. ## Waarom Een release-build heeft geen VM-service, dus alle ~800 `logError`/`logWarning`-aanroepen verdwenen er spoorloos — ook de zone-handler en `FlutterError.onError`. Meldingen als "start niet" en "loopt vast" (Windows 10) waren daardoor niet te onderzoeken. ## Wat er verandert - **Logbestand** (`lib/services/diagnostics/log_file.dart`): `<app-support>/logs/ocideck.log`, roteert naar `ocideck.1.log` bij 1 MiB, nooit meer dan twee bestanden. Tot het eerste frame wordt elke regel synchroon geschreven, want een start die vastloopt in synchroon werk houdt de event loop vast, en dan zouden juist de laatste mijlpalen ontbreken. Daarna gebundeld en asynchroon, met een bovengrens op wat er mag wachten. Het beamervenster krijgt `beamer.log`. - **Privacy aan de bron** (`lib/utils/log.dart`): `redactPaths` maakt van elk absoluut pad `<pad#hash.ext>`. `logName()` doet hetzelfde voor kale bestandsnamen en relatieve repo-paden. Die zijn op ~15 aanroepplekken ingezet, en de gebruikersnaam is uit de LibrePlan-melding gehaald. `log_no_content_test` bewaakt dit. Herhaalde meldingen worden na 20 keer bemonsterd; fouten en stacks worden afgekapt. - **Opstartmijlpalen**: main, mediastaging, privacylexicon, venster, eerste frame, toestemming, shell — elk met de milliseconden sinds `main`. - **Sessiemarkering**: `ocideck.sessie` verdwijnt bij een nette afsluiting (`quitApp`; op macOS ook het exit-verzoek van Cmd+Q). Ligt hij er bij de volgende start nog, dan staat er in het log dat de vorige sessie vastliep, werd beëindigd of met de computer mee uitging. - **Stallmelder** (`ui_stall_watch.dart`): tikt elke 250 ms. Komt een tik meer dan 500 ms te laat, dan volgt één regel met de duur en de werklabels (`noteWork`) uit dat venster. Labels staan op de zware paden uit de inspectie: gezichtsscan, documentexport, openen, Quill↔Markdown, split-runindex, privacyscan, kwaliteitscontrole. - **Kaart Diagnose** in Instellingen → Over OciDeck (alleen desktop): - **Diagnose kopiëren** zet Markdown op het klembord: versie, systeem en de laatste 200 regels in een codeblok, plakklaar voor een issue. - **Logmap openen** opent de map met de bestanden. Zes nieuwe teksten, in alle 30 andere talen. - **Alles terugzetten** wist ook de logbestanden; de markering van de lopende sessie blijft staan. - **Windows-rookproef** (`release.yml`): die keek alleen of het proces na 15 s leefde. Nu wacht hij tot 60 s op `opstart: eerste frame` in het logbestand en zet hij het log bij een fout in het releaselog. Een proces dat leeft maar nooit tekent, is voor de gebruiker "start niet". - **Integratietest** `integration_test/diagnostics_log_test.dart`: draait als echte app in de echte app-supportmap, lokaal groen op macOS. De spiegel-CI draait hem op macOS en Windows. - **Docs**: - `PRIVACY.md`: wat er in het log staat en wat niet. - `TROUBLESHOOTING_GUIDE` (en+nl): diagnose ophalen en de logmap per platform. Daarbij twee verouderde stellingen rechtgezet: "geen installatieprogramma" en "geen versie om te noemen". - `USER_GUIDE` (en+nl), `PERFORMANCE_GUIDE`, `CHECKS`, `SOURCE_MAP`, `CHANGELOG`. ## Poort - `make check` groen op `05444e412` (vóór de merge van main) en op merge-commit `697348e64` (zie onder). - `make check-secrets` en `make sast` groen (0 lekken, 0 bevindingen). - DAST niet gedraaid: de webbundel is niet geraakt (het logbestand bestaat niet op web). - Mutatieproef: met de padsanering of de rotatie uitgezet worden de tests rood; met een kale `${file.name}` in een logmelding valt `log_no_content_test`. Bewust één bestandsplafond opgehoogd: `settings_dialog.dart` 1048 → 1050, voor de import van `Diagnostics`, zoals eerder bij #2429 en #2510. ## Niet getoetst **Windows zelf.** De integratietest en de strengere rookproef zijn geschreven voor Windows, maar hebben daar nog niet gedraaid. Het pad `%APPDATA%\Stichting LibreKAT\ocideck\logs` volgt uit de broncode van `path_provider_windows`, niet uit een meting. Een CI-run op de spiegel (`workflow_dispatch` op deze tak) bewijst het vóór de volgende release. ## Bewaker — de afwegingen Het `.md` en het formaat blijven ongemoeid. Er komt geen nieuwe partij of netwerkverkeer bij: delen gaat bewust via het klembord en niet via een vooringevulde issue-URL, want die stuurt het log over het netwerk voordat de gebruiker het heeft gezien. Drie botsingen tussen diagnose en privacy: 1. **Verbindingsnamen en serveradressen blijven in het log staan** (bijv. "HTTP 401 for git.example.org"). Ze zijn diagnostisch waardevol en door de gebruiker zelf ingesteld. Van gedachten veranderen als blijkt dat gebruikers verbindingen naar personen of klanten noemen: dan ook door `logName`. 2. **Tijdstempels tonen wanneer iemand werkte.** Begrensd door de rotatie (2 MiB) en weg met *Alles terugzetten*; gedocumenteerd in PRIVACY.md. 3. **Het log is niet versleuteld.** Het leunt, net als de herstelbestanden, op de accountbescherming van het OS (op Linux `chmod 700` via `restrictToOwner`). Versleutelen zou het juist onleesbaar maken voor de gebruiker die het wil delen. Veiligheid gaat hier niet verloren: de app werkt ook zonder logbestand. Elke schijffout zet het log uit, nooit de app. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Een release-build heeft geen VM-service, dus alle ~800 logError- en
logWarning-aanroepen verdwenen er spoorloos. log.dart krijgt een LogSink:
elke regel gaat, naast dart:developer, naar een geïnstalleerde bestemming.

Wat de schijf haalt, gaat eerst door redactPaths: elk absoluut pad wordt
<pad#hash.ext>, zodat een gebruikersnaam of bestandsnaam niet in een
logbestand belandt dat een gebruiker met ons deelt. Dezelfde melding mag
twintig keer vrij door, daarna één op de honderd met telling. Fouten
worden afgekapt, stacks blijven bij de bovenste frames.

Daarnaast logInfo/logMilestone voor opstartmijlpalen en noteWork/
workSince: een goedkope ring met werklabels die de stallmelder na een
vastloper kan noemen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Een periodieke timer van 250 ms die ruim te laat vuurt, betekent dat de
UI-isolate al die tijd bezet was. Boven de 500 ms extra komt er één
logregel met de duur en de werklabels die in dat venster zijn gezet.
Een verborgen venster (App Nap) of een toestandswissel telt niet mee.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LogFile schrijft naar <app-support>/logs/ocideck.log, met hooguit twee
bestanden van 1 MiB. Tot het eerste frame schrijft elke regel meteen en
synchroon: een start die vastloopt in synchroon werk houdt de event loop
vast, en dan zouden juist de regels ontbreken die zeggen waar. Daarna
gebundeld en asynchroon, met een bovengrens op wat er mag wachten.

Een .sessie-markering die bij een nette afsluiting verdwijnt, meldt bij
de volgende start dat de vorige sessie vastliep, werd beëindigd of met
de computer mee uitging. Diagnostics bundelt het: opzetten, de
systeemregel per sessie, de stallmelder na het eerste frame, en de tekst
voor "Diagnose kopiëren".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main zet het logbestand op vóór al het andere en logt een mijlpaal na
elke stap die de start kan ophouden: mediastaging, privacylexicon,
venster, eerste frame. Het beamervenster krijgt een eigen bestand zonder
sessiemarkering.

quitApp rondt het log af vlak vóór het sluiten; op macOS doet de
luisteraar op het exit-verzoek dat ook, want Cmd+Q loopt niet via
quitApp. Het antwoord op dat verzoek blijft exit, zoals zonder luisteraar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
noteWork op de plekken die de prestatie-inspectie van 28-10 als
UI-blokkades aanwees: gezichtsscan, documentexport, presentatie openen,
de Quill↔Markdown-omzetting, de split-runindex, de privacyscan en de
kwaliteitscontrole. Een stallregel noemt daarmee niet alleen hoe lang,
maar ook waardoor. Het label staat in de gedeelde functie, niet bij elke
aanroeper.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Een logregel staat sinds het logbestand op schijf en kan met ons worden
gedeeld. Absolute paden haalt redactPaths eruit, maar een kale
bestandsnaam, een archiefnaam of een relatief repo-pad als
decks/ontslag-jan is voor een patroon een gewoon woord. Die gaan nu door
logName(), dat ze tot <naam#hash.ext> maakt: twee regels over hetzelfde
bestand blijven herkenbaar, de naam zelf niet. De LibreplanClient-melding
noemde de gebruikersnaam; die is eruit.

log_no_content_test bewaakt het: een interpolatie van file.name,
entry.path, deckDir, sourceName, username of een kale name in een
logmelding faalt. De stallregel bouwt zijn labelopsomming buiten de
aanroep, zodat ook de bestaande inhoudsvormentoets schoon blijft.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Na het eerste frame kan een start nog op de toestemmingspoort of het
opbouwen van de shell hangen. Beide komen nu als mijlpaal in het log.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diagnose kopiëren levert Markdown: systeemregels als lijst, de laatste
200 logregels in een codeblok, zodat de tracker ze niet als opmaak leest.
Via het klembord en bewust niet via een vooringevulde issue-URL: dan ging
het log over het netwerk voordat de gebruiker het had gezien.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Twee routes om het logboek bij een melding te voegen: "Diagnose
kopiëren" zet versie, systeem en de laatste 200 logregels plakklaar op
het klembord, "Logmap openen" opent de map om het bestand mee te sturen.
Er gaat niets vanzelf de deur uit. Desktop alleen; vindbaar via het
zoekveld van de instellingen ("logboek", "crash", "issue").

Zes nieuwe teksten, in alle dertig andere talen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
De Windows-rookproef keek alleen of het proces na 15 s nog leefde. Een
proces dat leeft maar nooit een frame tekent, is voor de gebruiker "de
app start niet": het venster verschijnt op Windows pas bij het eerste
frame. De proef wacht nu tot 60 s op de mijlpaal "eerste frame" in het
logbestand en zet dat log bij een fout in het releaselog.

integration_test/diagnostics_log_test.dart draait het logbestand als
echte app in de echte app-supportmap. Op Windows is dat de enige plek
waar %APPDATA%, de virusscanner en de rotatie samenkomen; de spiegel-CI
draait hem op macOS en Windows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Het logbestand is een nieuw spoor op schijf: het zegt wanneer de app
draaide en wat er misging. "Terug naar de begintoestand" belooft alle
sporen weg te halen, dus DiskTraces.clearLogs neemt de .log-bestanden
mee. De sessiemarkering van de lopende sessie blijft staan, zodat een
crash straks nog steeds bij de volgende start gemeld wordt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PRIVACY.md zegt wat het logbestand bevat en wat niet, en dat Alles
terugzetten het meeneemt. De probleemoplossingsgids (en + nl) legt uit
hoe je bij "start niet" of "loopt vast" de diagnose ophaalt en waar de
logmap per platform staat; daarbij twee stellingen rechtgezet die al
langer niet klopten ("er is geen installatieprogramma", "er is geen
versie om te noemen"). USER_GUIDE noemt de kaart, PERFORMANCE_GUIDE de
stallregels als waarneming in het veld, CHECKS.md de integratietest en
de strengere Windows-rookproef, SOURCE_MAP de drie nieuwe bestanden.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
De analyzer staat op zijn klasseplafond; het label hoort evengoed bij de
plek waar de deckbrede analyse wordt aangevraagd.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diagnostics.openLogDirectory opent de map; de kaart vraagt het alleen.
De widgettest wachtte binnen runAsync op een vaste klok en wacht nu met
pumpUntil op het rapport op het klembord. Het bestandsplafond van
settings_dialog.dart gaat +1 voor de import van Diagnostics, zoals bij
de eerdere kaarten (#2429, #2510).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
buildDocumentExportBytes zat op het methodeplafond; het label staat nu
in writeDocumentExport, vlak voor de bouw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Owner

Bijgewerkt op 98e57dc2d: origin/main erin gemerged (de tak was mergeable: false geworden na PR #2665). Drie echte botsingen opgelost:

  • lib/main.dart: de nieuwe opstartvolgorde van #2626/#2628/#2656 behouden; de diagnosemijlpalen eromheen gezet. De Linux-enkel-exemplaarcontrole staat bewust vóór Diagnostics.start — een doorgestuurde tweede instantie zou anders de sessiemarkering van de primaire overschrijven en als valse "niet netjes afgesloten" terugkomen, plus gelijktijdig in hetzelfde logbestand schrijven.
  • lib/utils/markdown_quill_codec.dart: noteWork behouden én de snellere convertDocument van #2621 (niet terug naar toDelta()).
  • tool/check_conventions.dart: beide baseline-toelichtingen samengevoegd; het plafond blijft 1050.

Lokale poort op deze head: make check-full volledig groen (format, analyze, conventies, ~14,5k tests + coverage + goldens, gitleaks, trufflehog, semgrep, sbom/web/marp).

Bijgewerkt op `98e57dc2d`: `origin/main` erin gemerged (de tak was `mergeable: false` geworden na PR #2665). Drie echte botsingen opgelost: - `lib/main.dart`: de nieuwe opstartvolgorde van #2626/#2628/#2656 behouden; de diagnosemijlpalen eromheen gezet. De Linux-enkel-exemplaarcontrole staat bewust **vóór** `Diagnostics.start` — een doorgestuurde tweede instantie zou anders de sessiemarkering van de primaire overschrijven en als valse "niet netjes afgesloten" terugkomen, plus gelijktijdig in hetzelfde logbestand schrijven. - `lib/utils/markdown_quill_codec.dart`: `noteWork` behouden én de snellere `convertDocument` van #2621 (niet terug naar `toDelta()`). - `tool/check_conventions.dart`: beide baseline-toelichtingen samengevoegd; het plafond blijft 1050. Lokale poort op deze head: `make check-full` volledig groen (format, analyze, conventies, ~14,5k tests + coverage + goldens, gitleaks, trufflehog, semgrep, sbom/web/marp).
brenno merged commit 709e9ba77b into main 2026-10-29 15:22:49 +00:00
Sign in to join this conversation.
No description provided.