feat(logs): read the logs modal with the shared log parser #431

Merged
allan merged 1 commits from feat/sas-logs-modal into release/7.22 2026-11-01 22:18:15 +00:00
Collaborator

What was wrong

The logs modal parsed the log itself, with two regular expressions and a substring, and the lists it drew disagreed with the log beside them.

  • An ODS HTML line was cut from the first capital E anywhere in it and lost its last character: ERROR: File WORK.X.DATA does not exist.</font>.
  • A message SAS wrapped over several lines was recorded once, truncated at the break. ERROR 22-322: Syntax error, expecting one of the following: ;, CANCEL, never showed RUN, QUIT., and the tail was not reported anywhere.
  • The colon-less form SAS writes (ERROR Libref X is not assigned.) was missed on any line carrying markup.
  • The notable notes - uninitialised variable, invalid data, a format too small for its value - were not shown at all.
  • Clicking an issue searched the rendered text for its message with includes, so a log that repeats a message always jumped to the first copy. Two identical ERROR 22-322: lines in one step is ordinary.
  • The trees rendered at zero: @if (logErrors) is truthy for [], so a clean log drew Errors (0) and Warnings (0).

What it is now

parseLog from @sasjs/utils 4.3.1 replaces all of it - the same function the VS Code extension and the SASjs server Studio use, so one log reads identically in every host.

piece before after
lists errors, warnings errors, warnings, notes
entry the whole line, or a substring of it the message, with SAS's message code where there is one
line index found by searching the text issue.line, plus endLine for a wrapped message
mark <font class="error-line"> on the marker line on every line the issue occupies
scroll search for the message text line * lineHeight
zero lists drawn not drawn

Notes are always shown, as sasjs/server#500 does by default, and are left unmarked in the log text and coloured blue when clicked - a notable note is not a defect. They are not capped: a step that reports one note per rejected record will list them all, which is worth revisiting if a real load makes the tree unwieldy.

The modal is SAS Logs now: the menu entry, the deploy screen's button, the abort modal's Open logs modal, the modal title, the component (SasLogsModalComponent, app-sas-logs-modal), the event service method and the stylesheet selectors. The tabs underneath (SAS Log, SAS Source Code, SAS Generated Code, SAS Work Tables) are unchanged.

Two smaller things on the way past: the warning flash was yellow where the warning text is ochre (both ochre now, notes blue), and formatTimestamp decided on moment().format(), which is the truthy string 'Invalid date' for anything it cannot parse, so its fallback could never fire - it asks isValid() now.

Verification

  • client, scoped: ng test --include='src/app/shared/sas-logs-modal/**/*.spec.ts' - 20 specs, all pass: grouping by severity, codes, a wrapped endLine, a log with nothing to report, a request with no log at all, line marking for wrapped messages, notes left unmarked, the scroll arithmetic (including two identical messages at different lines and a wrap covering two lines), the opened input, timestamps and the three downloads.
  • client, full karma suite: 724 of 724 pass.
  • client, coverage of sas-logs-modal.component.ts (--code-coverage, scoped to those specs): statements 89.41%, branches 92.3%, functions 86.36%, lines 88.31%. What is left is the empty ngOnInit, the layout measurement goToLogLine stubs out, and the three-second overlay removal.
  • client: npm run build (production) exits 0 and emits dist; SAS Logs and the parser's keyword table are both in main.js, so the dependency is bundled rather than tree-shaken away.
  • client: npm run lint:check and prettier --check src/styles.scss clean.
  • CI on this branch: build.yaml run 2810 success - install (npm ci), lint, SAS lint, licence check, Angular tests in both timezones, production build, and the Cypress suite against a mocked SASjs server; lighthouse.yaml run 2811 success.
  • Not run by me: an estate deploy, so the modal has not been clicked in a real SAS 9 or Viya session - CI exercises it against the mocked server only.

The dependency bump

@sasjs/utils goes 4.0.0 -> 4.3.1, which is where the parser lives.

The lock diff is much larger than one version, and it has to be: the lock is regenerated with the repository's own client/.npmrc (ignore-scripts, save-exact), so npm resolves peer dependencies the same way CI does. Regenerating it with legacy-peer-deps produces a lock that omits 44 peer packages, and npm ci then refuses to install at all - which is exactly how the first push failed here.

Verified the way CI runs it: a clean copy of package.json + package-lock.json + .npmrc + the decrypted sheet-crypto.tgz, then npm ci --ignore-scripts - 1818 packages installed, exit 0. The previous lock under the same command fails with npm ci can only install packages when your package.json and package-lock.json ... are in sync.

@sasjs/adapter 5.1.2 pins @sasjs/utils 4.0.1, so its own copy stays nested and untouched - DC imports the parser from the top-level package.

