diff --git a/src/data/error_log.ts b/src/data/error_log.ts index b1bb4de170..a84925f70a 100644 --- a/src/data/error_log.ts +++ b/src/data/error_log.ts @@ -1,4 +1,9 @@ import { HomeAssistant } from "../types"; +export interface LogProvider { + key: string; + name: string; +} + export const fetchErrorLog = (hass: HomeAssistant) => hass.callApi("GET", "error_log"); diff --git a/src/panels/config/logs/error-log-card.ts b/src/panels/config/logs/error-log-card.ts index 50dc5db3a9..190fdd9437 100644 --- a/src/panels/config/logs/error-log-card.ts +++ b/src/panels/config/logs/error-log-card.ts @@ -1,11 +1,53 @@ import "@material/mwc-button"; +import "@material/mwc-list/mwc-list-item"; import { mdiRefresh } from "@mdi/js"; -import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; -import { property, state } from "lit/decorators"; +import { + css, + CSSResultGroup, + html, + LitElement, + PropertyValues, + TemplateResult, +} from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { isComponentLoaded } from "../../../common/config/is_component_loaded"; +import "../../../components/ha-alert"; +import "../../../components/ha-card"; import "../../../components/ha-icon-button"; -import { fetchErrorLog } from "../../../data/error_log"; +import "../../../components/ha-select"; +import { fetchErrorLog, LogProvider } from "../../../data/error_log"; +import { extractApiErrorMessage } from "../../../data/hassio/common"; +import { fetchHassioLogs } from "../../../data/hassio/supervisor"; import { HomeAssistant } from "../../../types"; +const logProviders: LogProvider[] = [ + { + key: "supervisor", + name: "Supervisor", + }, + { + key: "core", + name: "Home Assistant Core", + }, + { + key: "host", + name: "Host", + }, + { + key: "dns", + name: "DNS", + }, + { + key: "audio", + name: "Audio", + }, + { + key: "multicast", + name: "Multicast", + }, +]; + +@customElement("error-log-card") class ErrorLogCard extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -13,37 +55,69 @@ class ErrorLogCard extends LitElement { @state() private _isLogLoaded = false; - @state() private _errorHTML!: TemplateResult[] | string; + @state() private _logHTML!: TemplateResult[] | string; + + @state() private _error?: string; + + @state() private _selectedLogProvider?: string; protected render(): TemplateResult { return html`
- ${this._errorHTML + ${this._error + ? html`${this._error}` + : ""} + ${this._logHTML ? html` - -
${this._errorHTML}
+
+ ${this.hass.userData?.showAdvanced && + isComponentLoaded(this.hass, "hassio") + ? html` + + ${logProviders.map( + (provider) => html` + + ${provider.name} + + ` + )} + + ` + : ""} + +
+
${this._logHTML}
` - : html` - + : ""} + ${!this._logHTML + ? html` + ${this.hass.localize("ui.panel.config.logs.load_full_log")} - `} + ` + : ""}
`; } - protected firstUpdated(changedProps) { + protected firstUpdated(changedProps: PropertyValues) { super.firstUpdated(changedProps); if (this.hass?.config.safe_mode) { this.hass.loadFragmentTranslation("config"); - this._refreshErrorLog(); + this._refreshLogs(); } } @@ -51,52 +125,56 @@ class ErrorLogCard extends LitElement { super.updated(changedProps); if (changedProps.has("filter") && this._isLogLoaded) { - this._refreshErrorLog(); + this._refreshLogs(); } } - static get styles(): CSSResultGroup { - return css` - .error-log-intro { - text-align: center; - margin: 16px; - } + private async _setLogProvider(ev): Promise { + const provider = ev.target.value; + if (provider === this._selectedLogProvider) { + return; + } - ha-icon-button { - float: right; - } - - .error-log { - font-family: var(--code-font-family, monospace); - clear: both; - text-align: left; - padding-top: 12px; - } - - .error-log > div:hover { - background-color: var(--secondary-background-color); - } - - .error { - color: var(--error-color); - } - - .warning { - color: var(--warning-color); - } - - :host-context([style*="direction: rtl;"]) mwc-button { - direction: rtl; - } - `; + this._selectedLogProvider = provider; + this._refreshLogs(); } - private async _refreshErrorLog(): Promise { - this._errorHTML = this.hass.localize("ui.panel.config.logs.loading_log"); - const log = await fetchErrorLog(this.hass!); + private async _refresh(ev: CustomEvent): Promise { + const button = ev.currentTarget as any; + button.progress = true; + + await this._refreshLogs(); + button.progress = false; + } + + private async _refreshLogs(): Promise { + this._logHTML = this.hass.localize("ui.panel.config.logs.loading_log"); + let log: string; + + if (!this._selectedLogProvider && isComponentLoaded(this.hass, "hassio")) { + this._selectedLogProvider = "core"; + } + + if (this._selectedLogProvider) { + try { + log = await fetchHassioLogs(this.hass, this._selectedLogProvider); + } catch (err: any) { + this._error = this.hass.localize( + "ui.panel.config.logs.failed_get_logs", + "provider", + this._selectedLogProvider, + "error", + extractApiErrorMessage(err) + ); + return; + } + } else { + log = await fetchErrorLog(this.hass!); + } + this._isLogLoaded = true; - this._errorHTML = log + this._logHTML = log ? log .split("\n") .filter((entry) => { @@ -123,6 +201,61 @@ class ErrorLogCard extends LitElement { }) : this.hass.localize("ui.panel.config.logs.no_errors"); } + + static styles: CSSResultGroup = css` + .error-log-intro { + text-align: center; + margin: 16px; + } + + .header { + display: flex; + justify-content: space-between; + padding: 16px; + } + + ha-select { + display: block; + max-width: 500px; + width: 100%; + } + + ha-icon-button { + float: right; + } + + .error-log { + font-family: var(--code-font-family, monospace); + clear: both; + text-align: left; + padding-top: 12px; + } + + .error-log > div { + overflow: auto; + overflow-wrap: break-word; + } + + .error-log > div:hover { + background-color: var(--secondary-background-color); + } + + .error { + color: var(--error-color); + } + + .warning { + color: var(--warning-color); + } + + :host-context([style*="direction: rtl;"]) mwc-button { + direction: rtl; + } + `; } -customElements.define("error-log-card", ErrorLogCard); +declare global { + interface HTMLElementTagNameMap { + "error-log-card": ErrorLogCard; + } +} diff --git a/src/translations/en.json b/src/translations/en.json index 6dd926e542..8a6481ac1d 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1514,6 +1514,7 @@ "description": "View the Home Assistant logs", "details": "Log Details ({level})", "search": "Search logs", + "failed_get_logs": "Failed to get {provider} logs, {error}", "no_issues_search": "No issues found for search term ''{term}''", "load_full_log": "Load Full Home Assistant Log", "loading_log": "Loading error log…", @@ -1522,6 +1523,7 @@ "clear": "Clear", "refresh": "Refresh", "copy": "Copy log entry", + "log_provider": "Log Provider", "multiple_messages": "message first occurred at {time} and shows up {counter} times", "level": { "critical": "CRITICAL",