Adds autorefresh with play/pause

pull/10616/head
Brandon Farmer 2018-06-01 15:05:38 -07:00
parent 955b2d278f
commit 3404d7375b
3 changed files with 160 additions and 26 deletions

View File

@ -17,9 +17,11 @@ interface Props {
currentSource: Source | null
currentNamespaces: Namespace[]
timeRange: TimeRange
liveUpdating: boolean
onChooseSource: (sourceID: string) => void
onChooseNamespace: (namespace: Namespace) => void
onChooseTimerange: (timeRange: TimeRange) => void
onChangeLiveUpdatingStatus: () => void
}
class LogViewerHeader extends PureComponent<Props> {
@ -29,7 +31,10 @@ class LogViewerHeader extends PureComponent<Props> {
<div className="page-header full-width">
<div className="page-header__container">
<div className="page-header__left">
<h1 className="page-header__title">Log Viewer</h1>
{this.status}
<h1 className="page-header__title" style={{marginLeft: '10px'}}>
Log Viewer
</h1>
</div>
<div className="page-header__right">
<Dropdown
@ -55,6 +60,29 @@ class LogViewerHeader extends PureComponent<Props> {
)
}
private get status(): JSX.Element {
const {liveUpdating, onChangeLiveUpdatingStatus} = this.props
if (liveUpdating) {
return (
<button
className={'btn btn-sm btn-default btn-square'}
onClick={onChangeLiveUpdatingStatus}
>
<span style={{marginRight: '10px'}} className="icon pause" />
</button>
)
} else {
return (
<button
className={'btn btn-sm btn-default btn-square'}
onClick={onChangeLiveUpdatingStatus}
>
<span style={{marginRight: '10px'}} className="icon refresh" />
</button>
)
}
}
private handleChooseTimeRange = (timerange: TimeRange) => {
this.props.onChooseTimerange(timerange)
}

View File

@ -1,39 +1,64 @@
import _ from 'lodash'
import moment from 'moment'
import React, {PureComponent, MouseEvent} from 'react'
import React, {Component, MouseEvent} from 'react'
import {Grid, AutoSizer} from 'react-virtualized'
import {getDeep} from 'src/utils/wrappers'
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
const ROW_HEIGHT = 40
const ROW_HEIGHT = 30
const HIGHLIGHT_COLOR = '#555'
interface Props {
data: {
columns: string[]
values: string[]
}
scrolledToTop: boolean
onScrollVertical: () => void
onScrolledToTop: () => void
}
interface State {
scrollLeft: number
scrollTop: number
currentRow: number
}
class LogsTable extends PureComponent<Props, State> {
class LogsTable extends Component<Props, State> {
public static getDerivedStateFromProps(props, state) {
const {scrolledToTop} = props
let scrollTop = _.get(state, 'scrollTop', 0)
if (scrolledToTop) {
scrollTop = 0
}
return {
scrollTop,
scrollLeft: 0,
currentRow: -1,
}
}
constructor(props: Props) {
super(props)
this.state = {
scrollLeft: 0,
scrollTop: 0,
scrollLeft: 0,
currentRow: -1,
}
}
public render() {
const rowCount = getDeep(this.props, 'data.values.length', 0)
const columnCount = getDeep(this.props, 'data.columns.length', 1) - 1
return (
<div className="logs-viewer--table-container">
<div
className="logs-viewer--table-container"
onMouseOut={this.handleMouseOut}
>
<AutoSizer>
{({width}) => (
<Grid
@ -72,7 +97,7 @@ class LogsTable extends PureComponent<Props, State> {
cellRenderer={this.cellRenderer}
columnCount={columnCount}
columnWidth={this.getColumnWidth}
style={{height: 40 * rowCount}}
style={{height: ROW_HEIGHT * rowCount}}
/>
</FancyScrollbar>
)}
@ -91,6 +116,12 @@ class LogsTable extends PureComponent<Props, State> {
private handleScroll = scrollInfo => {
const {scrollLeft, scrollTop} = scrollInfo
if (scrollTop === 0) {
this.props.onScrolledToTop()
} else if (scrollTop !== this.state.scrollTop) {
this.props.onScrollVertical()
}
this.setState({scrollLeft, scrollTop})
}
@ -116,7 +147,7 @@ class LogsTable extends PureComponent<Props, State> {
switch (column) {
case 'message':
return 900
return 1200
case 'timestamp':
return 200
case 'procid':
@ -168,7 +199,9 @@ class LogsTable extends PureComponent<Props, State> {
''
)
let value = this.props.data.values[rowIndex][columnIndex + 1]
let value: string | JSX.Element = this.props.data.values[rowIndex][
columnIndex + 1
]
switch (column) {
case 'timestamp':
@ -181,22 +214,39 @@ class LogsTable extends PureComponent<Props, State> {
value = _.replace(value, '\\n', '')
break
case 'severity':
return (
<div style={style} key={key}>
<div
className={`logs-viewer--dot ${value}-severity`}
title={this.severityLevel(value)}
/>
</div>
value = (
<div
className={`logs-viewer--dot ${value}-severity`}
title={this.severityLevel(value)}
/>
)
}
let backgroundColor = ''
if (rowIndex === this.state.currentRow && columnIndex > 0) {
backgroundColor = HIGHLIGHT_COLOR
}
return (
<div style={style} key={key}>
<div
style={{...style, padding: '5px', backgroundColor}}
key={key}
onMouseOver={this.handleMouseOver}
data-index={rowIndex}
>
{value}
</div>
)
}
private handleMouseOver = (e: MouseEvent<HTMLElement>) => {
const target = e.target as HTMLElement
this.setState({currentRow: +target.dataset.index})
}
private handleMouseOut = () => {
this.setState({currentRow: -1})
}
}
export default LogsTable

View File

@ -51,25 +51,29 @@ interface Props {
interface State {
searchString: string
filters: Filter[]
liveUpdating: boolean
}
const DUMMY_FILTERS = [
{
id: '0',
key: 'host',
value: 'prod1-rsavage.local',
operator: '==',
enabled: true,
},
// {
// id: '0',
// key: 'host',
// value: 'prod1-rsavage.local',
// operator: '==',
// enabled: true,
// },
]
class LogsPage extends PureComponent<Props, State> {
private interval: NodeJS.Timer
constructor(props: Props) {
super(props)
this.state = {
searchString: '',
filters: DUMMY_FILTERS,
liveUpdating: false,
}
}
@ -85,10 +89,16 @@ class LogsPage extends PureComponent<Props, State> {
if (this.props.currentNamespace) {
this.props.executeQueriesAsync()
}
this.startUpdating()
}
public componentWillUnmount() {
clearInterval(this.interval)
}
public render() {
const {filters} = this.state
const {filters, liveUpdating} = this.state
const {searchTerm} = this.props
const count = getDeep(this.props, 'tableData.values.length', 0)
@ -107,12 +117,43 @@ class LogsPage extends PureComponent<Props, State> {
filters={filters}
onUpdateFilters={this.handleUpdateFilters}
/>
<LogsTable data={this.props.tableData} />
<LogsTable
data={this.props.tableData}
onScrollVertical={this.handleVerticalScroll}
onScrolledToTop={this.handleScrollToTop}
scrolledToTop={liveUpdating}
/>
</div>
</div>
)
}
private startUpdating = () => {
if (this.interval) {
clearInterval(this.interval)
}
this.interval = setInterval(this.handleInterval, 10000)
this.setState({liveUpdating: true})
}
private handleScrollToTop = () => {
if (!this.state.liveUpdating) {
this.startUpdating()
}
}
private handleVerticalScroll = () => {
if (this.state.liveUpdating) {
clearInterval(this.interval)
this.setState({liveUpdating: false})
}
}
private handleInterval = () => {
this.props.executeQueriesAsync()
}
private get chart(): JSX.Element {
const {histogramData, timeRange} = this.props
return (
@ -133,8 +174,11 @@ class LogsPage extends PureComponent<Props, State> {
timeRange,
} = this.props
const {liveUpdating} = this.state
return (
<LogViewerHeader
liveUpdating={liveUpdating}
availableSources={sources}
timeRange={timeRange}
onChooseSource={this.handleChooseSource}
@ -143,10 +187,22 @@ class LogsPage extends PureComponent<Props, State> {
currentSource={currentSource}
currentNamespaces={currentNamespaces}
currentNamespace={currentNamespace}
onChangeLiveUpdatingStatus={this.handleChangeLiveUpdatingStatus}
/>
)
}
private handleChangeLiveUpdatingStatus = (): void => {
const {liveUpdating} = this.state
if (liveUpdating) {
clearInterval(this.interval)
this.setState({liveUpdating: false})
} else {
this.startUpdating()
}
}
private handleSubmitSearch = (value: string): void => {
this.props.setSearchTermAsync(value)
}