One follow-up, in utils

A wrapped line of an ODS HTML log is not folded: isWrappedLine tested the raw line for indentation, and SAS indents inside the <font> tags, so the second half of a wrapped message never folded on SAS 9. That is fixed in sasjs/utils#294 (fix(logs): fold a wrapped line that carries ODS HTML markup) and reaches DC with the next utils release. This PR does not depend on it: the wrap it exercises is plain text.

## What was wrong The logs modal parsed the log itself, with two regular expressions and a `substring`, and the lists it drew disagreed with the log beside them. - An ODS HTML line was cut from the first capital `E` anywhere in it and lost its last character: `ERROR: File WORK.X.DATA does not exist.</font>`. - A message SAS wrapped over several lines was recorded once, truncated at the break. `ERROR 22-322: Syntax error, expecting one of the following: ;, CANCEL,` never showed `RUN, QUIT.`, and the tail was not reported anywhere. - The colon-less form SAS writes (`ERROR Libref X is not assigned.`) was missed on any line carrying markup. - The notable notes - uninitialised variable, invalid data, a format too small for its value - were not shown at all. - Clicking an issue searched the rendered text for its message with `includes`, so a log that repeats a message always jumped to the first copy. Two identical `ERROR 22-322:` lines in one step is ordinary. - The trees rendered at zero: `@if (logErrors)` is truthy for `[]`, so a clean log drew `Errors (0)` and `Warnings (0)`. ## What it is now `parseLog` from `@sasjs/utils` 4.3.1 replaces all of it - the same function the VS Code extension and the SASjs server Studio use, so one log reads identically in every host. | piece | before | after | | --- | --- | --- | | lists | errors, warnings | errors, warnings, **notes** | | entry | the whole line, or a substring of it | the message, with SAS's message code where there is one | | line | index found by searching the text | `issue.line`, plus `endLine` for a wrapped message | | mark | `<font class="error-line">` on the marker line | on every line the issue occupies | | scroll | search for the message text | `line * lineHeight` | | zero lists | drawn | not drawn | Notes are always shown, as sasjs/server#500 does by default, and are left unmarked in the log text and coloured blue when clicked - a notable note is not a defect. They are not capped: a step that reports one note per rejected record will list them all, which is worth revisiting if a real load makes the tree unwieldy. The modal is **SAS Logs** now: the menu entry, the deploy screen's button, the abort modal's `Open logs modal`, the modal title, the component (`SasLogsModalComponent`, `app-sas-logs-modal`), the event service method and the stylesheet selectors. The tabs underneath (SAS Log, SAS Source Code, SAS Generated Code, SAS Work Tables) are unchanged. Two smaller things on the way past: the warning flash was yellow where the warning text is ochre (both ochre now, notes blue), and `formatTimestamp` decided on `moment().format()`, which is the truthy string `'Invalid date'` for anything it cannot parse, so its fallback could never fire - it asks `isValid()` now. ## Verification - `client`, scoped: `ng test --include='src/app/shared/sas-logs-modal/**/*.spec.ts'` - **20 specs, all pass**: grouping by severity, codes, a wrapped `endLine`, a log with nothing to report, a request with no log at all, line marking for wrapped messages, notes left unmarked, the scroll arithmetic (including two identical messages at different lines and a wrap covering two lines), the `opened` input, timestamps and the three downloads. - `client`, full karma suite: **724 of 724 pass**. - `client`, coverage of `sas-logs-modal.component.ts` (`--code-coverage`, scoped to those specs): statements **89.41%**, branches **92.3%**, functions **86.36%**, lines **88.31%**. What is left is the empty `ngOnInit`, the layout measurement `goToLogLine` stubs out, and the three-second overlay removal. - `client`: `npm run build` (production) exits 0 and emits `dist`; `SAS Logs` and the parser's keyword table are both in `main.js`, so the dependency is bundled rather than tree-shaken away. - `client`: `npm run lint:check` and `prettier --check src/styles.scss` clean. - CI on this branch: `build.yaml` run 2810 **success** - install (`npm ci`), lint, SAS lint, licence check, Angular tests in both timezones, production build, and the Cypress suite against a mocked SASjs server; `lighthouse.yaml` run 2811 **success**. - Not run by me: an estate deploy, so the modal has not been clicked in a real SAS 9 or Viya session - CI exercises it against the mocked server only. ## The dependency bump `@sasjs/utils` goes `4.0.0` -> `4.3.1`, which is where the parser lives. The lock diff is much larger than one version, and it has to be: the lock is regenerated with the repository's own `client/.npmrc` (`ignore-scripts`, `save-exact`), so npm resolves peer dependencies the same way CI does. Regenerating it with `legacy-peer-deps` produces a lock that omits 44 peer packages, and `npm ci` then refuses to install at all - which is exactly how the first push failed here. Verified the way CI runs it: a clean copy of `package.json` + `package-lock.json` + `.npmrc` + the decrypted `sheet-crypto.tgz`, then `npm ci --ignore-scripts` - **1818 packages installed, exit 0**. The previous lock under the same command fails with `npm ci can only install packages when your package.json and package-lock.json ... are in sync`. `@sasjs/adapter` 5.1.2 pins `@sasjs/utils` 4.0.1, so its own copy stays nested and untouched - DC imports the parser from the top-level package. ## One follow-up, in utils A wrapped line of an ODS HTML log is not folded: `isWrappedLine` tested the raw line for indentation, and SAS indents inside the `<font>` tags, so the second half of a wrapped message never folded on SAS 9. That is fixed in sasjs/utils#294 (`fix(logs): fold a wrapped line that carries ODS HTML markup`) and reaches DC with the next utils release. This PR does not depend on it: the wrap it exercises is plain text.
hermes force-pushed feat/sas-logs-modal from 43e7274dc2 to f5d431eaa9 2026-11-01 21:23:32 +00:00 Compare
hermes force-pushed feat/sas-logs-modal from f5d431eaa9 to 3583781d77 2026-11-01 21:23:58 +00:00 Compare
hermes added 1 commit 2026-11-01 21:45:09 +00:00
feat(logs): read the logs modal with the shared log parser
Build / Build-and-ng-test (pull_request) Successful in 6m34s
Lighthouse Checks / lighthouse (pull_request) Successful in 20m44s
Build / Build-and-test-development (pull_request) Successful in 32m1s
aabc578d5e
The modal decided what was wrong with two regular expressions of its own, and the answer disagreed with the log it was showing:

