Add Supervisor logs to core page (#12410)

This commit is contained in:
Zack Barett 2022-04-25 10:35:03 -05:00 committed by GitHub
parent 8e55c83996
commit 3f04abfa9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 195 additions and 55 deletions

View File

@ -1,4 +1,9 @@
import { HomeAssistant } from "../types";
export interface LogProvider {
key: string;
name: string;
}
export const fetchErrorLog = (hass: HomeAssistant) =>
hass.callApi<string>("GET", "error_log");

View File

@ -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`
<div class="error-log-intro">
${this._errorHTML
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: ""}
${this._logHTML
? html`
<ha-card>
<ha-icon-button
.path=${mdiRefresh}
@click=${this._refreshErrorLog}
.label=${this.hass.localize("ui.common.refresh")}
></ha-icon-button>
<div class="card-content error-log">${this._errorHTML}</div>
<div class="header">
${this.hass.userData?.showAdvanced &&
isComponentLoaded(this.hass, "hassio")
? html`
<ha-select
.label=${this.hass.localize(
"ui.panel.config.logs.log_provider"
)}
@selected=${this._setLogProvider}
.value=${this._selectedLogProvider}
>
${logProviders.map(
(provider) => html`
<mwc-list-item .value=${provider.key}>
${provider.name}
</mwc-list-item>
`
)}
</ha-select>
`
: ""}
<ha-icon-button
.path=${mdiRefresh}
@click=${this._refresh}
.label=${this.hass.localize("ui.common.refresh")}
></ha-icon-button>
</div>
<div class="card-content error-log">${this._logHTML}</div>
</ha-card>
`
: html`
<mwc-button raised @click=${this._refreshErrorLog}>
: ""}
${!this._logHTML
? html`
<mwc-button raised @click=${this._refreshLogs}>
${this.hass.localize("ui.panel.config.logs.load_full_log")}
</mwc-button>
`}
`
: ""}
</div>
`;
}
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<void> {
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<void> {
this._errorHTML = this.hass.localize("ui.panel.config.logs.loading_log");
const log = await fetchErrorLog(this.hass!);
private async _refresh(ev: CustomEvent): Promise<void> {
const button = ev.currentTarget as any;
button.progress = true;
await this._refreshLogs();
button.progress = false;
}
private async _refreshLogs(): Promise<void> {
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;
}
}

View File

@ -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",