Skip to content

Commit

Permalink
move download logs button, switch between raw and normal logs (#22721)
Browse files Browse the repository at this point in the history
  • Loading branch information
bramkragten committed Nov 7, 2024
1 parent 125ad9c commit 3873927
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 143 deletions.
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

0 comments on commit 3873927

Please sign in to comment.