Skip to content

Commit

Permalink
Improve log settings experience
Browse files Browse the repository at this point in the history
Switch from the OverflowMenu to a custom Popover to improve the user
experience of toggling log levels. The OverflowMenu approach closes
the menu on each change requiring multiple additional clicks for each
log level the user wishes to toggle. With the Popover we can now keep
the menu open until explicitly dismissed, meaning users can much more
easily make multiple changes.

Also display a notice at the start of the logs if lines are hidden
due to the currently selected log levels.
  • Loading branch information
AlanGreene authored and tekton-robot committed Jan 7, 2025
1 parent ff551f1 commit 158cc5b
Show file tree
Hide file tree
Showing 24 changed files with 269 additions and 120 deletions.
4 changes: 2 additions & 2 deletions docs/logs.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The Tekton Dashboard log viewer supports ANSI colour codes and text styles, and

## Toolbar

The toolbar diplayed in the log viewer includes a number of additional features, including:
The toolbar displayed in the log viewer includes a number of additional features, including:

- maximize: increase the area available to the log viewer by hiding the task list and run header. This allows the user to eliminate distractions from other parts of the app and focus on the log content.
- open in new window: open the raw logs in a separate browser window. This provides an unmodified and unprocessed view of the logs, without any of the added features provided by the log viewer.
Expand All @@ -26,7 +26,7 @@ The toolbar diplayed in the log viewer includes a number of additional features,

The Dashboard will always request logs with timestamps from the Kubernetes pod logs API, and show / hide the timestamps in the log viewer based on the user's preference. This can be toggled from the settings menu in the toolbar at the top of the log viewer.

The timestamps are localised based on users' browser settings, with the raw timestamp value received from the API provided as a tooltip on hover.
The timestamps are localised based on the user's browser settings, with the raw timestamp value received from the API provided as a tooltip on hover.

In releases prior to Tekton Dashboard v0.54, the timestamp preference was found on the Settings page, and governed whether or not timestamps were requested from the Kubernetes API server.

