From 8029c3d6726ab15ca1156b5373aa3c0023c0e1e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Wed, 29 Apr 2020 21:57:31 +0200 Subject: [PATCH] Adds more log types to the system tab (#5496) * Add more log types to the system tab * Fix lint issues * Fix more lint issues * Add loading screen while waiting for logs. * Only show log selector if advenced user. * Update hassio/src/system/hassio-supervisor-log.ts Co-Authored-By: Bram Kragten * Log adjustments * Remove the need for exported ANSI_HTML_STYLE * Add core as a log provider * Removed unneeded hints Co-authored-by: Bram Kragten --- hassio/src/addon-view/hassio-addon-logs.ts | 33 ++- hassio/src/ansi-to-html.ts | 223 ---------------- hassio/src/components/hassio-ansi-to-html.ts | 253 +++++++++++++++++++ hassio/src/system/hassio-supervisor-log.ts | 105 +++++++- src/data/hassio/supervisor.ts | 7 +- 5 files changed, 366 insertions(+), 255 deletions(-) delete mode 100644 hassio/src/ansi-to-html.ts create mode 100644 hassio/src/components/hassio-ansi-to-html.ts diff --git a/hassio/src/addon-view/hassio-addon-logs.ts b/hassio/src/addon-view/hassio-addon-logs.ts index b5ad7f38e9..37f0203c34 100644 --- a/hassio/src/addon-view/hassio-addon-logs.ts +++ b/hassio/src/addon-view/hassio-addon-logs.ts @@ -7,7 +7,6 @@ import { html, LitElement, property, - query, TemplateResult, } from "lit-element"; import { @@ -16,18 +15,18 @@ import { } from "../../../src/data/hassio/addon"; import { haStyle } from "../../../src/resources/styles"; import { HomeAssistant } from "../../../src/types"; -import { ANSI_HTML_STYLE, parseTextToColoredPre } from "../ansi-to-html"; +import "../components/hassio-ansi-to-html"; import { hassioStyle } from "../resources/hassio-style"; @customElement("hassio-addon-logs") class HassioAddonLogs extends LitElement { - @property() public hass!: HomeAssistant; + @property({ attribute: false }) public hass!: HomeAssistant; - @property() public addon!: HassioAddonDetails; + @property({ attribute: false }) public addon!: HassioAddonDetails; @property() private _error?: string; - @query("#content") private _logContent!: any; + @property() private _content?: string; public async connectedCallback(): Promise { super.connectedCallback(); @@ -38,7 +37,13 @@ class HassioAddonLogs extends LitElement { return html` ${this._error ? html`
${this._error}
` : ""} -
+
+ ${this._content + ? html`` + : html``} +
Refresh
@@ -50,17 +55,11 @@ class HassioAddonLogs extends LitElement { return [ haStyle, hassioStyle, - ANSI_HTML_STYLE, css` :host, paper-card { display: block; } - pre { - overflow-x: auto; - white-space: pre-wrap; - overflow-wrap: break-word; - } .errors { color: var(--google-red-500); margin-bottom: 16px; @@ -72,13 +71,11 @@ class HassioAddonLogs extends LitElement { private async _loadData(): Promise { this._error = undefined; try { - const content = await fetchHassioAddonLogs(this.hass, this.addon.slug); - while (this._logContent.lastChild) { - this._logContent.removeChild(this._logContent.lastChild as Node); - } - this._logContent.appendChild(parseTextToColoredPre(content)); + this._content = await fetchHassioAddonLogs(this.hass, this.addon.slug); } catch (err) { - this._error = `Failed to get addon logs, ${err.body?.message || err}`; + this._error = `Failed to get supervisor logs, ${ + err.body?.message || err + }`; } } diff --git a/hassio/src/ansi-to-html.ts b/hassio/src/ansi-to-html.ts deleted file mode 100644 index 0601f5ba5a..0000000000 --- a/hassio/src/ansi-to-html.ts +++ /dev/null @@ -1,223 +0,0 @@ -import { css } from "lit-element"; - -interface State { - bold: boolean; - italic: boolean; - underline: boolean; - strikethrough: boolean; - foregroundColor: null | string; - backgroundColor: null | string; -} - -export const ANSI_HTML_STYLE = css` - .bold { - font-weight: bold; - } - .italic { - font-style: italic; - } - .underline { - text-decoration: underline; - } - .strikethrough { - text-decoration: line-through; - } - .underline.strikethrough { - text-decoration: underline line-through; - } - .fg-red { - color: rgb(222, 56, 43); - } - .fg-green { - color: rgb(57, 181, 74); - } - .fg-yellow { - color: rgb(255, 199, 6); - } - .fg-blue { - color: rgb(0, 111, 184); - } - .fg-magenta { - color: rgb(118, 38, 113); - } - .fg-cyan { - color: rgb(44, 181, 233); - } - .fg-white { - color: rgb(204, 204, 204); - } - .bg-black { - background-color: rgb(0, 0, 0); - } - .bg-red { - background-color: rgb(222, 56, 43); - } - .bg-green { - background-color: rgb(57, 181, 74); - } - .bg-yellow { - background-color: rgb(255, 199, 6); - } - .bg-blue { - background-color: rgb(0, 111, 184); - } - .bg-magenta { - background-color: rgb(118, 38, 113); - } - .bg-cyan { - background-color: rgb(44, 181, 233); - } - .bg-white { - background-color: rgb(204, 204, 204); - } -`; - -export function parseTextToColoredPre(text) { - const pre = document.createElement("pre"); - const re = /\033(?:\[(.*?)[@-~]|\].*?(?:\007|\033\\))/g; - let i = 0; - - const state: State = { - bold: false, - italic: false, - underline: false, - strikethrough: false, - foregroundColor: null, - backgroundColor: null, - }; - - const addSpan = (content) => { - const span = document.createElement("span"); - if (state.bold) { - span.classList.add("bold"); - } - if (state.italic) { - span.classList.add("italic"); - } - if (state.underline) { - span.classList.add("underline"); - } - if (state.strikethrough) { - span.classList.add("strikethrough"); - } - if (state.foregroundColor !== null) { - span.classList.add(`fg-${state.foregroundColor}`); - } - if (state.backgroundColor !== null) { - span.classList.add(`bg-${state.backgroundColor}`); - } - span.appendChild(document.createTextNode(content)); - pre.appendChild(span); - }; - - /* eslint-disable no-cond-assign */ - let match; - // eslint-disable-next-line - while ((match = re.exec(text)) !== null) { - const j = match!.index; - addSpan(text.substring(i, j)); - i = j + match[0].length; - - if (match[1] === undefined) { - continue; - } - - match[1].split(";").forEach((colorCode: string) => { - switch (parseInt(colorCode, 10)) { - case 0: - // reset - state.bold = false; - state.italic = false; - state.underline = false; - state.strikethrough = false; - state.foregroundColor = null; - state.backgroundColor = null; - break; - case 1: - state.bold = true; - break; - case 3: - state.italic = true; - break; - case 4: - state.underline = true; - break; - case 9: - state.strikethrough = true; - break; - case 22: - state.bold = false; - break; - case 23: - state.italic = false; - break; - case 24: - state.underline = false; - break; - case 29: - state.strikethrough = false; - break; - case 30: - // foreground black - state.foregroundColor = null; - break; - case 31: - state.foregroundColor = "red"; - break; - case 32: - state.foregroundColor = "green"; - break; - case 33: - state.foregroundColor = "yellow"; - break; - case 34: - state.foregroundColor = "blue"; - break; - case 35: - state.foregroundColor = "magenta"; - break; - case 36: - state.foregroundColor = "cyan"; - break; - case 37: - state.foregroundColor = "white"; - break; - case 39: - // foreground reset - state.foregroundColor = null; - break; - case 40: - state.backgroundColor = "black"; - break; - case 41: - state.backgroundColor = "red"; - break; - case 42: - state.backgroundColor = "green"; - break; - case 43: - state.backgroundColor = "yellow"; - break; - case 44: - state.backgroundColor = "blue"; - break; - case 45: - state.backgroundColor = "magenta"; - break; - case 46: - state.backgroundColor = "cyan"; - break; - case 47: - state.backgroundColor = "white"; - break; - case 49: - // background reset - state.backgroundColor = null; - break; - } - }); - } - addSpan(text.substring(i)); - - return pre; -} diff --git a/hassio/src/components/hassio-ansi-to-html.ts b/hassio/src/components/hassio-ansi-to-html.ts new file mode 100644 index 0000000000..b553367b8a --- /dev/null +++ b/hassio/src/components/hassio-ansi-to-html.ts @@ -0,0 +1,253 @@ +import { + css, + CSSResult, + customElement, + html, + LitElement, + property, + TemplateResult, +} from "lit-element"; + +interface State { + bold: boolean; + italic: boolean; + underline: boolean; + strikethrough: boolean; + foregroundColor: null | string; + backgroundColor: null | string; +} + +@customElement("hassio-ansi-to-html") +class HassioAnsiToHtml extends LitElement { + @property() public content!: string; + + public render(): TemplateResult | void { + return html`${this._parseTextToColoredPre(this.content)}`; + } + + static get styles(): CSSResult { + return css` + pre { + overflow-x: auto; + white-space: pre-wrap; + overflow-wrap: break-word; + } + .bold { + font-weight: bold; + } + .italic { + font-style: italic; + } + .underline { + text-decoration: underline; + } + .strikethrough { + text-decoration: line-through; + } + .underline.strikethrough { + text-decoration: underline line-through; + } + .fg-red { + color: rgb(222, 56, 43); + } + .fg-green { + color: rgb(57, 181, 74); + } + .fg-yellow { + color: rgb(255, 199, 6); + } + .fg-blue { + color: rgb(0, 111, 184); + } + .fg-magenta { + color: rgb(118, 38, 113); + } + .fg-cyan { + color: rgb(44, 181, 233); + } + .fg-white { + color: rgb(204, 204, 204); + } + .bg-black { + background-color: rgb(0, 0, 0); + } + .bg-red { + background-color: rgb(222, 56, 43); + } + .bg-green { + background-color: rgb(57, 181, 74); + } + .bg-yellow { + background-color: rgb(255, 199, 6); + } + .bg-blue { + background-color: rgb(0, 111, 184); + } + .bg-magenta { + background-color: rgb(118, 38, 113); + } + .bg-cyan { + background-color: rgb(44, 181, 233); + } + .bg-white { + background-color: rgb(204, 204, 204); + } + `; + } + + private _parseTextToColoredPre(text) { + const pre = document.createElement("pre"); + const re = /\033(?:\[(.*?)[@-~]|\].*?(?:\007|\033\\))/g; + let i = 0; + + const state: State = { + bold: false, + italic: false, + underline: false, + strikethrough: false, + foregroundColor: null, + backgroundColor: null, + }; + + const addSpan = (content) => { + const span = document.createElement("span"); + if (state.bold) { + span.classList.add("bold"); + } + if (state.italic) { + span.classList.add("italic"); + } + if (state.underline) { + span.classList.add("underline"); + } + if (state.strikethrough) { + span.classList.add("strikethrough"); + } + if (state.foregroundColor !== null) { + span.classList.add(`fg-${state.foregroundColor}`); + } + if (state.backgroundColor !== null) { + span.classList.add(`bg-${state.backgroundColor}`); + } + span.appendChild(document.createTextNode(content)); + pre.appendChild(span); + }; + + /* eslint-disable no-cond-assign */ + let match; + // eslint-disable-next-line + while ((match = re.exec(text)) !== null) { + const j = match!.index; + addSpan(text.substring(i, j)); + i = j + match[0].length; + + if (match[1] === undefined) { + continue; + } + + match[1].split(";").forEach((colorCode: string) => { + switch (parseInt(colorCode, 10)) { + case 0: + // reset + state.bold = false; + state.italic = false; + state.underline = false; + state.strikethrough = false; + state.foregroundColor = null; + state.backgroundColor = null; + break; + case 1: + state.bold = true; + break; + case 3: + state.italic = true; + break; + case 4: + state.underline = true; + break; + case 9: + state.strikethrough = true; + break; + case 22: + state.bold = false; + break; + case 23: + state.italic = false; + break; + case 24: + state.underline = false; + break; + case 29: + state.strikethrough = false; + break; + case 30: + // foreground black + state.foregroundColor = null; + break; + case 31: + state.foregroundColor = "red"; + break; + case 32: + state.foregroundColor = "green"; + break; + case 33: + state.foregroundColor = "yellow"; + break; + case 34: + state.foregroundColor = "blue"; + break; + case 35: + state.foregroundColor = "magenta"; + break; + case 36: + state.foregroundColor = "cyan"; + break; + case 37: + state.foregroundColor = "white"; + break; + case 39: + // foreground reset + state.foregroundColor = null; + break; + case 40: + state.backgroundColor = "black"; + break; + case 41: + state.backgroundColor = "red"; + break; + case 42: + state.backgroundColor = "green"; + break; + case 43: + state.backgroundColor = "yellow"; + break; + case 44: + state.backgroundColor = "blue"; + break; + case 45: + state.backgroundColor = "magenta"; + break; + case 46: + state.backgroundColor = "cyan"; + break; + case 47: + state.backgroundColor = "white"; + break; + case 49: + // background reset + state.backgroundColor = null; + break; + } + }); + } + addSpan(text.substring(i)); + + return pre; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hassio-ansi-to-html": HassioAnsiToHtml; + } +} diff --git a/hassio/src/system/hassio-supervisor-log.ts b/hassio/src/system/hassio-supervisor-log.ts index 654775f94d..ff5b426f0e 100644 --- a/hassio/src/system/hassio-supervisor-log.ts +++ b/hassio/src/system/hassio-supervisor-log.ts @@ -1,4 +1,7 @@ import "@material/mwc-button"; +import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; +import "@polymer/paper-item/paper-item"; +import "@polymer/paper-listbox/paper-listbox"; import "@polymer/paper-card/paper-card"; import { css, @@ -7,22 +10,58 @@ import { html, LitElement, property, - query, TemplateResult, } from "lit-element"; -import { fetchSupervisorLogs } from "../../../src/data/hassio/supervisor"; import { haStyle } from "../../../src/resources/styles"; import { HomeAssistant } from "../../../src/types"; -import { ANSI_HTML_STYLE, parseTextToColoredPre } from "../ansi-to-html"; + +import { fetchHassioLogs } from "../../../src/data/hassio/supervisor"; + +import "../components/hassio-ansi-to-html"; import { hassioStyle } from "../resources/hassio-style"; +import "../../../src/layouts/loading-screen"; + +interface LogProvider { + key: string; + name: string; +} + +const logProviders: LogProvider[] = [ + { + key: "supervisor", + name: "Supervisor", + }, + { + key: "core", + name: "Core", + }, + { + key: "host", + name: "Host", + }, + { + key: "dns", + name: "DNS", + }, + { + key: "audio", + name: "Audio", + }, + { + key: "multicast", + name: "Multicast", + }, +]; @customElement("hassio-supervisor-log") class HassioSupervisorLog extends LitElement { - @property() public hass!: HomeAssistant; + @property({ attribute: false }) public hass!: HomeAssistant; @property() private _error?: string; - @query("#content") private _logContent!: HTMLDivElement; + @property() private _selectedLogProvider = "supervisor"; + + @property() private _content?: string; public async connectedCallback(): Promise { super.connectedCallback(); @@ -33,7 +72,36 @@ class HassioSupervisorLog extends LitElement { return html` ${this._error ? html`
${this._error}
` : ""} -
+ ${this.hass.userData?.showAdvanced + ? html` + + + ${logProviders.map((provider) => { + return html` + ${provider.name} + `; + })} + + + ` + : ""} + +
+ ${this._content + ? html`` + : html``} +
Refresh
@@ -45,7 +113,6 @@ class HassioSupervisorLog extends LitElement { return [ haStyle, hassioStyle, - ANSI_HTML_STYLE, css` paper-card { width: 100%; @@ -53,22 +120,36 @@ class HassioSupervisorLog extends LitElement { pre { white-space: pre-wrap; } + paper-dropdown-menu { + padding: 0 2%; + width: 96%; + } .errors { color: var(--google-red-500); margin-bottom: 16px; } + .card-content { + padding-top: 0px; + } `, ]; } + private async _setLogProvider(ev): Promise { + const provider = ev.detail.item.getAttribute("provider"); + this._selectedLogProvider = provider; + await this._loadData(); + } + private async _loadData(): Promise { this._error = undefined; + this._content = undefined; + try { - const content = await fetchSupervisorLogs(this.hass); - while (this._logContent.lastChild) { - this._logContent.removeChild(this._logContent.lastChild as Node); - } - this._logContent.appendChild(parseTextToColoredPre(content)); + this._content = await fetchHassioLogs( + this.hass, + this._selectedLogProvider + ); } catch (err) { this._error = `Failed to get supervisor logs, ${ err.body?.message || err diff --git a/src/data/hassio/supervisor.ts b/src/data/hassio/supervisor.ts index ccd5ef4d52..8788b88f14 100644 --- a/src/data/hassio/supervisor.ts +++ b/src/data/hassio/supervisor.ts @@ -37,8 +37,11 @@ export const fetchHassioSupervisorInfo = async (hass: HomeAssistant) => { ); }; -export const fetchSupervisorLogs = async (hass: HomeAssistant) => { - return hass.callApi("GET", "hassio/supervisor/logs"); +export const fetchHassioLogs = async ( + hass: HomeAssistant, + provider: string +) => { + return hass.callApi("GET", `hassio/${provider}/logs`); }; export const createHassioSession = async (hass: HomeAssistant) => {