Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move download logs button, switch between raw and normal logs #22721

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion hassio/src/addon-view/log/hassio-addon-log-tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ class HassioAddonLogDashboard extends LitElement {
.localizeFunc=${this.supervisor.localize}
.header=${this.addon.name}
.provider=${this.addon.slug}
show
.filter=${this._filter}
>
</error-log-card>
Expand Down
231 changes: 114 additions & 117 deletions src/panels/config/logs/error-log-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
mdiRefresh,
mdiWrap,
mdiWrapDisabled,
mdiFolderTextOutline,
} from "@mdi/js";
import {
css,
Expand Down Expand Up @@ -58,7 +59,7 @@ import {
downloadFileSupported,
fileDownload,
} from "../../../util/file_download";
import type { HASSDomEvent } from "../../../common/dom/fire_event";
import { fireEvent, type HASSDomEvent } from "../../../common/dom/fire_event";
import type { ConnectionStatus } from "../../../data/connection-status";
import { atLeastVersion } from "../../../common/config/version";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
Expand All @@ -79,9 +80,10 @@ class ErrorLogCard extends LitElement {

@property() public header?: string;

@property() public provider!: string;
@property() public provider?: string;

@property({ type: Boolean, attribute: true }) public show = false;
@property({ attribute: "allow-switch", type: Boolean }) public allowSwitch =
false;

@query(".error-log") private _logElement?: HTMLElement;

Expand Down Expand Up @@ -130,26 +132,32 @@ class ErrorLogCard extends LitElement {

@state() private _wrapLines = true;

@state() private _downloadSupported;
@state() private _downloadSupported?: boolean;

@state() private _logsFileLink;
@state() private _logsFileLink?: string;

protected render(): TemplateResult {
const streaming =
this._streamSupported &&
this.provider &&
isComponentLoaded(this.hass, "hassio") &&
this._loadingState !== "loading";

const hasBoots = this._streamSupported && Array.isArray(this._boots);

const localize = this.localizeFunc || this.hass.localize;
return html`
<div class="error-log-intro">
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: nothing}
<ha-card outlined class=${classMap({ hidden: this.show === false })}>
<ha-card outlined>
<div class="header">
<h1 class="card-header">
${this.header || localize("ui.panel.config.logs.show_full_logs")}
</h1>
<div class="action-buttons">
${this._streamSupported &&
Array.isArray(this._boots) &&
this._showBootsSelect
${hasBoots && this._showBootsSelect
? html`
<ha-assist-chip
.title=${localize(
Expand All @@ -175,7 +183,7 @@ class ErrorLogCard extends LitElement {
id="boots-menu"
positioning="fixed"
>
${this._boots.map(
${this._boots!.map(
(boot) => html`
<ha-md-menu-item
.value=${boot}
Expand Down Expand Up @@ -232,27 +240,40 @@ class ErrorLogCard extends LitElement {
`ui.panel.config.logs.${this._wrapLines ? "full_width" : "wrap_lines"}`
)}
></ha-icon-button>
${!this._streamSupported || this._error
${!streaming || this._error
? html`<ha-icon-button
.path=${mdiRefresh}
@click=${this._loadLogs}
.label=${localize("ui.common.refresh")}
></ha-icon-button>`
: nothing}
${this._streamSupported && Array.isArray(this._boots)
${(this.allowSwitch && this.provider === "core") || hasBoots
? html`
<ha-button-menu @action=${this._handleOverflowAction}>
<ha-icon-button slot="trigger" .path=${mdiDotsVertical}>
</ha-icon-button>
<ha-list-item graphic="icon">
<ha-svg-icon
slot="graphic"
.path=${mdiFormatListNumbered}
></ha-svg-icon>
${localize(
`ui.panel.config.logs.${this._showBootsSelect ? "hide" : "show"}_haos_boots`
)}
</ha-list-item>
${this.allowSwitch && this.provider === "core"
? html`<ha-list-item graphic="icon">
<ha-svg-icon
slot="graphic"
.path=${mdiFolderTextOutline}
></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.logs.show_condensed_logs"
)}
</ha-list-item>`
: nothing}
${hasBoots
? html`<ha-list-item graphic="icon">
<ha-svg-icon
slot="graphic"
.path=${mdiFormatListNumbered}
></ha-svg-icon>
${localize(
`ui.panel.config.logs.${this._showBootsSelect ? "hide" : "show"}_haos_boots`
)}
</ha-list-item>`
: nothing}
</ha-button-menu>
`
: nothing}
Expand Down Expand Up @@ -305,48 +326,34 @@ class ErrorLogCard extends LitElement {
slot="trailingIcon"
></ha-svg-icon>
</ha-button>
${this._streamSupported &&
this._loadingState !== "loading" &&
this._boot === 0 &&
!this._error
${streaming && this._boot === 0 && !this._error
? html`<div class="live-indicator">
<ha-svg-icon path=${mdiCircle}></ha-svg-icon>
Live
</div>`
: nothing}
</ha-card>
${this.show === false
? html`
${this._downloadSupported
? html`
<ha-button outlined @click=${this._downloadLogs}>
<ha-svg-icon .path=${mdiDownload}></ha-svg-icon>
${localize("ui.panel.config.logs.download_logs")}
</ha-button>
`
: nothing}
<mwc-button raised @click=${this._showLogs}>
${localize("ui.panel.config.logs.load_logs")}
</mwc-button>
`
: nothing}
</div>
`;
}

public connectedCallback() {
super.connectedCallback();

if (this._streamSupported === undefined) {
this._streamSupported = atLeastVersion(
this.hass.config.version,
2024,
11
);
protected willUpdate(changedProps: PropertyValues) {
super.willUpdate(changedProps);
if (changedProps.has("provider")) {
this._boot = 0;
this._loadLogs();
}
if (this._downloadSupported === undefined && this.hass) {
this._downloadSupported = downloadFileSupported(this.hass);
if (this.hasUpdated) {
return;
}
this._streamSupported = atLeastVersion(this.hass.config.version, 2024, 11);
this._downloadSupported = downloadFileSupported(this.hass);
// just needs to be loaded once, because only the host endpoints provide boots information
this._loadBoots();

window.addEventListener("connection-status", this._handleConnectionStatus);

this.hass.loadFragmentTranslation("config");
}

protected firstUpdated(changedProps: PropertyValues) {
Expand All @@ -356,28 +363,11 @@ class ErrorLogCard extends LitElement {

this._scrolledToTopController.callback = this._handleTopScroll;
this._scrolledToTopController.observe(this._scrollTopMarkerElement!);

window.addEventListener("connection-status", this._handleConnectionStatus);

if (this.hass?.config.recovery_mode || this.show) {
this.hass.loadFragmentTranslation("config");
}

// just needs to be loaded once, because only the host endpoints provide boots information
this._loadBoots();
}

protected updated(changedProps) {
super.updated(changedProps);

if (
(changedProps.has("show") && this.show) ||
(changedProps.has("provider") && this.show)
) {
this._boot = 0;
this._loadLogs();
}

if (this._newLogsIndicator && this._scrolledToBottomController.value) {
this._newLogsIndicator = false;
}
Expand Down Expand Up @@ -411,7 +401,7 @@ class ErrorLogCard extends LitElement {
}

private async _downloadLogs(): Promise<void> {
if (this._streamSupported) {
if (this._streamSupported && this.provider) {
showDownloadLogsDialog(this, {
header: this.header,
provider: this.provider,
Expand All @@ -433,10 +423,6 @@ class ErrorLogCard extends LitElement {
}
}

private _showLogs(): void {
this.show = true;
}

private async _loadLogs(): Promise<void> {
this._error = undefined;
this._loadingState = "loading";
Expand All @@ -448,15 +434,16 @@ class ErrorLogCard extends LitElement {
try {
if (this._logStreamAborter) {
this._logStreamAborter.abort();
this._logStreamAborter = undefined;
}

this._logStreamAborter = new AbortController();

if (
this._streamSupported &&
isComponentLoaded(this.hass, "hassio") &&
this.provider
) {
this._logStreamAborter = new AbortController();

// check if there are any logs at all
const testResponse = await fetchHassioLogs(
this.hass,
Expand Down Expand Up @@ -599,60 +586,62 @@ class ErrorLogCard extends LitElement {
if (ev.detail === "disconnected" && this._logStreamAborter) {
this._logStreamAborter.abort();
}
if (ev.detail === "connected" && this.show) {
if (ev.detail === "connected") {
this._loadLogs();
}
};

private async _loadMoreLogs() {
if (
this._firstCursor &&
this._loadingPrevState !== "loading" &&
this._loadingState === "loaded" &&
this._logElement
!this._firstCursor ||
this._loadingPrevState === "loading" ||
this._loadingState !== "loaded" ||
!this._logElement ||
!this.provider
) {
const scrolledToBottom = this._scrolledToBottomController.value;
const scrollPositionFromBottom =
this._logElement.scrollHeight - this._logElement.scrollTop;
this._loadingPrevState = "loading";
const response = await fetchHassioLogs(
this.hass,
this.provider,
`entries=${this._firstCursor}:-100:100`,
this._boot
);
return;
}
const scrolledToBottom = this._scrolledToBottomController.value;
const scrollPositionFromBottom =
this._logElement.scrollHeight - this._logElement.scrollTop;
this._loadingPrevState = "loading";
const response = await fetchHassioLogs(
this.hass,
this.provider,
`entries=${this._firstCursor}:-100:100`,
this._boot
);

if (response.headers.has("X-First-Cursor")) {
if (this._firstCursor === response.headers.get("X-First-Cursor")!) {
this._loadingPrevState = "end";
return;
}
this._firstCursor = response.headers.get("X-First-Cursor")!;
if (response.headers.has("X-First-Cursor")) {
if (this._firstCursor === response.headers.get("X-First-Cursor")!) {
this._loadingPrevState = "end";
return;
}
this._firstCursor = response.headers.get("X-First-Cursor")!;
}

const body = await response.text();
const body = await response.text();

if (body) {
const lines = body
.split("\n")
.filter((line) => line.trim() !== "")
.reverse();
if (body) {
const lines = body
.split("\n")
.filter((line) => line.trim() !== "")
.reverse();

this._ansiToHtmlElement?.parseLinesToColoredPre(lines, true);
this._numberOfLines! += lines.length;
this._loadingPrevState = "loaded";
} else {
this._loadingPrevState = "end";
}
this._ansiToHtmlElement?.parseLinesToColoredPre(lines, true);
this._numberOfLines! += lines.length;
this._loadingPrevState = "loaded";
} else {
this._loadingPrevState = "end";
}

if (scrolledToBottom) {
this._scrollToBottom();
} else if (this._loadingPrevState !== "end" && this._logElement) {
window.requestAnimationFrame(() => {
this._logElement!.scrollTop =
this._logElement!.scrollHeight - scrollPositionFromBottom;
});
}
if (scrolledToBottom) {
this._scrollToBottom();
} else if (this._loadingPrevState !== "end" && this._logElement) {
window.requestAnimationFrame(() => {
this._logElement!.scrollTop =
this._logElement!.scrollHeight - scrollPositionFromBottom;
});
}
}

Expand Down Expand Up @@ -694,7 +683,15 @@ class ErrorLogCard extends LitElement {
}

private _handleOverflowAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
let index = ev.detail.index;
if (this.provider === "core") {
index--;
}
switch (index) {
case -1:
// @ts-ignore
fireEvent(this, "switch-log-view");
break;
case 0:
this._showBootsSelect = !this._showBootsSelect;
break;
Expand Down
Loading
Loading