Update logs loading description (#4452)

* Update logs table loading time and timestamp
* Make time bounds in searching state have gradient text
* Add loading animation
* Add graphic for logs No Results state
* Update CHANGELOG 1.7 UI IMPROVEMENTS
* Update default logs message

Co-authored-by: Alex Paxton <thealexpaxton@gmail.com>
pull/4487/head
Delmer 2018-09-21 15:33:13 -04:00 committed by GitHub
parent e207bd3ec1
commit e91faf6a6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 224 additions and 47 deletions

View File

@ -38,6 +38,7 @@
1. [#2265](https://github.com/influxdata/chronograf/pull/2265): Autofocus dashboard query editor
1. [#4429](https://github.com/influxdata/chronograf/pull/4429): Fix query editor flickering on update
1. [#4452](https://github.com/influxdata/chronograf/pull/4452): Improve log search spinner info
### UI Improvements
1. [#4236](https://github.com/influxdata/chronograf/pull/4236): Add spinner when loading logs table rows

25
ui/assets/images/log.svg Normal file
View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="40px" height="40px" viewBox="0 0 40 40" style="enable-background:new 0 0 40 40;" xml:space="preserve">
<style type="text/css">
.st0{fill:#8E91A1;}
</style>
<path class="st0" d="M38.6,22.3L38.6,22.3c-1.2-1-1.7-1.4-2.2-1.8c-0.2-0.2-0.5-0.4-0.8-0.6l2.9-0.5c0.4,0,0.7,0,1-0.2l0,0l0.1-0.1
l0.1-0.1c0,0,0,0,0.1-0.1c1.1-1.5-1.6-4.5-2.9-5.7c-1.2-1.2-3.9-3.6-5.4-3l-5.8,0.3c-3.5-3.5-3.1-3.3-5.5-6.2
c-0.7-0.8-1.5-1.8-2.5-3l0,0c0,0,0,0-0.1-0.1C15.2-1.1,10,0.7,5.2,5.4s-6.5,10-4.3,12.3L22,38.8c2.3,2.3,7.5,0.5,12.3-4.3
C39.1,29.9,40.9,24.6,38.6,22.3z M37,27.6c-0.5-0.6-1-1.2-1.5-1.8c0.2,0.1,0.3,0.1,0.5,0.2c0.7,0.4,1.1,0.6,1.3,0.8
C37.2,27,37.1,27.3,37,27.6z M35.5,14.6c0.9,0.9,1.5,1.6,1.9,2.2c-0.6-0.4-1.3-1-2.2-1.9s-1.5-1.6-1.9-2.2
C33.9,13.1,34.7,13.7,35.5,14.6z M6.7,6.9c3.9-3.9,8.2-5.5,9.4-4.2l0,0c1.2,1.2-0.4,5.5-4.3,9.4C8,16,3.7,17.5,2.4,16.3
S2.8,10.8,6.7,6.9z M10.4,15.9c0.1,0.1,0.1,0.2,0.2,0.3c0.3,0.4,0.6,0.8,0.9,1.1c-0.2-0.1-0.5-0.2-0.7-0.3c-0.5-0.3-0.8-0.4-1.1-0.6
C10,16.2,10.2,16.1,10.4,15.9z M23.5,37.4L4.7,18.6C6,18.4,7.4,17.8,8.8,17c0.4,0.2,0.9,0.5,1.6,0.9c1.8,0.9,3.7,1.4,3.8,1.4
c0.3,0.1,0.6-0.1,0.6-0.4s-0.1-0.5-0.4-0.6c-1.3-0.4-2.1-1.5-3-2.8c-0.1-0.1-0.2-0.2-0.3-0.3c0.7-0.6,1.3-1.1,2-1.8
c2.8-2.8,4.5-5.8,5-8.2c0.1,0.1,0.2,0.2,0.2,0.3c2.1,2.5,1.7,2.3,4.3,5l-1.4,0.1c-0.6,0-1,0.5-1,1c0,0.3,0.1,0.5,0.3,0.7
s0.5,0.3,0.8,0.3l9.2-0.4c0.5,1.5,2.1,3.2,3,4.1c0.4,0.4,1,1,1.7,1.5L28.1,19c-0.6,0.1-0.9,0.6-0.8,1.1c0,0.2,0.1,0.4,0.3,0.6
c0.2,0.2,0.5,0.3,0.9,0.3l4.4-0.8c1.2,1.1,1.6,1.4,2.2,1.8c0.4,0.3,0.9,0.7,2,1.6c0.4,0.4,0.5,1.1,0.4,1.9c-0.3-0.2-0.7-0.4-1.1-0.6
c-1.8-0.9-3.7-1.4-3.8-1.4c-0.3,0-0.6,0.2-0.6,0.5c-0.1,0.2,0,0.4,0.1,0.5s0.1,0.1,0.2,0.1c1.4,0.5,3,2.5,4.1,3.9l0,0
c-0.8,1.4-1.9,3-3.5,4.6C29,37.1,24.8,38.6,23.5,37.4z"/>
<path class="st0" d="M14.2,5.5c0-0.4-0.2-0.6-0.3-0.7c-1.3-1.3-5.6,2.8-6.1,3.3s-4.6,4.7-3.3,6c0.2,0.2,0.5,0.2,0.7,0s0.2-0.5,0-0.7
C5.1,13,6.1,11.1,8.5,8.7c2.3-2.3,4.1-3.3,4.6-3.3c0,0.5-1,2.5-3.3,4.6c-0.7,0.7-1.2,1-1.5,1.2c0.2-0.3,0.5-0.9,1.2-1.4
c0.2-0.2,0.2-0.5,0-0.7S9,8.9,8.8,9.1c-1.1,1.1-2,2.5-1.4,3.1c0.5,0.5,1.5,0.1,3.1-1.4C12.7,8.8,14.2,6.7,14.2,5.5z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="36px" height="36px" viewBox="0 0 36 36" style="enable-background:new 0 0 36 36;" xml:space="preserve">
<style type="text/css">
.st0{fill:#292933;}
.st1{fill:url(#SVGID_1_);}
</style>
<g>
<path class="st0" d="M31.7,36c-1.2,0-2.3-0.4-3.1-1.3l-6.2-6.2c-2.2,1.2-4.7,1.9-7.2,1.9C6.8,30.4,0,23.6,0,15.2
C0,6.8,6.8,0,15.1,0c8.4,0,15.2,6.8,15.2,15.2c0,2.6-0.6,5-1.8,7.2l6.2,6.2c1.7,1.6,1.8,4.4,0.2,6.1C34,35.6,32.9,36,31.7,36z
M15.1,6.2c-5,0-9,4-9,9c0,5,4.1,9.1,9.1,9.1c2.2,0,4.4-0.8,6-2.3c0.1-0.1,0.2-0.3,0.3-0.4c0.1-0.1,0.3-0.2,0.4-0.3c0,0,0,0,0,0
l0,0c1.5-1.6,2.3-3.8,2.3-6C24.2,10.3,20.1,6.2,15.1,6.2z"/>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="7.7215" y1="7.3808" x2="27.1691" y2="27.7753">
<stop offset="0" style="stop-color:#00C9FF"/>
<stop offset="1" style="stop-color:#9394FF"/>
</linearGradient>
<path class="st1" d="M33.7,29.5l-6.9-6.9c1.4-2.2,2.2-4.6,2.2-7.4c0-7.7-6.2-13.9-13.9-13.9S1.3,7.6,1.3,15.2s6.2,13.9,13.9,13.9
c2.7,0,5.2-0.8,7.4-2.2l6.9,6.9c0.6,0.6,1.4,0.9,2.2,0.9c0.8,0,1.6-0.3,2.2-0.9C35,32.7,35,30.7,33.7,29.5z M4.8,15.2
c0-5.7,4.6-10.3,10.3-10.3s10.4,4.6,10.4,10.4c0,2.7-1,5.2-2.7,7c-0.1,0.1-0.3,0.2-0.3,0.3c-0.1,0.1-0.2,0.3-0.3,0.3
c-1.8,1.7-4.3,2.7-7,2.7C9.4,25.6,4.8,21,4.8,15.2z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="40px" height="40px" viewBox="0 0 40 40" style="enable-background:new 0 0 40 40;" xml:space="preserve">
<style type="text/css">
.st0{fill:#8E91A1;}
</style>
<path class="st0" d="M14.8,2.2c0.6,0,1.1,0.1,1.3,0.4l0,0c0.3,0.4,0.6,0.8,0.9,1.1c0.6,0.7,1.1,1.3,1.5,1.8c0.6,0.7,1,1.2,1.4,1.7
c1.1,1.3,1.6,2,4.2,4.6l0.6,0.6l0.9,0l5.8-0.3l0.3,0l0.2-0.1c0.5,0.1,1.8,0.9,3.4,2.5l0,0l0,0c1.1,1,1.9,2.1,2.3,2.8l-2.6,0.4
l-4.9,0.9l4.2,2.8c0.2,0.1,0.4,0.3,0.5,0.4l0.1,0.1l0.1,0.1c0.5,0.4,1,0.8,2.1,1.7c0.2,0.3,0.4,0.7,0.4,1.3c0,1.7-1.3,4.7-4.7,8.1
c-3.5,3.5-6.6,4.7-8.2,4.7c-0.9,0-1.2-0.3-1.3-0.4L2.3,16.3c-1.1-1.1,0-5.3,4.3-9.5C10.1,3.4,13.1,2.2,14.8,2.2 M14.8,0.2
c-2.7,0-6.3,1.9-9.6,5.2c-4.8,4.7-6.5,10-4.3,12.3L22,38.8c0.7,0.7,1.6,1,2.7,1c2.6,0,6.2-1.9,9.6-5.3c4.8-4.6,6.6-9.9,4.3-12.2
c-1.2-1-1.7-1.4-2.2-1.8c-0.2-0.2-0.5-0.4-0.8-0.6l2.9-0.5c0.4,0,0.7,0,1-0.2l0.1-0.1l0.1-0.1c0,0,0,0,0.1-0.1
c1.1-1.5-1.6-4.5-2.9-5.7c-1.1-1.1-3.3-3.1-4.9-3.1c-0.2,0-0.4,0-0.5,0.1l-5.8,0.3c-3.5-3.5-3.1-3.3-5.5-6.2c-0.7-0.8-1.5-1.8-2.5-3
c0,0,0,0-0.1-0.1C16.9,0.5,16,0.2,14.8,0.2L14.8,0.2z"/>
<g>
<path class="st0" d="M19.3,15.5c-0.3,0-0.6,0-0.9,0.1c-1,0.2-1.8,0.6-2.3,1.3s-0.7,1.5-0.6,2.5l2.3-0.5c0-0.4,0.1-0.7,0.2-1
c0.1-0.2,0.3-0.4,0.6-0.4c0.1,0,0.1,0,0.2,0c0.3,0,0.5,0.2,0.6,0.5c0.1,0.4,0.1,0.7,0,1s-0.3,0.6-0.6,0.9l-0.5,0.6
c-0.5,0.7-0.7,1.7-0.5,2.8l1.9-0.4c0-0.6,0.1-1.2,0.5-1.7l0.7-0.8c0.7-0.9,1-1.7,0.9-2.5c-0.1-0.6-0.3-1-0.6-1.4
c-0.3-0.4-0.7-0.6-1.2-0.7C19.9,15.6,19.6,15.5,19.3,15.5L19.3,15.5z M19,24.1c-0.1,0-0.2,0-0.3,0c-0.4,0.1-0.6,0.3-0.8,0.6
c-0.2,0.3-0.3,0.6-0.2,1c0.1,0.3,0.3,0.6,0.6,0.8c0.2,0.1,0.4,0.2,0.6,0.2c0.1,0,0.2,0,0.3,0c0.4-0.1,0.6-0.3,0.8-0.6
c0.2-0.3,0.3-0.6,0.2-1c-0.1-0.3-0.3-0.6-0.6-0.7C19.5,24.1,19.3,24.1,19,24.1L19,24.1z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -35,6 +35,7 @@ import {
SeverityFormatOptions,
SeverityColorOptions,
SeverityLevelOptions,
DEFAULT_TIME_FORMAT,
} from 'src/logs/constants'
import {TimeRange, NotificationAction} from 'src/types'
@ -46,7 +47,6 @@ import {
RowHeightHandler,
SearchStatus,
Filter,
TimeFormatOption,
} from 'src/types/logs'
import {INITIAL_LIMIT} from 'src/logs/actions'
@ -542,11 +542,6 @@ class LogsTable extends Component<Props, State> {
const highlightRow = rowIndex === this.state.currentRow
if (column === 'timestamp') {
const formattedTime = moment(
formattedValue as string,
TimeFormatOption.SLASH_YMD_TIME
).format(TimeFormatOption.DEFAULT)
return (
<div
className={classnames('logs-viewer--cell', {
@ -561,12 +556,12 @@ class LogsTable extends Component<Props, State> {
<div
data-tag-key={column}
data-tag-value={value}
onClick={this.handleTimestampClick(`${formattedTime}`)}
onClick={this.handleTimestampClick(`${formattedValue}`)}
data-index={rowIndex}
onMouseOver={this.handleMouseOver}
className="logs-viewer--clickable"
>
{formattedTime}
{formattedValue}
</div>
</div>
)
@ -662,7 +657,7 @@ class LogsTable extends Component<Props, State> {
private handleTimestampClick = (time: string) => () => {
const {onChooseCustomTime} = this.props
const formattedTime = moment(time, TimeFormatOption.DEFAULT).toISOString()
const formattedTime = moment(time, DEFAULT_TIME_FORMAT).toISOString()
onChooseCustomTime(formattedTime)
}
@ -686,7 +681,8 @@ class LogsTable extends Component<Props, State> {
return (
<LoadingStatus
status={this.props.searchStatus}
currentOlderLowerBound={this.props.lower}
lower={this.props.lower}
upper={this.props.upper}
/>
)
}

View File

@ -1,26 +1,22 @@
import React, {PureComponent} from 'react'
import _ from 'lodash'
import moment from 'moment'
import {SearchStatus} from 'src/types/logs'
import {LoadingMessages} from 'src/logs/constants'
import {formatTime} from 'src/logs/utils'
interface Props {
status: SearchStatus
currentOlderLowerBound: number
lower: number
upper: number
}
class LoadingStatus extends PureComponent<Props> {
public render() {
const {currentOlderLowerBound} = this.props
const loadingTime = moment(currentOlderLowerBound).fromNow()
return (
<div className="logs-viewer--table-container generic-empty-state">
{this.loadingSpinner}
<h4>
{this.loadingMessage}: ({loadingTime})
{this.loadingMessage} {this.description}
</h4>
<p>{this.description}</p>
</div>
)
}
@ -30,34 +26,65 @@ class LoadingStatus extends PureComponent<Props> {
case SearchStatus.NoResults:
return (
<>
Try changing the <strong>time range</strong> or{' '}
<strong>removing filters</strong>
Try changing the <strong>Time Range</strong> or{' '}
<strong>Removing Filters</strong>
</>
)
default:
return <>{this.randomLoadingDescription}</>
return <>{this.timeBounds}</>
}
}
private get randomLoadingDescription(): string {
return _.sample(LoadingMessages)
private get loadingSpinner(): JSX.Element {
switch (this.props.status) {
case SearchStatus.NoResults:
return (
<div className="logs-viewer--search-graphic">
<div className="logs-viewer--graphic-empty" />
</div>
)
case SearchStatus.UpdatingFilters:
case SearchStatus.UpdatingTimeBounds:
case SearchStatus.UpdatingSource:
case SearchStatus.UpdatingNamespace:
case SearchStatus.Loading:
return (
<div className="logs-viewer--search-graphic">
<div className="logs-viewer--graphic-log" />
<div className="logs-viewer--graphic-magnifier-a">
<div className="logs-viewer--graphic-magnifier-b" />
</div>
</div>
)
default:
return null
}
}
private get timeBounds(): JSX.Element {
return (
<div className="logs-viewer--searching-time">
from <strong>{formatTime(this.props.upper)}</strong> to{' '}
<strong>{formatTime(this.props.lower)}</strong>
</div>
)
}
private get loadingMessage(): string {
switch (this.props.status) {
case SearchStatus.UpdatingFilters:
return 'Updating search filters'
return 'Updating search filters...'
case SearchStatus.NoResults:
return 'No logs found'
case SearchStatus.UpdatingTimeBounds:
return 'Searching time bounds'
return 'Searching time bounds...'
case SearchStatus.UpdatingSource:
return 'Searching updated source'
return 'Searching updated source...'
case SearchStatus.UpdatingNamespace:
return 'Searching updated namespace'
return 'Searching updated namespace...'
case SearchStatus.Loading:
default:
return 'Searching'
return 'Searching...'
}
}
}

View File

@ -1,5 +1,7 @@
import {TableData} from 'src/types/logs'
export const DEFAULT_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'
export enum SeverityColorOptions {
ruby = 'ruby',
fire = 'fire',
@ -207,13 +209,6 @@ export const HISTOGRAM_SHIFT = 1 / 5
export const SECONDS_TO_MS = 1000
export const LoadingMessages = [
'Looking for logs in all the wrong places',
'Rolling like a log',
'100% natural logs',
'Chopping wood',
]
export const DEFAULT_TAIL_CHUNK_DURATION_MS = 5000
export const DEFAULT_MAX_TAIL_BUFFER_DURATION_MS = 30000

View File

@ -1,7 +1,6 @@
import React, {Component} from 'react'
import uuid from 'uuid'
import _ from 'lodash'
import moment from 'moment'
import {connect} from 'react-redux'
import {AutoSizer} from 'react-virtualized'
import {withRouter, InjectedRouter} from 'react-router'
@ -81,7 +80,6 @@ import {
TimeBounds,
SearchStatus,
FetchLoop,
TimeFormatOption,
} from 'src/types/logs'
import {
applyChangesToTableData,
@ -89,6 +87,7 @@ import {
} from 'src/logs/utils/table'
import extentBy from 'src/utils/extentBy'
import {computeTimeBounds} from 'src/logs/utils/timeBounds'
import {formatTime} from 'src/logs/utils'
interface Props {
sources: Source[]
@ -628,7 +627,8 @@ class LogsPage extends Component<Props, State> {
sortBarGroups={this.handleSortHistogramBarGroups}
>
{({xScale, adjustedHeight, margins}) => {
const x = xScale(new Date(timeOption).valueOf())
const timeOptionValue = new Date(timeOption).valueOf()
const x = xScale(timeOptionValue)
const y1 = margins.top
const y2 = margins.top + adjustedHeight
const textSize = 11
@ -693,7 +693,7 @@ class LogsPage extends Component<Props, State> {
height={textSize}
fill={Greys.Sidewalk}
>
{moment(timeOption).format(TimeFormatOption.DEFAULT)}
{formatTime(timeOptionValue)}
</text>
</svg>
</>

View File

@ -15,6 +15,7 @@ import {
import {HistogramData} from 'src/types/histogram'
import {executeQueryAsync} from 'src/logs/api'
import {DEFAULT_TIME_FORMAT} from 'src/logs/constants'
const BIN_COUNT = 30
@ -345,3 +346,7 @@ export const parseHistogramQueryResponse = (
return data
}
export const formatTime = (time: number): string => {
return moment(time).format(DEFAULT_TIME_FORMAT)
}

View File

@ -2,7 +2,7 @@ import _ from 'lodash'
import moment from 'moment'
import {getDeep} from 'src/utils/wrappers'
import {TableData, LogsTableColumn, SeverityFormat} from 'src/types/logs'
import {SeverityFormatOptions} from 'src/logs/constants'
import {SeverityFormatOptions, DEFAULT_TIME_FORMAT} from 'src/logs/constants'
import {
orderTableColumns,
filterTableColumns,
@ -40,7 +40,7 @@ export const formatColumnValue = (
): string => {
switch (column) {
case 'timestamp':
return moment(+value / 1000000).format('YYYY/MM/DD HH:mm:ss')
return moment(+value / 1000000).format(DEFAULT_TIME_FORMAT)
case 'procid':
case 'host':
case 'appname':

View File

@ -334,4 +334,90 @@ $logs-viewer-gutter: 60px;
.logs-viewer--table-count {
position: absolute;
top: -60px;
}
.logs-viewer--searching-time {
text-transform: uppercase;
margin-top: $ix-marg-b;
@include gradient-h($c-laser, $c-comet);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
@keyframes magnifier {
0% {
transform: translate(-50%,-50%);
}
25% {
transform: translate(0%,-50%);
}
75% {
transform: translate(-100%,-50%);
}
100% {
transform: translate(-50%,-50%);
}
}
@keyframes magRotateA {
0% {
transform: translate(-50%, -50%) rotate(0deg);
}
100% {
transform: translate(-50%, -50%) rotate(360deg);
}
}
@keyframes magRotateB {
0% {
transform: translate(-50%,-50%) rotate(0deg);
}
100% {
transform: translate(-50%,-50%) rotate(-360deg);
}
}
.logs-viewer--search-graphic {
width: 100px;
height: 100px;
display: inline-block;
position: relative;
}
.logs-viewer--graphic-log,
.logs-viewer--graphic-magnifier-b,
.logs-viewer--graphic-empty {
background-size: 100% 100%;
background-position: center center;
position: absolute;
top: 50%;
left: 50%;
}
.logs-viewer--graphic-magnifier-a {
width: 100px;
height: 100px;
position: absolute;
top: 50%;
left: 50%;
animation: magRotateA 1.8s infinite linear;
}
.logs-viewer--graphic-log {
background-image: url('../../assets/images/log.svg');
width: 50px;
height: 50px;
transform: translate(-50%,-50%);
}
.logs-viewer--graphic-magnifier-b {
background-image: url('../../assets/images/magnifier.svg');
width: 50px;
height: 50px;
top: 35%;
left: 35%;
animation: magRotateB 1.8s infinite linear;
}
.logs-viewer--graphic-empty {
background-image: url('../../assets/images/no-logs.svg');
width: 60px;
height: 60px;
transform: translate(-50%,-50%);
}

View File

@ -10,11 +10,6 @@ import {FieldOption} from 'src/types/dashboards'
import {TimeSeriesValue} from 'src/types/series'
import {TimeRange} from 'src/types/logs'
export enum TimeFormatOption {
DEFAULT = 'YYYY-MM-DD HH:mm:ss',
SLASH_YMD_TIME = 'YYYY/MM/DD HH:mm:ss',
}
export enum SearchStatus {
None = 'None',
Loading = 'Loading',