- an ODS HTML line was cut from the first capital `E` in it and lost its last character, so an error read `ERROR: File WORK.X.DATA does not exist.</font>`;
- a message SAS wrapped over several lines was reported once, truncated at the break, and the rest of it was not reported at all;
- the colon-less form SAS writes (`ERROR Libref X is not assigned.`) was missed on any line carrying markup;
- the notable notes - uninitialised variable, invalid data, a format too small - were not shown anywhere;
- clicking an issue searched the rendered text for its message, so a log that repeats a message (two `ERROR 22-322:` lines is ordinary) always jumped to the first of them.

`parseLog` from `@sasjs/utils` replaces all of it, the same function the VS Code extension and the SASjs server Studio use, so one log now reads identically in every host. Each request keeps the issues the parser reported, grouped by severity, and the tree gains a Notes list beside Errors and Warnings. The click scrolls to `issue.line` and flashes from that line to `issue.endLine`, and the lists no longer render at zero.

The modal is SAS Logs now, as are the menu entry, the deploy button and the abort modal's button; the request history is unchanged underneath.

`formatTimestamp` decided on `moment().format()`, which is 'Invalid date' - truthy - for anything it cannot parse, so its fallback could never fire. It asks `isValid()` now.

The warning flash was yellow where the warning text is ochre; both are ochre now, and the notes flash blue.

`@sasjs/utils` goes to 4.3.1, which is where the parser lives.
hermes force-pushed feat/sas-logs-modal from 3583781d77 to aabc578d5e 2026-11-01 21:45:09 +00:00 Compare
Author
Collaborator

CI on the first push caught a real problem in this PR, now fixed.

lighthouse.yaml failed at Install npm dependencies: npm ci refused the tree with can only install packages when your package.json and package-lock.json ... are in sync. The lock had been regenerated while a local .npmrc carrying legacy-peer-deps=true was in place, so npm resolved the tree differently from CI and left out 44 peer packages.

The lock is regenerated with the repository's own client/.npmrc and the PR body now says so. Verified locally against the CI command, in a clean directory holding only package.json, package-lock.json, .npmrc and the decrypted sheet-crypto.tgz:

  • npm ci --ignore-scripts with the previous lock: fails, the same out-of-sync error as CI;
  • npm ci --ignore-scripts with the lock in this branch: 1818 packages added, exit 0.

The only dependency change is @sasjs/utils 4.0.0 -> 4.3.1 plus one patch drift it pulled in (@typescript-eslint/types 8.72.0 -> 8.72.1, inside its existing range).

