mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-22 16:56:35 +00:00
Add filtering to system log card and error log card (#11166)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
0e41a408e7
commit
3aca67d511
@ -9,6 +9,10 @@ import { HomeAssistant } from "../../../types";
|
|||||||
class ErrorLogCard extends LitElement {
|
class ErrorLogCard extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public filter = "";
|
||||||
|
|
||||||
|
@state() private _isLogLoaded = false;
|
||||||
|
|
||||||
@state() private _errorHTML!: TemplateResult[] | string;
|
@state() private _errorHTML!: TemplateResult[] | string;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
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 {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
.error-log-intro {
|
.error-log-intro {
|
||||||
@ -55,12 +67,16 @@ class ErrorLogCard extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.error-log {
|
.error-log {
|
||||||
@apply --paper-font-code)
|
font-family: var(--code-font-family, monospace);
|
||||||
clear: both;
|
clear: both;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding-top: 12px;
|
padding-top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error-log > div:hover {
|
||||||
|
background-color: var(--secondary-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
color: var(--error-color);
|
color: var(--error-color);
|
||||||
}
|
}
|
||||||
@ -74,24 +90,33 @@ class ErrorLogCard extends LitElement {
|
|||||||
private async _refreshErrorLog(): Promise<void> {
|
private async _refreshErrorLog(): Promise<void> {
|
||||||
this._errorHTML = this.hass.localize("ui.panel.config.logs.loading_log");
|
this._errorHTML = this.hass.localize("ui.panel.config.logs.loading_log");
|
||||||
const log = await fetchErrorLog(this.hass!);
|
const log = await fetchErrorLog(this.hass!);
|
||||||
|
this._isLogLoaded = true;
|
||||||
|
|
||||||
this._errorHTML = log
|
this._errorHTML = log
|
||||||
? log.split("\n").map((entry) => {
|
? log
|
||||||
if (entry.includes("INFO"))
|
.split("\n")
|
||||||
return html`<div class="info">${entry}</div>`;
|
.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"))
|
if (entry.includes("WARNING"))
|
||||||
return html`<div class="warning">${entry}</div>`;
|
return html`<div class="warning">${entry}</div>`;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
entry.includes("ERROR") ||
|
entry.includes("ERROR") ||
|
||||||
entry.includes("FATAL") ||
|
entry.includes("FATAL") ||
|
||||||
entry.includes("CRITICAL")
|
entry.includes("CRITICAL")
|
||||||
)
|
)
|
||||||
return html`<div class="error">${entry}</div>`;
|
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");
|
: this.hass.localize("ui.panel.config.logs.no_errors");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
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 "../../../layouts/hass-tabs-subpage";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import { HomeAssistant, Route } from "../../../types";
|
import { HomeAssistant, Route } from "../../../types";
|
||||||
import { configSections } from "../ha-panel-config";
|
import { configSections } from "../ha-panel-config";
|
||||||
|
import "../../../common/search/search-input";
|
||||||
|
import { extractSearchParam } from "../../../common/url/search-params";
|
||||||
import "./error-log-card";
|
import "./error-log-card";
|
||||||
import "./system-log-card";
|
import "./system-log-card";
|
||||||
import type { SystemLogCard } from "./system-log-card";
|
import type { SystemLogCard } from "./system-log-card";
|
||||||
@ -20,6 +22,8 @@ export class HaConfigLogs extends LitElement {
|
|||||||
|
|
||||||
@property() public route!: Route;
|
@property() public route!: Route;
|
||||||
|
|
||||||
|
@state() private _filter = extractSearchParam("filter") ?? "";
|
||||||
|
|
||||||
@query("system-log-card", true) private systemLog?: SystemLogCard;
|
@query("system-log-card", true) private systemLog?: SystemLogCard;
|
||||||
|
|
||||||
public connectedCallback() {
|
public connectedCallback() {
|
||||||
@ -29,7 +33,39 @@ export class HaConfigLogs extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _filterChanged(ev) {
|
||||||
|
this._filter = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
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`
|
return html`
|
||||||
<hass-tabs-subpage
|
<hass-tabs-subpage
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@ -38,9 +74,16 @@ export class HaConfigLogs extends LitElement {
|
|||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.tabs=${configSections.general}
|
.tabs=${configSections.general}
|
||||||
>
|
>
|
||||||
|
${search}
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<system-log-card .hass=${this.hass}></system-log-card>
|
<system-log-card
|
||||||
<error-log-card .hass=${this.hass}></error-log-card>
|
.hass=${this.hass}
|
||||||
|
.filter=${this._filter}
|
||||||
|
></system-log-card>
|
||||||
|
<error-log-card
|
||||||
|
.hass=${this.hass}
|
||||||
|
.filter=${this._filter}
|
||||||
|
></error-log-card>
|
||||||
</div>
|
</div>
|
||||||
</hass-tabs-subpage>
|
</hass-tabs-subpage>
|
||||||
`;
|
`;
|
||||||
@ -56,6 +99,17 @@ export class HaConfigLogs extends LitElement {
|
|||||||
-moz-user-select: initial;
|
-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 {
|
.content {
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import "@polymer/paper-item/paper-item";
|
|||||||
import "@polymer/paper-item/paper-item-body";
|
import "@polymer/paper-item/paper-item-body";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
import "../../../components/buttons/ha-call-service-button";
|
import "../../../components/buttons/ha-call-service-button";
|
||||||
import "../../../components/buttons/ha-progress-button";
|
import "../../../components/buttons/ha-progress-button";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
@ -22,6 +23,8 @@ import { formatSystemLogTime } from "./util";
|
|||||||
export class SystemLogCard extends LitElement {
|
export class SystemLogCard extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public filter = "";
|
||||||
|
|
||||||
public loaded = false;
|
public loaded = false;
|
||||||
|
|
||||||
@state() private _items?: LoggedError[];
|
@state() private _items?: LoggedError[];
|
||||||
@ -31,9 +34,44 @@ export class SystemLogCard extends LitElement {
|
|||||||
this._items = await fetchSystemLog(this.hass!);
|
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 {
|
protected render(): TemplateResult {
|
||||||
const integrations = this._items
|
const filteredItems = this._items
|
||||||
? this._items.map((item) => getLoggedErrorIntegration(item))
|
? this._getFilteredItems(this._items, this.filter.toLowerCase())
|
||||||
|
: [];
|
||||||
|
const integrations = filteredItems.length
|
||||||
|
? filteredItems.map((item) => getLoggedErrorIntegration(item))
|
||||||
: [];
|
: [];
|
||||||
return html`
|
return html`
|
||||||
<div class="system-log-intro">
|
<div class="system-log-intro">
|
||||||
@ -51,17 +89,21 @@ export class SystemLogCard extends LitElement {
|
|||||||
${this.hass.localize("ui.panel.config.logs.no_issues")}
|
${this.hass.localize("ui.panel.config.logs.no_issues")}
|
||||||
</div>
|
</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`
|
(item, idx) => html`
|
||||||
<paper-item @click=${this._openLog} .logItem=${item}>
|
<paper-item @click=${this._openLog} .logItem=${item}>
|
||||||
<paper-item-body two-line>
|
<paper-item-body two-line>
|
||||||
<div class="row">${item.message[0]}</div>
|
<div class="row">${item.message[0]}</div>
|
||||||
<div secondary>
|
<div secondary>
|
||||||
${formatSystemLogTime(
|
${this._timestamp(item)} –
|
||||||
item.timestamp,
|
|
||||||
this.hass!.locale
|
|
||||||
)}
|
|
||||||
–
|
|
||||||
${html`(<span class=${item.level.toLowerCase()}
|
${html`(<span class=${item.level.toLowerCase()}
|
||||||
>${this.hass.localize(
|
>${this.hass.localize(
|
||||||
"ui.panel.config.logs.level." +
|
"ui.panel.config.logs.level." +
|
||||||
@ -81,19 +123,7 @@ export class SystemLogCard extends LitElement {
|
|||||||
}`
|
}`
|
||||||
: item.source[0]}
|
: item.source[0]}
|
||||||
${item.count > 1
|
${item.count > 1
|
||||||
? html`
|
? html` - ${this._multipleMessages(item)} `
|
||||||
-
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.logs.multiple_messages",
|
|
||||||
"time",
|
|
||||||
formatSystemLogTime(
|
|
||||||
item.first_occurred,
|
|
||||||
this.hass!.locale
|
|
||||||
),
|
|
||||||
"counter",
|
|
||||||
item.count
|
|
||||||
)}
|
|
||||||
`
|
|
||||||
: html``}
|
: html``}
|
||||||
</div>
|
</div>
|
||||||
</paper-item-body>
|
</paper-item-body>
|
||||||
|
@ -1344,6 +1344,8 @@
|
|||||||
"caption": "Logs",
|
"caption": "Logs",
|
||||||
"description": "View the Home Assistant logs",
|
"description": "View the Home Assistant logs",
|
||||||
"details": "Log Details ({level})",
|
"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",
|
"load_full_log": "Load Full Home Assistant Log",
|
||||||
"loading_log": "Loading error log…",
|
"loading_log": "Loading error log…",
|
||||||
"no_errors": "No errors have been reported",
|
"no_errors": "No errors have been reported",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user