diff --git a/src/panels/config/logs/error-log-card.ts b/src/panels/config/logs/error-log-card.ts index d5a4c2c156..3008ec2484 100644 --- a/src/panels/config/logs/error-log-card.ts +++ b/src/panels/config/logs/error-log-card.ts @@ -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 { 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`
${entry}
`; + ? 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`
${entry}
`; - if (entry.includes("WARNING")) - return html`
${entry}
`; + if (entry.includes("WARNING")) + return html`
${entry}
`; - if ( - entry.includes("ERROR") || - entry.includes("FATAL") || - entry.includes("CRITICAL") - ) - return html`
${entry}
`; + if ( + entry.includes("ERROR") || + entry.includes("FATAL") || + entry.includes("CRITICAL") + ) + return html`
${entry}
`; - return html`
${entry}
`; - }) + return html`
${entry}
`; + }) : this.hass.localize("ui.panel.config.logs.no_errors"); } } diff --git a/src/panels/config/logs/ha-config-logs.ts b/src/panels/config/logs/ha-config-logs.ts index 13ef1137dc..48f90807eb 100644 --- a/src/panels/config/logs/ha-config-logs.ts +++ b/src/panels/config/logs/ha-config-logs.ts @@ -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` +
+ +
+ ` + : html` + + `; + return html` + ${search}
- - + +
`; @@ -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; } diff --git a/src/panels/config/logs/system-log-card.ts b/src/panels/config/logs/system-log-card.ts index 3baf39266a..373b797a06 100644 --- a/src/panels/config/logs/system-log-card.ts +++ b/src/panels/config/logs/system-log-card.ts @@ -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`
@@ -51,17 +89,21 @@ export class SystemLogCard extends LitElement { ${this.hass.localize("ui.panel.config.logs.no_issues")}
` - : this._items.map( + : filteredItems.length === 0 && this.filter + ? html`
+ ${this.hass.localize( + "ui.panel.config.logs.no_issues_search", + "term", + this.filter + )} +
` + : filteredItems.map( (item, idx) => html`
${item.message[0]}
- ${formatSystemLogTime( - item.timestamp, - this.hass!.locale - )} - – + ${this._timestamp(item)} – ${html`(${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``}
diff --git a/src/translations/en.json b/src/translations/en.json index ba969edb2a..2c64069e4c 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -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",