aboutsummaryrefslogtreecommitdiff
path: root/pw_console/py/pw_console/html/main.js
diff options
context:
space:
mode:
Diffstat (limited to 'pw_console/py/pw_console/html/main.js')
-rw-r--r--pw_console/py/pw_console/html/main.js316
1 files changed, 143 insertions, 173 deletions
diff --git a/pw_console/py/pw_console/html/main.js b/pw_console/py/pw_console/html/main.js
index d08d019ed..79e239eca 100644
--- a/pw_console/py/pw_console/html/main.js
+++ b/pw_console/py/pw_console/html/main.js
@@ -12,136 +12,146 @@
// License for the specific language governing permissions and limitations under
// the License.
-var VirtualizedList = window.VirtualizedList.default;
-const rowHeight = 30;
+// eslint-disable-next-line no-undef
+const { createLogViewer, LogSource, LogEntry, Severity } = PigweedLogging;
-function formatDate(dt) {
- function pad2(n) {
- return (n < 10 ? '0' : '') + n;
- }
+let currentTheme = {};
+let defaultLogStyleRule = 'color: #ffffff;';
+let columnStyleRules = {};
+let defaultColumnStyles = [];
+let logLevelStyles = {};
- return dt.getFullYear() + pad2(dt.getMonth() + 1) + pad2(dt.getDate()) + ' ' +
- pad2(dt.getHours()) + ':' + pad2(dt.getMinutes()) + ':' +
- pad2(dt.getSeconds());
-}
+const logLevelToString = {
+ 10: 'DBG',
+ 20: 'INF',
+ 21: 'OUT',
+ 30: 'WRN',
+ 40: 'ERR',
+ 50: 'CRT',
+ 70: 'FTL',
+};
-let data = [];
-function clearLogs() {
- data = [{
- 'message': 'Logs started',
- 'levelno': 20,
- time: formatDate(new Date()),
- 'levelname': '\u001b[35m\u001b[1mINF\u001b[0m',
- 'args': [],
- 'fields': {'module': '', 'file': '', 'timestamp': '', 'keys': ''}
- }];
-}
-clearLogs();
+const logLevelToSeverity = {
+ 10: Severity.DEBUG,
+ 20: Severity.INFO,
+ 21: Severity.INFO,
+ 30: Severity.WARNING,
+ 40: Severity.ERROR,
+ 50: Severity.CRITICAL,
+ 70: Severity.CRITICAL,
+};
-let nonAdditionalDataFields =
- ['_hosttime', 'levelname', 'levelno', 'args', 'fields', 'message', 'time'];
+let nonAdditionalDataFields = [
+ '_hosttime',
+ 'levelname',
+ 'levelno',
+ 'args',
+ 'fields',
+ 'message',
+ 'time',
+];
let additionalHeaders = [];
-function updateHeadersFromData(data) {
- let dirty = false;
- Object.keys(data).forEach((columnName) => {
- if (nonAdditionalDataFields.indexOf(columnName) === -1 &&
- additionalHeaders.indexOf(columnName) === -1) {
- additionalHeaders.push(columnName);
- dirty = true;
- }
- });
- Object.keys(data.fields || {}).forEach((columnName) => {
- if (nonAdditionalDataFields.indexOf(columnName) === -1 &&
- additionalHeaders.indexOf(columnName) === -1) {
- additionalHeaders.push(columnName);
- dirty = true;
- }
- });
- const headerDOM = document.querySelector('.log-header');
- if (dirty) {
- headerDOM.innerHTML = `
- <span class="_hosttime">Time</span>
- <span class="level">Level</span>
- ${
- additionalHeaders
- .map((key) => `
- <span class="${key}">${key}</span>
- `).join('\n')}
- <span class="msg">Message</span>`
+// New LogSource to consume pw-console log json messages
+class PwConsoleLogSource extends LogSource {
+ constructor() {
+ super();
+ }
+ append_log(data) {
+ var fields = [
+ { key: 'severity', value: logLevelToSeverity[data.levelno] },
+ { key: 'time', value: data.time },
+ ];
+ Object.keys(data.fields).forEach((columnName) => {
+ if (
+ nonAdditionalDataFields.indexOf(columnName) === -1 &&
+ additionalHeaders.indexOf(columnName) === -1
+ ) {
+ fields.push({ key: columnName, value: data.fields[columnName] });
+ }
+ });
+ fields.push({ key: 'message', value: data.message });
+ fields.push({ key: 'py_file', value: data.py_file });
+ fields.push({ key: 'py_logger', value: data.py_logger });
+ this.emitEvent('logEntry', {
+ severity: logLevelToSeverity[data.levelno],
+ timestamp: new Date(),
+ fields: fields,
+ });
}
+}
- // Also update column widths to match actual row.
- const headerChildren = Array.from(headerDOM.children);
+// Setup the pigweedjs log-viewer
+const logSource = new PwConsoleLogSource();
+const containerEl = document.querySelector('#log-viewer-container');
+let unsubscribe = createLogViewer(logSource, containerEl);
- const firstRow = document.querySelector('.log-container .log-entry');
- const firstRowChildren = Array.from(firstRow.children);
- headerChildren.forEach((col, index) => {
- if (firstRowChildren[index]) {
- col.setAttribute(
- 'style',
- `width:${firstRowChildren[index].getBoundingClientRect().width}`);
- col.setAttribute('title', col.innerText);
- }
- })
+// Format a date in the standard pw_cli style YYYY-mm-dd HH:MM:SS
+function formatDate(dt) {
+ function pad2(n) {
+ return (n < 10 ? '0' : '') + n;
+ }
+
+ return (
+ dt.getFullYear() +
+ pad2(dt.getMonth() + 1) +
+ pad2(dt.getDate()) +
+ ' ' +
+ pad2(dt.getHours()) +
+ ':' +
+ pad2(dt.getMinutes()) +
+ ':' +
+ pad2(dt.getSeconds())
+ );
}
+// Return the value for the given # parameter name.
function getUrlHashParameter(param) {
var params = getUrlHashParameters();
return params[param];
}
+// Capture all # parameters from the current URL.
function getUrlHashParameters() {
var sPageURL = window.location.hash;
- if (sPageURL)
- sPageURL = sPageURL.split('#')[1];
+ if (sPageURL) sPageURL = sPageURL.split('#')[1];
var pairs = sPageURL.split('&');
var object = {};
- pairs.forEach(function(pair, i) {
+ pairs.forEach(function (pair, i) {
pair = pair.split('=');
- if (pair[0] !== '')
- object[pair[0]] = pair[1];
+ if (pair[0] !== '') object[pair[0]] = pair[1];
});
return object;
}
-let currentTheme = {};
-let defaultLogStyleRule = 'color: #ffffff;';
-let columnStyleRules = {};
-let defaultColumnStyles = [];
-let logLevelStyles = {};
-const logLevelToString = {
- 10: 'DBG',
- 20: 'INF',
- 21: 'OUT',
- 30: 'WRN',
- 40: 'ERR',
- 50: 'CRT',
- 70: 'FTL'
-};
+// Update web page CSS styles based on a pw-console color json log message.
function setCurrentTheme(newTheme) {
currentTheme = newTheme;
- defaultLogStyleRule = parseStyle(newTheme.default);
- document.querySelector('body').setAttribute('style', defaultLogStyleRule);
+ defaultLogStyleRule = parsePromptToolkitStyle(newTheme.default);
+ // Set body background color
+ // document.querySelector('body').setAttribute('style', defaultLogStyleRule);
+
// Apply default font styles to columns
let styles = [];
- Object.keys(newTheme).forEach(key => {
+ Object.keys(newTheme).forEach((key) => {
if (key.startsWith('log-table-column-')) {
styles.push(newTheme[key]);
}
if (key.startsWith('log-level-')) {
logLevelStyles[parseInt(key.replace('log-level-', ''))] =
- parseStyle(newTheme[key]);
+ parsePromptToolkitStyle(newTheme[key]);
}
});
defaultColumnStyles = styles;
}
-function parseStyle(rule) {
+// Convert prompt_toolkit color format strings to CSS.
+// 'bg:#BG-HEX #FG-HEX STYLE' where STYLE is either 'bold' or 'underline'
+function parsePromptToolkitStyle(rule) {
const ruleList = rule.split(' ');
- let outputStyle = ruleList.map(fragment => {
+ let outputStyle = ruleList.map((fragment) => {
if (fragment.startsWith('bg:')) {
- return `background-color: ${fragment.replace('bg:', '')}`
+ return `background-color: ${fragment.replace('bg:', '')}`;
} else if (fragment === 'bold') {
return `font-weight: bold`;
} else if (fragment === 'underline') {
@@ -150,32 +160,35 @@ function parseStyle(rule) {
return `color: ${fragment}`;
}
});
- return outputStyle.join(';')
+ return outputStyle.join(';');
}
+// Inject styled spans into the log message column values.
function applyStyling(data, applyColors = false) {
let colIndex = 0;
- Object.keys(data).forEach(key => {
+ Object.keys(data).forEach((key) => {
if (columnStyleRules[key] && typeof data[key] === 'string') {
- Object.keys(columnStyleRules[key]).forEach(token => {
+ Object.keys(columnStyleRules[key]).forEach((token) => {
data[key] = data[key].replaceAll(
- token,
- `<span
+ token,
+ `<span
style="${defaultLogStyleRule};${
- applyColors ? (defaultColumnStyles
- [colIndex % defaultColumnStyles.length]) :
- ''};${parseStyle(columnStyleRules[key][token])};">
+ applyColors
+ ? defaultColumnStyles[colIndex % defaultColumnStyles.length]
+ : ''
+ };${parsePromptToolkitStyle(columnStyleRules[key][token])};">
${token}
- </span>`);
+ </span>`,
+ );
});
} else if (key === 'fields') {
data[key] = applyStyling(data.fields, true);
}
if (applyColors) {
data[key] = `<span
- style="${
- parseStyle(
- defaultColumnStyles[colIndex % defaultColumnStyles.length])}">
+ style="${parsePromptToolkitStyle(
+ defaultColumnStyles[colIndex % defaultColumnStyles.length],
+ )}">
${data[key]}
</span>`;
}
@@ -184,78 +197,35 @@ function applyStyling(data, applyColors = false) {
return data;
}
-(function() {
-const container = document.querySelector('.log-container');
-const height = window.innerHeight - 50
-let follow = true;
-// Initialize our VirtualizedList
-var virtualizedList = new VirtualizedList(container, {
- height,
- rowCount: data.length,
- rowHeight: rowHeight,
- estimatedRowHeight: rowHeight,
- renderRow: (index) => {
- const element = document.createElement('div');
- element.classList.add('log-entry');
- element.setAttribute('style', `height: ${rowHeight}px;`);
- const logData = data[index];
- element.innerHTML = `
- <span class="time">${logData.time}</span>
- <span class="level" style="${logLevelStyles[logData.levelno] || ''}">${
- logLevelToString[logData.levelno]}</span>
- ${
- additionalHeaders
- .map(
- (key) => `
- <span class="${key}">${
- logData[key] || logData.fields[key] || ''}</span>
- `).join('\n')}
- <span class="msg">${logData.message}</span>
- `;
- return element;
- },
- initialIndex: 0,
- onScroll: (scrollTop, event) => {
- const offset =
- virtualizedList._sizeAndPositionManager.getUpdatedOffsetForIndex({
- containerSize: height,
- targetIndex: data.length - 1,
- });
-
- if (scrollTop < offset) {
- follow = false;
- } else {
- follow = true;
- }
- }
-});
-
-const port = getUrlHashParameter('ws')
-const hostname = location.hostname || '127.0.0.1';
-var ws = new WebSocket(`ws://${hostname}:${port}/`);
-ws.onmessage = function(event) {
- let dataObj;
- try {
- dataObj = JSON.parse(event.data);
- } catch (e) {
- }
- if (!dataObj)
- return;
-
- if (dataObj.__pw_console_colors) {
- const colors = dataObj.__pw_console_colors;
- setCurrentTheme(colors.classes);
- if (colors.column_values) {
- columnStyleRules = {...colors.column_values};
+// Connect to the pw-console websocket and start emitting logs.
+(function () {
+ const container = document.querySelector('.log-container');
+ const height = window.innerHeight - 50;
+ let follow = true;
+
+ const port = getUrlHashParameter('ws');
+ const hostname = location.hostname || '127.0.0.1';
+ var ws = new WebSocket(`ws://${hostname}:${port}/`);
+ ws.onmessage = function (event) {
+ let dataObj;
+ try {
+ dataObj = JSON.parse(event.data);
+ } catch (e) {
+ // empty
}
- } else {
- const currentData = {...dataObj, time: formatDate(new Date())};
- updateHeadersFromData(currentData);
- data.push(applyStyling(currentData));
- virtualizedList.setRowCount(data.length);
- if (follow) {
- virtualizedList.scrollToIndex(data.length - 1);
+ if (!dataObj) return;
+
+ if (dataObj.__pw_console_colors) {
+ // If this is a color theme message, update themes.
+ const colors = dataObj.__pw_console_colors;
+ setCurrentTheme(colors.classes);
+ if (colors.column_values) {
+ columnStyleRules = { ...colors.column_values };
+ }
+ } else {
+ // Normal log message.
+ const currentData = { ...dataObj, time: formatDate(new Date()) };
+ logSource.append_log(currentData);
}
- }
-};
+ };
})();