Add filtering to system log card and error log card (#11166)

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
Kuba Wolanin 2022-02-03 11:15:27 +01:00 committed by GitHub
parent 0e41a408e7
commit 3aca67d511
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 150 additions and 39 deletions

View File

@ -9,6 +9,10 @@ import { HomeAssistant } from "../../../types";
class ErrorLogCard extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public filter = "";
@state() private _isLogLoaded = false;
@state() private _errorHTML!: TemplateResult[] | string;
protected render(): TemplateResult {
@ -43,6 +47,14 @@ class ErrorLogCard extends LitElement {
}
}
protected updated(changedProps) {
super.updated(changedProps);
if (changedProps.has("filter") && this._isLogLoaded) {
this._refreshErrorLog();
}
}
static get styles(): CSSResultGroup {
return css`
.error-log-intro {
@ -55,12 +67,16 @@ class ErrorLogCard extends LitElement {
}
.error-log {
@apply --paper-font-code)
clear: both;
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);
}
@ -74,24 +90,33 @@ class ErrorLogCard extends LitElement {
private async _refreshErrorLog(): Promise<void> {
this._errorHTML = this.hass.localize("ui.panel.config.logs.loading_log");
const log = await fetchErrorLog(this.hass!);
this._isLogLoaded = true;
this._errorHTML = log
? log.split("\n").map((entry) => {
if (entry.includes("INFO"))
return html`<div class="info">${entry}</div>`;
? log
.split("\n")
.filter((entry) => {
if (this.filter) {
return entry.toLowerCase().includes(this.filter.toLowerCase());
}
return entry;
})
.map((entry) => {
if (entry.includes("INFO"))
return html`<div class="info">${entry}</div>`;
if (entry.includes("WARNING"))
return html`<div class="warning">${entry}</div>`;
if (entry.includes("WARNING"))
return html`<div class="warning">${entry}</div>`;
if (
entry.includes("ERROR") ||
entry.includes("FATAL") ||
entry.includes("CRITICAL")
)
return html`<div class="error">${entry}</div>`;
if (
entry.includes("ERROR") ||
entry.includes("FATAL") ||
entry.includes("CRITICAL")
)
return html`<div class="error">${entry}</div>`;
return html`<div>${entry}</div>`;
})
return html`<div>${entry}</div>`;
})
: this.hass.localize("ui.panel.config.logs.no_errors");
}
}

View File

@ -1,9 +1,11 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import { customElement, property, query, state } from "lit/decorators";
import "../../../layouts/hass-tabs-subpage";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant, Route } from "../../../types";
import { configSections } from "../ha-panel-config";
import "../../../common/search/search-input";
import { extractSearchParam } from "../../../common/url/search-params";
import "./error-log-card";
import "./system-log-card";
import type { SystemLogCard } from "./system-log-card";
@ -20,6 +22,8 @@ export class HaConfigLogs extends LitElement {
@property() public route!: Route;
@state() private _filter = extractSearchParam("filter") ?? "";
@query("system-log-card", true) private systemLog?: SystemLogCard;
public connectedCallback() {
@ -29,7 +33,39 @@ export class HaConfigLogs extends LitElement {
}
}
private async _filterChanged(ev) {
this._filter = ev.detail.value;
}
protected render(): TemplateResult {
const search = this.narrow
? html`
<div slot="header">
<search-input
class="header"
no-label-float
no-underline
@value-changed=${this._filterChanged}
.hass=${this.hass}
.filter=${this._filter}
.label=${this.hass.localize("ui.panel.config.logs.search")}
></search-input>
</div>
`
: html`
<div class="search">
<search-input
autofocus
no-label-float
no-underline
@value-changed=${this._filterChanged}
.hass=${this.hass}
.filter=${this._filter}
.label=${this.hass.localize("ui.panel.config.logs.search")}
></search-input>
</div>
`;
return html`
<hass-tabs-subpage
.hass=${this.hass}
@ -38,9 +74,16 @@ export class HaConfigLogs extends LitElement {
.route=${this.route}
.tabs=${configSections.general}
>
${search}
<div class="content">
<system-log-card .hass=${this.hass}></system-log-card>
<error-log-card .hass=${this.hass}></error-log-card>
<system-log-card
.hass=${this.hass}
.filter=${this._filter}
></system-log-card>
<error-log-card
.hass=${this.hass}
.filter=${this._filter}
></error-log-card>
</div>
</hass-tabs-subpage>
`;
@ -56,6 +99,17 @@ export class HaConfigLogs extends LitElement {
-moz-user-select: initial;
}
.search {
padding: 0 16px;
background: var(--sidebar-background-color);
border-bottom: 1px solid var(--divider-color);
}
.search search-input {
position: relative;
top: 2px;
}
.content {
direction: ltr;
}

View File

@ -2,6 +2,7 @@ import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import "../../../components/buttons/ha-call-service-button";
import "../../../components/buttons/ha-progress-button";
import "../../../components/ha-card";
@ -22,6 +23,8 @@ import { formatSystemLogTime } from "./util";
export class SystemLogCard extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public filter = "";
public loaded = false;
@state() private _items?: LoggedError[];
@ -31,9 +34,44 @@ export class SystemLogCard extends LitElement {
this._items = await fetchSystemLog(this.hass!);
}
private _timestamp(item: LoggedError): string {
return formatSystemLogTime(item.timestamp, this.hass!.locale);
}
private _multipleMessages(item: LoggedError): string {
return this.hass.localize(
"ui.panel.config.logs.multiple_messages",
"time",
formatSystemLogTime(item.first_occurred, this.hass!.locale),
"counter",
item.count
);
}
private _getFilteredItems = memoizeOne(
(items: LoggedError[], filter: string) =>
items.filter((item: LoggedError) => {
if (filter) {
return (
item.message.some((message: string) =>
message.toLowerCase().includes(filter)
) ||
item.source[0].toLowerCase().includes(filter) ||
item.name.toLowerCase().includes(filter) ||
this._timestamp(item).toLowerCase().includes(filter) ||
this._multipleMessages(item).toLowerCase().includes(filter)
);
}
return item;
})
);
protected render(): TemplateResult {
const integrations = this._items
? this._items.map((item) => getLoggedErrorIntegration(item))
const filteredItems = this._items
? this._getFilteredItems(this._items, this.filter.toLowerCase())
: [];
const integrations = filteredItems.length
? filteredItems.map((item) => getLoggedErrorIntegration(item))
: [];
return html`
<div class="system-log-intro">
@ -51,17 +89,21 @@ export class SystemLogCard extends LitElement {
${this.hass.localize("ui.panel.config.logs.no_issues")}
</div>
`
: this._items.map(
: filteredItems.length === 0 && this.filter
? html`<div class="card-content">
${this.hass.localize(
"ui.panel.config.logs.no_issues_search",
"term",
this.filter
)}
</div>`
: filteredItems.map(
(item, idx) => html`
<paper-item @click=${this._openLog} .logItem=${item}>
<paper-item-body two-line>
<div class="row">${item.message[0]}</div>
<div secondary>
${formatSystemLogTime(
item.timestamp,
this.hass!.locale
)}
${this._timestamp(item)}
${html`(<span class=${item.level.toLowerCase()}
>${this.hass.localize(
"ui.panel.config.logs.level." +
@ -81,19 +123,7 @@ export class SystemLogCard extends LitElement {
}`
: item.source[0]}
${item.count > 1
? html`
-
${this.hass.localize(
"ui.panel.config.logs.multiple_messages",
"time",
formatSystemLogTime(
item.first_occurred,
this.hass!.locale
),
"counter",
item.count
)}
`
? html` - ${this._multipleMessages(item)} `
: html``}
</div>
</paper-item-body>

View File

@ -1344,6 +1344,8 @@
"caption": "Logs",
"description": "View the Home Assistant logs",
"details": "Log Details ({level})",
"search": "Search logs",
"no_issues_search": "No issues found for search term ''{term}''",
"load_full_log": "Load Full Home Assistant Log",
"loading_log": "Loading error log…",
"no_errors": "No errors have been reported",