CI on the first push caught a real problem in this PR, now fixed. `lighthouse.yaml` failed at **Install npm dependencies**: `npm ci` refused the tree with `can only install packages when your package.json and package-lock.json ... are in sync`. The lock had been regenerated while a local `.npmrc` carrying `legacy-peer-deps=true` was in place, so npm resolved the tree differently from CI and left out 44 peer packages. The lock is regenerated with the repository's own `client/.npmrc` and the PR body now says so. Verified locally against the CI command, in a clean directory holding only `package.json`, `package-lock.json`, `.npmrc` and the decrypted `sheet-crypto.tgz`: - `npm ci --ignore-scripts` with the previous lock: fails, the same out-of-sync error as CI; - `npm ci --ignore-scripts` with the lock in this branch: **1818 packages added, exit 0**. The only dependency change is `@sasjs/utils` 4.0.0 -> 4.3.1 plus one patch drift it pulled in (`@typescript-eslint/types` 8.72.0 -> 8.72.1, inside its existing range).
Author
Collaborator

FYI while triaging a red run nearby: the merge commit for this branch (43e7274) fails Build / Build-and-ng-test at npm ci --ignore-scripts, before any spec runs:

npm error `npm ci` can only install packages when your package.json and package-lock.json or npm-shrinkwrap.json are in sync. Please update your lock file with `npm install` before continuing.
npm error Missing: @typescript-eslint/utils@8.72.1 from lock file
npm error Missing: @webcomponents/custom-elements@1.6.0 from lock file
npm error Missing: @babel/preset-env@7.29.7 from lock file
npm error Missing: babel-loader@9.2.1 from lock file
...

This branch changes client/package.json (+1/-1) and client/package-lock.json (46/46), so the lock looks only partly regenerated for the bumped package's transitive set. cd client && npm install (or npm install --package-lock-only with the registry config in place) and committing the resulting lock should clear it. It reproduced identically on a re-run, so it is not runner flakiness.

FYI while triaging a red run nearby: the merge commit for this branch (`43e7274`) fails `Build / Build-and-ng-test` at `npm ci --ignore-scripts`, before any spec runs: ``` npm error `npm ci` can only install packages when your package.json and package-lock.json or npm-shrinkwrap.json are in sync. Please update your lock file with `npm install` before continuing. npm error Missing: @typescript-eslint/utils@8.72.1 from lock file npm error Missing: @webcomponents/custom-elements@1.6.0 from lock file npm error Missing: @babel/preset-env@7.29.7 from lock file npm error Missing: babel-loader@9.2.1 from lock file ... ``` This branch changes `client/package.json` (+1/-1) and `client/package-lock.json` (46/46), so the lock looks only partly regenerated for the bumped package's transitive set. `cd client && npm install` (or `npm install --package-lock-only` with the registry config in place) and committing the resulting lock should clear it. It reproduced identically on a re-run, so it is not runner flakiness.
hermes left a comment
Author
Collaborator

REVIEWED ON 2026-11-01 22:11 UTC

Reviewed the full diff: the renamed modal component and template, the parseLog integration in markIssueLines and goToLogLine, the event/service renames, styles.scss, and the @sasjs/utils bump.

  • client/src/app/shared/sas-logs-modal/sas-logs-modal.component.ts (markIssueLines) - log lines are interpolated into <font class="..."> markup that the template binds through [innerHTML]. Angular's sanitiser stops scripts, but a log line containing < or & is parsed as markup and the display is mangled: a SAS log echoing data or a < in a path is enough. Escape the line text (and the attribute value) before wrapping it, and only add the severity wrapper yourself.
  • client/package-lock.json - besides @sasjs/utils 4.0.0 -> 4.3.1 the lock also moves the @typescript-eslint/* packages 8.72.0 -> 8.72.1. That is unrelated dev-tooling churn from a plain install, and it will be read later as a deliberate upgrade. Regenerate so the lock diff carries only the package this PR changes.

No blocking issues.

REVIEWED ON 2026-11-01 22:11 UTC Reviewed the full diff: the renamed modal component and template, the parseLog integration in markIssueLines and goToLogLine, the event/service renames, styles.scss, and the @sasjs/utils bump. - client/src/app/shared/sas-logs-modal/sas-logs-modal.component.ts (markIssueLines) - log lines are interpolated into `<font class="...">` markup that the template binds through [innerHTML]. Angular's sanitiser stops scripts, but a log line containing `<` or `&` is parsed as markup and the display is mangled: a SAS log echoing data or a `<` in a path is enough. Escape the line text (and the attribute value) before wrapping it, and only add the severity wrapper yourself. - client/package-lock.json - besides @sasjs/utils 4.0.0 -> 4.3.1 the lock also moves the @typescript-eslint/* packages 8.72.0 -> 8.72.1. That is unrelated dev-tooling churn from a plain install, and it will be read later as a deliberate upgrade. Regenerate so the lock diff carries only the package this PR changes. No blocking issues.
allan merged commit 0d8fa59e81 into release/7.22 2026-11-01 22:18:15 +00:00
allan deleted branch feat/sas-logs-modal 2026-11-01 22:18:15 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dc/dc#431