Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions packages/logger/lib/writers/Console.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {chalkStderr as chalk} from "chalk";
import figures from "figures";
import {MultiBar} from "cli-progress";
import Logger from "../loggers/Logger.js";
import {getLevelPrefix} from "./internal/levelPrefix.js";
import {formatLogLine, prefixModuleName} from "./internal/format.js";
import {REMOTE_CONNECTIONS_WARNING_LINES} from "./interactiveConsole/remoteConnectionsWarning.js";

/**
Expand Down Expand Up @@ -199,8 +199,7 @@ class Console {
if (!Logger.isLevelEnabled(level)) {
return;
}
const levelPrefix = getLevelPrefix(level);
const msg = `${levelPrefix} ${message}\n`;
const msg = formatLogLine(level, message) + "\n";

if (this.#progressBarContainer) {
// If a progress bar is in use, we have to log through it's API
Expand All @@ -213,7 +212,7 @@ class Console {

#handleLogEvent({level, message, moduleName}) {
if (this.#filterModule(moduleName)) {
this.#writeMessage(level, `${chalk.blue(moduleName)} ${message}`);
this.#writeMessage(level, prefixModuleName(message, moduleName));
}
}

Expand Down
25 changes: 10 additions & 15 deletions packages/logger/lib/writers/InteractiveConsole.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import process from "node:process";
import {createLogUpdate} from "log-update";
import sliceAnsi from "slice-ansi";
import chalk from "chalk";
import Logger from "../loggers/Logger.js";
import {getLevelPrefix} from "./internal/levelPrefix.js";
import {formatLogLine, prefixModuleName} from "./internal/format.js";
import {createHeaderState, setTool} from "./interactiveConsole/state/header.js";
import {createProjectState, setProject, enableProjectPlaceholders} from "./interactiveConsole/state/project.js";
import {createServerState, setListening, enableServerPlaceholders} from "./interactiveConsole/state/server.js";
Expand All @@ -18,6 +17,12 @@ import {
// Spinner tick interval while in `building` or `validating` state.
const BUILDING_TICK_MS = 120;

// Module names whose info-level logs duplicate state the banner already renders
// via named build events (ui5.build-metadata / ui5.build-status). Their info
// lines are suppressed so they don't scroll above the live region. Warnings and
// errors from these modules, and all other levels, still pass through.
const BANNER_REDUNDANT_INFO_MODULES = new Set(["ProjectBuilder"]);

// Decode the tail of `stream.write(chunk[, encoding][, callback])`. `encoding`
// falls back to "utf8" (Node's default) when not supplied.
function parseWriteArgs(encodingOrCallback, maybeCallback) {
Expand Down Expand Up @@ -261,23 +266,13 @@ class InteractiveConsole {
}

#handleLog({level, message, moduleName}) {
// The banner curates `info` away (the status line already represents
// the build's state). Verbose / perf / silly users hit the fallback
// path before the writer ever activates. Warnings and errors persist —
// but only if the configured log level lets them through. The standard
// ConsoleWriter does this filtering implicitly via its `#writeMessage`;
// this writer acts as its own sink here, so it has to check too.
if (level !== "warn" && level !== "error") {
if (!Logger.isLevelEnabled(level)) {
return;
}
if (!Logger.isLevelEnabled(level)) {
if (level === "info" && BANNER_REDUNDANT_INFO_MODULES.has(moduleName)) {
return;
}
const levelPrefix = getLevelPrefix(level);
const formatted = moduleName ?
`${levelPrefix} ${chalk.blue(moduleName)} ${message}` :
`${levelPrefix} ${message}`;
this.logAbove(formatted);
this.logAbove(formatLogLine(level, prefixModuleName(message, moduleName)));
}

#handleToolInfo(evt) {
Expand Down
41 changes: 41 additions & 0 deletions packages/logger/lib/writers/internal/format.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {chalkStderr as chalk} from "chalk";

// Shared message-formatting helpers used by every console-writing writer so
// scrolled log lines look identical regardless of which writer produced them.
// Kept as an internal helper module rather than a public export — the exact
// styling is an implementation detail of `writers/Console` and its siblings.

function getLevelPrefix(level) {
switch (level) {
case "silly":
return chalk.inverse(level);
case "verbose":
return chalk.cyan("verb");
case "perf":
return chalk.bgYellow.red(level);
case "info":
return chalk.green(level);
case "warn":
return chalk.yellow(level);
case "error":
return chalk.bgRed.white(level);
default:
// Log level silent does not produce messages
throw new Error(`writers/internal/format: Invalid message log level "${level}"`);
}
}

// Prepend the blue module name to a message when one is present. Both console
// writers render log messages as "<moduleName> <message>", omitting the module
// name when it is absent.
export function prefixModuleName(message, moduleName) {
return moduleName ? `${chalk.blue(moduleName)} ${message}` : message;
}

// Render a full log line as "<levelPrefix> <message>". The trailing newline is
// left to the caller: `writers/Console` writes straight to the stream and needs
// it, while `writers/InteractiveConsole` hands the line to log-update, which
// adds its own.
export function formatLogLine(level, message) {
return `${getLevelPrefix(level)} ${message}`;
}
25 changes: 0 additions & 25 deletions packages/logger/lib/writers/internal/levelPrefix.js

This file was deleted.

2 changes: 1 addition & 1 deletion packages/logger/test/lib/writers/Console.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ test.serial("Log event with invalid log level 'silent'", (t) => {
moduleName: "my:module"
});
}, {
message: `writers/internal/levelPrefix: Invalid message log level "silent"`
message: `writers/internal/format: Invalid message log level "silent"`
});

t.is(stderrWriteStub.callCount, 0, "Logged no message");
Expand Down
43 changes: 39 additions & 4 deletions packages/logger/test/lib/writers/InteractiveConsole.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ test.serial("warn / error logs scroll above the live region", (t) => {
writer.disable();
});

test.serial("info logs are filtered — status line represents build state", (t) => {
test.serial("info logs logs scroll above the live region", (t) => {
const {writer, stderr} = createWriter();

process.emit("ui5.log", {
Expand All @@ -208,10 +208,45 @@ test.serial("info logs are filtered — status line represents build state", (t)
moduleName: "my:module",
});

// The persistent frame renders on info events too because #render is
// called; but no line containing the info text should scroll above.
const output = stripAnsi(stderr.writes.join(""));
t.notRegex(output, /quiet info/);
t.regex(output, /quiet info/);

writer.disable();
});

test.serial("info logs from ProjectBuilder are suppressed (banner-redundant)", (t) => {
const {writer, stderr} = createWriter();

process.emit("ui5.log", {
level: "info",
message: "Preparing build for projects",
moduleName: "ProjectBuilder",
});

const output = stripAnsi(stderr.writes.join(""));
t.notRegex(output, /Preparing build for projects/,
"ProjectBuilder info line does not scroll above the live region");

writer.disable();
});

test.serial("non-info logs from ProjectBuilder still scroll above the live region", (t) => {
const {writer, stderr} = createWriter();

process.emit("ui5.log", {
level: "warn",
message: "Build warning",
moduleName: "ProjectBuilder",
});
process.emit("ui5.log", {
level: "error",
message: "Build failed",
moduleName: "ProjectBuilder",
});

const output = stripAnsi(stderr.writes.join(""));
t.regex(output, /Build warning/);
t.regex(output, /Build failed/);

writer.disable();
});
Expand Down
Loading