Expand Down
57 changes: 25 additions & 32 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"test": "vitest"
},
"dependencies": {
"@carbon/react": "^1.72.0",
"@carbon/react": "^1.73.0",
"@codemirror/legacy-modes": "^6.4.2",
"@tanstack/react-query": "^4.36.1",
"@tektoncd/dashboard-components": "*",
Expand Down
105 changes: 82 additions & 23 deletions packages/components/src/components/Log/Log.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2019-2024 The Tekton Authors
Copyright 2019-2025 The Tekton Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Expand All @@ -14,9 +14,9 @@ limitations under the License.
import { Component, createRef } from 'react';
import { Button, PrefixContext, SkeletonText } from '@carbon/react';
import { FixedSizeList as List } from 'react-window';
import { injectIntl } from 'react-intl';
import { injectIntl, useIntl } from 'react-intl';
import { getStepStatusReason, isRunning } from '@tektoncd/dashboard-utils';
import { DownToBottom, UpToTop } from '@carbon/react/icons';
import { DownToBottom, Information, UpToTop } from '@carbon/react/icons';

import {
hasElementPositiveVerticalScrollBottom,
Expand All @@ -33,6 +33,52 @@ const defaultHeight = itemSize * 100 + itemSize / 2;
const logFormatRegex =
/^((?<timestamp>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3,9}Z)\s?)?(::(?<level>error|warning|info|notice|debug)::)?(?<message>.*)?$/s;

function LogsFilteredNotification({ displayedLogLines, totalLogLines }) {
const intl = useIntl();

if (displayedLogLines === totalLogLines) {
return null;
}

if (displayedLogLines === 0) {
return (
<span className="tkn--log-filtered">
<Information />
{intl.formatMessage({
id: 'dashboard.logs.hidden.all',
defaultMessage: 'All lines hidden due to selected log levels'
})}
</span>
);
}

const hiddenLines = totalLogLines - displayedLogLines;
const message =
hiddenLines === 1
? intl.formatMessage(
{
id: 'dashboard.logs.hidden.one',
defaultMessage: '1 line hidden due to selected log levels'
},
{ numHiddenLines: totalLogLines - displayedLogLines }
)
: intl.formatMessage(
{
id: 'dashboard.logs.hidden',
defaultMessage:
'{numHiddenLines, plural, other {# lines}} hidden due to selected log levels'
},
{ numHiddenLines: totalLogLines - displayedLogLines }
);

return (
<span className="tkn--log-filtered">
<Information />
{message}
</span>
);
}

export class LogContainer extends Component {
constructor(props) {
super(props);
Expand Down Expand Up @@ -294,12 +340,19 @@ export class LogContainer extends Component {
}
return acc;
}, []);

if (parsedLogs.length < 20_000) {
return (
<LogFormat
fields={{ level: showLevels, timestamp: showTimestamps }}
logs={parsedLogs}
/>
<>
<LogsFilteredNotification
displayedLogLines={parsedLogs.length}
totalLogLines={logs.length}
/>
<LogFormat
fields={{ level: showLevels, timestamp: showTimestamps }}
logs={parsedLogs}
/>
</>
);
}

Expand All @@ -308,22 +361,28 @@ export class LogContainer extends Component {
: defaultHeight;

return (
<List
height={height}
itemCount={parsedLogs.length}
itemData={parsedLogs}
itemSize={itemSize}
width="100%"
>
{({ data, index, style }) => (
<div style={style}>
<LogFormat
fields={{ level: showLevels, timestamp: showTimestamps }}
logs={[data[index]]}
/>
</div>
)}
</List>
<>
<LogsFilteredNotification
displayedLogLines={parsedLogs.length}
totalLogLines={logs.length}
/>
<List
height={height}
itemCount={parsedLogs.length}
itemData={parsedLogs}
itemSize={itemSize}
width="100%"
>
{({ data, index, style }) => (
<div style={style}>
<LogFormat
fields={{ level: showLevels, timestamp: showTimestamps }}
logs={[data[index]]}
/>
</div>
)}
</List>
</>
);
};

Expand Down
3 changes: 2 additions & 1 deletion packages/components/src/components/Log/Log.stories.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2019-2024 The Tekton Authors
Copyright 2019-2025 The Tekton Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Expand Down Expand Up @@ -135,6 +135,7 @@ export const Toolbar = {
{...args}
toolbar={
<LogsToolbar
id="logs-toolbar"
logLevels={args.showLevels ? args.logLevels : null}
name="step_log_filename.txt"
onToggleLogLevel={logLevel =>
Expand Down
29 changes: 26 additions & 3 deletions packages/components/src/components/Log/_Log.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2019-2024 The Tekton Authors
Copyright 2019-2025 The Tekton Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Expand Down Expand Up @@ -29,7 +29,6 @@ pre.tkn--log {
}

line-height: 1rem; // Update the react-window List itemSize if changing this
overflow: hidden;
background-color: $background;
color: $text-primary;

Expand Down Expand Up @@ -124,9 +123,33 @@ pre.tkn--log {

.tkn--log-container {
overflow-x: auto;

.tkn--log-filtered {
display: flex;
margin-block-end: 1rem;

svg {
margin-inline-end: 0.5rem;
}
}
}

.tkn--log-container:not(:empty) + .tkn--log-trailer {
.tkn--log-container:has(code:not(:empty)) + .tkn--log-trailer {
margin-block-start: 1rem;
}

.tkn--log-settings-menu-content {
padding-block-end: .5rem;
padding-block-start: 1rem;
padding-inline: 1rem;
font-family: 'IBM Plex Sans', sans-serif;
color: $text-secondary;

hr {
border: none;
margin-block: 1rem;
background: $border-subtle;
block-size: 1px;
}
}
}
Loading

0 comments on commit 158cc5b

Please sign in to comment.