Initial duel load of log viewer data
parent
0e0a263993
commit
b1bb27bbee
|
@ -9,6 +9,8 @@ import {
|
||||||
buildHistogramQueryConfig,
|
buildHistogramQueryConfig,
|
||||||
buildTableQueryConfig,
|
buildTableQueryConfig,
|
||||||
buildLogQuery,
|
buildLogQuery,
|
||||||
|
buildForwardLogQuery,
|
||||||
|
buildBackwardLogQuery,
|
||||||
parseHistogramQueryResponse,
|
parseHistogramQueryResponse,
|
||||||
} from 'src/logs/utils'
|
} from 'src/logs/utils'
|
||||||
import {
|
import {
|
||||||
|
@ -21,8 +23,11 @@ import {
|
||||||
// getLogConfig as getLogConfigAJAX,
|
// getLogConfig as getLogConfigAJAX,
|
||||||
// updateLogConfig as updateLogConfigAJAX,
|
// updateLogConfig as updateLogConfigAJAX,
|
||||||
} from 'src/logs/api'
|
} from 'src/logs/api'
|
||||||
|
import serverLogData from 'src/logs/data/serverLogData'
|
||||||
import {LogsState, Filter, TableData, LogConfig} from 'src/types/logs'
|
import {LogsState, Filter, TableData, LogConfig} from 'src/types/logs'
|
||||||
|
|
||||||
|
const INITIAL_LIMIT = 1000
|
||||||
|
|
||||||
const defaultTableData: TableData = {
|
const defaultTableData: TableData = {
|
||||||
columns: [
|
columns: [
|
||||||
'time',
|
'time',
|
||||||
|
@ -61,7 +66,40 @@ export enum ActionTypes {
|
||||||
DecrementQueryCount = 'LOGS_DECREMENT_QUERY_COUNT',
|
DecrementQueryCount = 'LOGS_DECREMENT_QUERY_COUNT',
|
||||||
ConcatMoreLogs = 'LOGS_CONCAT_MORE_LOGS',
|
ConcatMoreLogs = 'LOGS_CONCAT_MORE_LOGS',
|
||||||
SetConfig = 'SET_CONFIG',
|
SetConfig = 'SET_CONFIG',
|
||||||
|
SetTableRelativeTime = 'SET_TABLE_RELATIVE_TIME',
|
||||||
|
SetTableCustomTime = 'SET_TABLE_CUSTOM_TIME',
|
||||||
|
SetTableForwardData = 'SET_TABLE_FORWARD_DATA',
|
||||||
|
SetTableBackwardData = 'SET_TABLE_BACKWARD_DATA',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SetTableForwardDataAction {
|
||||||
|
type: ActionTypes.SetTableForwardData
|
||||||
|
payload: {
|
||||||
|
data: TableData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SetTableBackwardDataAction {
|
||||||
|
type: ActionTypes.SetTableBackwardData
|
||||||
|
payload: {
|
||||||
|
data: TableData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SetTableRelativeTimeAction {
|
||||||
|
type: ActionTypes.SetTableRelativeTime
|
||||||
|
payload: {
|
||||||
|
time: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SetTableCustomTimeAction {
|
||||||
|
type: ActionTypes.SetTableCustomTime
|
||||||
|
payload: {
|
||||||
|
time: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface ConcatMoreLogsAction {
|
export interface ConcatMoreLogsAction {
|
||||||
type: ActionTypes.ConcatMoreLogs
|
type: ActionTypes.ConcatMoreLogs
|
||||||
payload: {
|
payload: {
|
||||||
|
@ -194,6 +232,10 @@ export type Action =
|
||||||
| IncrementQueryCountAction
|
| IncrementQueryCountAction
|
||||||
| ConcatMoreLogsAction
|
| ConcatMoreLogsAction
|
||||||
| SetConfigsAction
|
| SetConfigsAction
|
||||||
|
| SetTableCustomTimeAction
|
||||||
|
| SetTableRelativeTimeAction
|
||||||
|
| SetTableForwardDataAction
|
||||||
|
| SetTableBackwardDataAction
|
||||||
|
|
||||||
const getTimeRange = (state: State): TimeRange | null =>
|
const getTimeRange = (state: State): TimeRange | null =>
|
||||||
getDeep<TimeRange | null>(state, 'logs.timeRange', null)
|
getDeep<TimeRange | null>(state, 'logs.timeRange', null)
|
||||||
|
@ -216,6 +258,131 @@ const getSearchTerm = (state: State): string | null =>
|
||||||
const getFilters = (state: State): Filter[] =>
|
const getFilters = (state: State): Filter[] =>
|
||||||
getDeep<Filter[]>(state, 'logs.filters', [])
|
getDeep<Filter[]>(state, 'logs.filters', [])
|
||||||
|
|
||||||
|
const getTableSelectedTime = (state: State): string => {
|
||||||
|
const custom = getDeep<string>(state, 'logs.tableTime.custom', '')
|
||||||
|
|
||||||
|
if (!_.isEmpty(custom)) {
|
||||||
|
return custom
|
||||||
|
}
|
||||||
|
|
||||||
|
const relative = getDeep<number>(state, 'logs.tableTime.relative', 0)
|
||||||
|
|
||||||
|
return moment()
|
||||||
|
.subtract(relative, 'seconds')
|
||||||
|
.toISOString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setTableCustomTime = (time: string): SetTableCustomTimeAction => ({
|
||||||
|
type: ActionTypes.SetTableCustomTime,
|
||||||
|
payload: {time},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const setTableRelativeTime = (
|
||||||
|
time: number
|
||||||
|
): SetTableRelativeTimeAction => ({
|
||||||
|
type: ActionTypes.SetTableRelativeTime,
|
||||||
|
payload: {time},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const setTableForwardData = (
|
||||||
|
data: TableData
|
||||||
|
): SetTableForwardDataAction => ({
|
||||||
|
type: ActionTypes.SetTableForwardData,
|
||||||
|
payload: {data},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const setTableBackwardData = (
|
||||||
|
data: TableData
|
||||||
|
): SetTableBackwardDataAction => ({
|
||||||
|
type: ActionTypes.SetTableBackwardData,
|
||||||
|
payload: {data},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const executeTableForwardQueryAsync = () => async (
|
||||||
|
dispatch,
|
||||||
|
getState: GetState
|
||||||
|
) => {
|
||||||
|
const state = getState()
|
||||||
|
|
||||||
|
const time = getTableSelectedTime(state)
|
||||||
|
const queryConfig = getTableQueryConfig(state)
|
||||||
|
const namespace = getNamespace(state)
|
||||||
|
const proxyLink = getProxyLink(state)
|
||||||
|
const searchTerm = getSearchTerm(state)
|
||||||
|
const filters = getFilters(state)
|
||||||
|
|
||||||
|
if (!_.every([queryConfig, time, namespace, proxyLink])) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
dispatch(incrementQueryCount())
|
||||||
|
|
||||||
|
const query = buildForwardLogQuery(time, queryConfig, filters, searchTerm)
|
||||||
|
const response = await executeQueryAsync(
|
||||||
|
proxyLink,
|
||||||
|
namespace,
|
||||||
|
`${query} ORDER BY time ASC LIMIT ${INITIAL_LIMIT}`
|
||||||
|
)
|
||||||
|
|
||||||
|
const series = getDeep(response, 'results.0.series.0', defaultTableData)
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
columns: series.columns,
|
||||||
|
values: _.reverse(series.values),
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(setTableForwardData(result))
|
||||||
|
} finally {
|
||||||
|
dispatch(decrementQueryCount())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const executeTableBackwardQueryAsync = () => async (
|
||||||
|
dispatch,
|
||||||
|
getState: GetState
|
||||||
|
) => {
|
||||||
|
const state = getState()
|
||||||
|
|
||||||
|
const time = getTableSelectedTime(state)
|
||||||
|
const queryConfig = getTableQueryConfig(state)
|
||||||
|
const namespace = getNamespace(state)
|
||||||
|
const proxyLink = getProxyLink(state)
|
||||||
|
const searchTerm = getSearchTerm(state)
|
||||||
|
const filters = getFilters(state)
|
||||||
|
|
||||||
|
if (!_.every([queryConfig, time, namespace, proxyLink])) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
dispatch(incrementQueryCount())
|
||||||
|
|
||||||
|
const query = buildBackwardLogQuery(time, queryConfig, filters, searchTerm)
|
||||||
|
const response = await executeQueryAsync(
|
||||||
|
proxyLink,
|
||||||
|
namespace,
|
||||||
|
`${query} ORDER BY time DESC LIMIT ${INITIAL_LIMIT}`
|
||||||
|
)
|
||||||
|
|
||||||
|
const series = getDeep(response, 'results.0.series.0', defaultTableData)
|
||||||
|
|
||||||
|
dispatch(setTableBackwardData(series))
|
||||||
|
} finally {
|
||||||
|
dispatch(decrementQueryCount())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setTableCustomTimeAsync = (time: string) => async dispatch => {
|
||||||
|
await dispatch(setTableCustomTime(time))
|
||||||
|
await dispatch(executeTableQueryAsync())
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setTableRelativeTimeAsync = (time: number) => async dispatch => {
|
||||||
|
await dispatch(setTableRelativeTime(time))
|
||||||
|
await dispatch(executeTableQueryAsync())
|
||||||
|
}
|
||||||
|
|
||||||
export const changeFilter = (id: string, operator: string, value: string) => ({
|
export const changeFilter = (id: string, operator: string, value: string) => ({
|
||||||
type: ActionTypes.ChangeFilter,
|
type: ActionTypes.ChangeFilter,
|
||||||
payload: {id, operator, value},
|
payload: {id, operator, value},
|
||||||
|
@ -271,44 +438,11 @@ export const executeHistogramQueryAsync = () => async (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const setTableData = (series: TableData): SetTableData => ({
|
export const executeTableQueryAsync = () => async (dispatch): Promise<void> => {
|
||||||
type: ActionTypes.SetTableData,
|
await Promise.all([
|
||||||
payload: {data: {columns: series.columns, values: series.values}},
|
dispatch(executeTableForwardQueryAsync()),
|
||||||
})
|
dispatch(executeTableBackwardQueryAsync()),
|
||||||
|
])
|
||||||
export const executeTableQueryAsync = () => async (
|
|
||||||
dispatch,
|
|
||||||
getState: GetState
|
|
||||||
): Promise<void> => {
|
|
||||||
const state = getState()
|
|
||||||
|
|
||||||
const queryConfig = getTableQueryConfig(state)
|
|
||||||
const timeRange = getTimeRange(state)
|
|
||||||
const namespace = getNamespace(state)
|
|
||||||
const proxyLink = getProxyLink(state)
|
|
||||||
const searchTerm = getSearchTerm(state)
|
|
||||||
const filters = getFilters(state)
|
|
||||||
|
|
||||||
if (!_.every([queryConfig, timeRange, namespace, proxyLink])) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
dispatch(incrementQueryCount())
|
|
||||||
|
|
||||||
const query = buildLogQuery(timeRange, queryConfig, filters, searchTerm)
|
|
||||||
const response = await executeQueryAsync(
|
|
||||||
proxyLink,
|
|
||||||
namespace,
|
|
||||||
`${query} ORDER BY time DESC LIMIT 1000`
|
|
||||||
)
|
|
||||||
|
|
||||||
const series = getDeep(response, 'results.0.series.0', defaultTableData)
|
|
||||||
|
|
||||||
dispatch(setTableData(series))
|
|
||||||
} finally {
|
|
||||||
dispatch(decrementQueryCount())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const decrementQueryCount = () => ({
|
export const decrementQueryCount = () => ({
|
||||||
|
|
|
@ -323,6 +323,7 @@ class LogsTable extends Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private loadMoreRows = async () => {
|
private loadMoreRows = async () => {
|
||||||
|
return
|
||||||
const data = getValuesFromData(this.props.data)
|
const data = getValuesFromData(this.props.data)
|
||||||
const {timeRange} = this.props
|
const {timeRange} = this.props
|
||||||
const lastTime = getDeep(
|
const lastTime = getDeep(
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
import React, {Component, MouseEvent} from 'react'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
||||||
|
import timePoints from 'src/logs/data/timePoints'
|
||||||
|
import {DROPDOWN_MENU_MAX_HEIGHT} from 'src/shared/constants/index'
|
||||||
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
import {ClickOutside} from 'src/shared/components/ClickOutside'
|
||||||
|
import CustomSingularTime from 'src/shared/components/CustomSingularTime'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
customTime?: string
|
||||||
|
relativeTime?: number
|
||||||
|
onChooseCustomTime: (time: string) => void
|
||||||
|
onChooseRelativeTime: (time: number) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
isOpen: boolean
|
||||||
|
isTimeSelectorOpen: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const dateFormat = 'YYYY-MM-DD HH:mm'
|
||||||
|
const format = t => moment(t.replace(/\'/g, '')).format(dateFormat)
|
||||||
|
|
||||||
|
@ErrorHandling
|
||||||
|
class TimeRangeDropdown extends Component<Props, State> {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isOpen: false,
|
||||||
|
isTimeSelectorOpen: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const {isTimeSelectorOpen} = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ClickOutside onClickOutside={this.handleClickOutside}>
|
||||||
|
<div className="time-range-dropdown" style={{display: 'inline'}}>
|
||||||
|
<div className={this.dropdownClassName}>
|
||||||
|
<div
|
||||||
|
className="btn btn-sm btn-default dropdown-toggle"
|
||||||
|
onClick={this.toggleMenu}
|
||||||
|
>
|
||||||
|
<span className="icon clock" />
|
||||||
|
<span className="dropdown-selected">{this.timeInputValue}</span>
|
||||||
|
<span className="caret" />
|
||||||
|
</div>
|
||||||
|
<ul className="dropdown-menu">
|
||||||
|
<FancyScrollbar
|
||||||
|
autoHide={false}
|
||||||
|
autoHeight={true}
|
||||||
|
maxHeight={DROPDOWN_MENU_MAX_HEIGHT}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<li className="dropdown-header">Absolute Time</li>
|
||||||
|
<li
|
||||||
|
className={
|
||||||
|
isTimeSelectorOpen
|
||||||
|
? 'active dropdown-item custom-timerange'
|
||||||
|
: 'dropdown-item custom-timerange'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<a href="#" onClick={this.handleOpenCustomTime}>
|
||||||
|
Date Picker
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</div>
|
||||||
|
<li className="dropdown-header">Relative Time</li>
|
||||||
|
{timePoints.map(point => {
|
||||||
|
return (
|
||||||
|
<li className="dropdown-item" key={`pot-${point.value}`}>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
onClick={this.handleSelection}
|
||||||
|
data-value={point.value}
|
||||||
|
>
|
||||||
|
{point.text}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</FancyScrollbar>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{isTimeSelectorOpen ? (
|
||||||
|
<ClickOutside onClickOutside={this.handleCloseCustomTime}>
|
||||||
|
<div className="custom-time--overlay">
|
||||||
|
<CustomSingularTime
|
||||||
|
onSelected={this.handleCustomSelection}
|
||||||
|
time={this.props.customTime}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ClickOutside>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</ClickOutside>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private get dropdownClassName(): string {
|
||||||
|
const {isOpen} = this.state
|
||||||
|
const absoluteTimeRange = !!this.props.customTime
|
||||||
|
|
||||||
|
return classnames('dropdown', {
|
||||||
|
'dropdown-290': absoluteTimeRange,
|
||||||
|
'dropdown-120': !absoluteTimeRange,
|
||||||
|
open: isOpen,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleCustomSelection = (time: string) => {
|
||||||
|
this.handleCloseCustomTime()
|
||||||
|
this.props.onChooseCustomTime(time)
|
||||||
|
this.setState({isOpen: false})
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleSelection = (e: MouseEvent<HTMLAnchorElement>) => {
|
||||||
|
e.preventDefault()
|
||||||
|
const {dataset} = e.target as HTMLAnchorElement
|
||||||
|
this.props.onChooseRelativeTime(+dataset.value)
|
||||||
|
this.setState({isOpen: false})
|
||||||
|
}
|
||||||
|
|
||||||
|
private get timeInputValue(): string {
|
||||||
|
if (!this.props.customTime) {
|
||||||
|
const point = timePoints.find(p => p.value === this.props.relativeTime)
|
||||||
|
if (point) {
|
||||||
|
return point.text
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'None'
|
||||||
|
}
|
||||||
|
|
||||||
|
return format(this.props.customTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleClickOutside = () => {
|
||||||
|
this.setState({isOpen: false})
|
||||||
|
}
|
||||||
|
|
||||||
|
private toggleMenu = () => {
|
||||||
|
this.setState({isOpen: !this.state.isOpen})
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleCloseCustomTime = () => {
|
||||||
|
this.setState({isTimeSelectorOpen: false})
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleOpenCustomTime = () => {
|
||||||
|
this.setState({isTimeSelectorOpen: true})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default TimeRangeDropdown
|
|
@ -8,7 +8,7 @@ import timeRanges from 'src/logs/data/timeRanges'
|
||||||
import {DROPDOWN_MENU_MAX_HEIGHT} from 'src/shared/constants/index'
|
import {DROPDOWN_MENU_MAX_HEIGHT} from 'src/shared/constants/index'
|
||||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
import {ClickOutside} from 'src/shared/components/ClickOutside'
|
import {ClickOutside} from 'src/shared/components/ClickOutside'
|
||||||
import CustomSingularTime from 'src/shared/components/CustomSingularTime'
|
import CustomTimeRange from 'src/shared/components/CustomTimeRange'
|
||||||
|
|
||||||
import {TimeRange} from 'src/types'
|
import {TimeRange} from 'src/types'
|
||||||
|
|
||||||
|
@ -23,8 +23,6 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
onChooseTimeRange: (timeRange: TimeRange) => void
|
onChooseTimeRange: (timeRange: TimeRange) => void
|
||||||
preventCustomTimeRange?: boolean
|
|
||||||
page?: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
|
@ -36,10 +34,6 @@ interface State {
|
||||||
|
|
||||||
@ErrorHandling
|
@ErrorHandling
|
||||||
class TimeRangeDropdown extends Component<Props, State> {
|
class TimeRangeDropdown extends Component<Props, State> {
|
||||||
public static defaultProps = {
|
|
||||||
page: 'default',
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
const {lower, upper} = props.selected
|
const {lower, upper} = props.selected
|
||||||
|
@ -56,7 +50,7 @@ class TimeRangeDropdown extends Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {selected, preventCustomTimeRange} = this.props
|
const {selected} = this.props
|
||||||
const {customTimeRange, isCustomTimeRangeOpen} = this.state
|
const {customTimeRange, isCustomTimeRangeOpen} = this.state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -79,25 +73,21 @@ class TimeRangeDropdown extends Component<Props, State> {
|
||||||
autoHeight={true}
|
autoHeight={true}
|
||||||
maxHeight={DROPDOWN_MENU_MAX_HEIGHT}
|
maxHeight={DROPDOWN_MENU_MAX_HEIGHT}
|
||||||
>
|
>
|
||||||
{preventCustomTimeRange ? null : (
|
<div>
|
||||||
<div>
|
<li className="dropdown-header">Absolute Time</li>
|
||||||
<li className="dropdown-header">Absolute Time</li>
|
<li
|
||||||
<li
|
className={
|
||||||
className={
|
isCustomTimeRangeOpen
|
||||||
isCustomTimeRangeOpen
|
? 'active dropdown-item custom-timerange'
|
||||||
? 'active dropdown-item custom-timerange'
|
: 'dropdown-item custom-timerange'
|
||||||
: 'dropdown-item custom-timerange'
|
}
|
||||||
}
|
>
|
||||||
>
|
<a href="#" onClick={this.showCustomTimeRange}>
|
||||||
<a href="#" onClick={this.showCustomTimeRange}>
|
Date Picker
|
||||||
Date Picker
|
</a>
|
||||||
</a>
|
</li>
|
||||||
</li>
|
</div>
|
||||||
</div>
|
<li className="dropdown-header">Relative Time</li>
|
||||||
)}
|
|
||||||
<li className="dropdown-header">
|
|
||||||
{preventCustomTimeRange ? '' : 'Relative '}Time
|
|
||||||
</li>
|
|
||||||
{timeRanges.map(item => {
|
{timeRanges.map(item => {
|
||||||
return (
|
return (
|
||||||
<li className="dropdown-item" key={item.menuOption}>
|
<li className="dropdown-item" key={item.menuOption}>
|
||||||
|
@ -113,11 +103,13 @@ class TimeRangeDropdown extends Component<Props, State> {
|
||||||
{isCustomTimeRangeOpen ? (
|
{isCustomTimeRangeOpen ? (
|
||||||
<ClickOutside onClickOutside={this.handleCloseCustomTimeRange}>
|
<ClickOutside onClickOutside={this.handleCloseCustomTimeRange}>
|
||||||
<div className="custom-time--overlay">
|
<div className="custom-time--overlay">
|
||||||
<CustomSingularTime
|
<CustomTimeRange
|
||||||
time={customTimeRange.lower}
|
onApplyTimeRange={this.handleApplyCustomTimeRange}
|
||||||
onSelected={this.handleApplyCustomTimeRange}
|
timeRange={customTimeRange}
|
||||||
onClose={this.handleCloseCustomTimeRange}
|
onClose={this.handleCloseCustomTimeRange}
|
||||||
|
isVisible={isCustomTimeRangeOpen}
|
||||||
timeInterval={300}
|
timeInterval={300}
|
||||||
|
page="default"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ClickOutside>
|
</ClickOutside>
|
||||||
|
@ -129,9 +121,7 @@ class TimeRangeDropdown extends Component<Props, State> {
|
||||||
|
|
||||||
private get dropdownClassName(): string {
|
private get dropdownClassName(): string {
|
||||||
const {isOpen} = this.state
|
const {isOpen} = this.state
|
||||||
|
|
||||||
const {lower, upper} = _.get(this.props, 'selected', {upper: '', lower: ''})
|
const {lower, upper} = _.get(this.props, 'selected', {upper: '', lower: ''})
|
||||||
|
|
||||||
const absoluteTimeRange = !_.isEmpty(lower) && !_.isEmpty(upper)
|
const absoluteTimeRange = !_.isEmpty(lower) && !_.isEmpty(upper)
|
||||||
|
|
||||||
return classnames('dropdown', {
|
return classnames('dropdown', {
|
||||||
|
|
|
@ -5,6 +5,8 @@ import {connect} from 'react-redux'
|
||||||
import {AutoSizer} from 'react-virtualized'
|
import {AutoSizer} from 'react-virtualized'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
setTableCustomTimeAsync,
|
||||||
|
setTableRelativeTimeAsync,
|
||||||
getSourceAndPopulateNamespacesAsync,
|
getSourceAndPopulateNamespacesAsync,
|
||||||
setTimeRangeAsync,
|
setTimeRangeAsync,
|
||||||
setNamespaceAsync,
|
setNamespaceAsync,
|
||||||
|
@ -26,14 +28,10 @@ import OptionsOverlay from 'src/logs/components/OptionsOverlay'
|
||||||
import SearchBar from 'src/logs/components/LogsSearchBar'
|
import SearchBar from 'src/logs/components/LogsSearchBar'
|
||||||
import FilterBar from 'src/logs/components/LogsFilterBar'
|
import FilterBar from 'src/logs/components/LogsFilterBar'
|
||||||
import LogsTable from 'src/logs/components/LogsTable'
|
import LogsTable from 'src/logs/components/LogsTable'
|
||||||
|
import PointInTimeDropDown from 'src/logs/components/PointInTimeDropDown'
|
||||||
import {getDeep} from 'src/utils/wrappers'
|
import {getDeep} from 'src/utils/wrappers'
|
||||||
import {colorForSeverity} from 'src/logs/utils/colors'
|
import {colorForSeverity} from 'src/logs/utils/colors'
|
||||||
import OverlayTechnology from 'src/reusable_ui/components/overlays/OverlayTechnology'
|
import OverlayTechnology from 'src/reusable_ui/components/overlays/OverlayTechnology'
|
||||||
import {
|
|
||||||
orderTableColumns,
|
|
||||||
filterTableColumns,
|
|
||||||
} from 'src/dashboards/utils/tableGraph'
|
|
||||||
|
|
||||||
import {SeverityFormatOptions} from 'src/logs/constants'
|
import {SeverityFormatOptions} from 'src/logs/constants'
|
||||||
import {Source, Namespace, TimeRange} from 'src/types'
|
import {Source, Namespace, TimeRange} from 'src/types'
|
||||||
|
|
||||||
|
@ -46,6 +44,7 @@ import {
|
||||||
LogConfig,
|
LogConfig,
|
||||||
TableData,
|
TableData,
|
||||||
} from 'src/types/logs'
|
} from 'src/types/logs'
|
||||||
|
import {applyChangesToTableData} from 'src/logs/utils/table'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
sources: Source[]
|
sources: Source[]
|
||||||
|
@ -59,6 +58,8 @@ interface Props {
|
||||||
changeZoomAsync: (timeRange: TimeRange) => void
|
changeZoomAsync: (timeRange: TimeRange) => void
|
||||||
executeQueriesAsync: () => void
|
executeQueriesAsync: () => void
|
||||||
setSearchTermAsync: (searchTerm: string) => void
|
setSearchTermAsync: (searchTerm: string) => void
|
||||||
|
setTableRelativeTime: (time: number) => void
|
||||||
|
setTableCustomTime: (time: string) => void
|
||||||
fetchMoreAsync: (queryTimeEnd: string, lastTime: number) => Promise<void>
|
fetchMoreAsync: (queryTimeEnd: string, lastTime: number) => Promise<void>
|
||||||
addFilter: (filter: Filter) => void
|
addFilter: (filter: Filter) => void
|
||||||
removeFilter: (id: string) => void
|
removeFilter: (id: string) => void
|
||||||
|
@ -73,6 +74,14 @@ interface Props {
|
||||||
queryCount: number
|
queryCount: number
|
||||||
logConfig: LogConfig
|
logConfig: LogConfig
|
||||||
logConfigLink: string
|
logConfigLink: string
|
||||||
|
tableInfiniteData: {
|
||||||
|
forward: TableData
|
||||||
|
backward: TableData
|
||||||
|
}
|
||||||
|
tableTime: {
|
||||||
|
custom: string
|
||||||
|
relative: number
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
|
@ -134,7 +143,7 @@ class LogsPage extends PureComponent<Props, State> {
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {liveUpdating} = this.state
|
const {liveUpdating} = this.state
|
||||||
const {searchTerm, filters, queryCount, timeRange} = this.props
|
const {searchTerm, filters, queryCount, timeRange, tableTime} = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -142,6 +151,17 @@ class LogsPage extends PureComponent<Props, State> {
|
||||||
{this.header}
|
{this.header}
|
||||||
<div className="page-contents logs-viewer">
|
<div className="page-contents logs-viewer">
|
||||||
<LogsGraphContainer>{this.chart}</LogsGraphContainer>
|
<LogsGraphContainer>{this.chart}</LogsGraphContainer>
|
||||||
|
<div style={{height: '50px', position: 'relative'}}>
|
||||||
|
<div style={{position: 'absolute', right: '10px', top: '10px'}}>
|
||||||
|
<span style={{marginRight: '10px'}}>Go to </span>
|
||||||
|
<PointInTimeDropDown
|
||||||
|
customTime={tableTime.custom}
|
||||||
|
relativeTime={tableTime.relative}
|
||||||
|
onChooseCustomTime={this.handleChooseCustomTime}
|
||||||
|
onChooseRelativeTime={this.handleChooseRelativeTime}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<SearchBar
|
<SearchBar
|
||||||
searchString={searchTerm}
|
searchString={searchTerm}
|
||||||
onSearch={this.handleSubmitSearch}
|
onSearch={this.handleSubmitSearch}
|
||||||
|
@ -174,21 +194,28 @@ class LogsPage extends PureComponent<Props, State> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private get tableData(): TableData {
|
private handleChooseCustomTime = (time: string) => {
|
||||||
const {tableData} = this.props
|
this.props.setTableCustomTime(time)
|
||||||
const tableColumns = this.tableColumns
|
}
|
||||||
const columns = _.get(tableData, 'columns', [])
|
|
||||||
const values = _.get(tableData, 'values', [])
|
|
||||||
const data = [columns, ...values]
|
|
||||||
|
|
||||||
const filteredData = filterTableColumns(data, tableColumns)
|
private handleChooseRelativeTime = (time: number) => {
|
||||||
const orderedData = orderTableColumns(filteredData, tableColumns)
|
this.props.setTableRelativeTime(time)
|
||||||
const updatedColumns: string[] = _.get(orderedData, '0', [])
|
}
|
||||||
const updatedValues = _.slice(orderedData, 1)
|
|
||||||
|
private get tableData(): TableData {
|
||||||
|
const forwardData = applyChangesToTableData(
|
||||||
|
this.props.tableInfiniteData.forward,
|
||||||
|
this.tableColumns
|
||||||
|
)
|
||||||
|
|
||||||
|
const backwardData = applyChangesToTableData(
|
||||||
|
this.props.tableInfiniteData.backward,
|
||||||
|
this.tableColumns
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
columns: updatedColumns,
|
columns: forwardData.columns,
|
||||||
values: updatedValues,
|
values: [...forwardData.values, ...backwardData.values],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,6 +463,8 @@ const mapStateToProps = ({
|
||||||
filters,
|
filters,
|
||||||
queryCount,
|
queryCount,
|
||||||
logConfig,
|
logConfig,
|
||||||
|
tableTime,
|
||||||
|
tableInfiniteData,
|
||||||
},
|
},
|
||||||
}) => ({
|
}) => ({
|
||||||
sources,
|
sources,
|
||||||
|
@ -449,7 +478,9 @@ const mapStateToProps = ({
|
||||||
filters,
|
filters,
|
||||||
queryCount,
|
queryCount,
|
||||||
logConfig,
|
logConfig,
|
||||||
|
tableTime,
|
||||||
logConfigLink: logViewer,
|
logConfigLink: logViewer,
|
||||||
|
tableInfiniteData,
|
||||||
})
|
})
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
|
@ -464,6 +495,8 @@ const mapDispatchToProps = {
|
||||||
removeFilter,
|
removeFilter,
|
||||||
changeFilter,
|
changeFilter,
|
||||||
fetchMoreAsync,
|
fetchMoreAsync,
|
||||||
|
setTableCustomTime: setTableCustomTimeAsync,
|
||||||
|
setTableRelativeTime: setTableRelativeTimeAsync,
|
||||||
getConfig: getLogConfigAsync,
|
getConfig: getLogConfigAsync,
|
||||||
updateConfig: updateLogConfigAsync,
|
updateConfig: updateLogConfigAsync,
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
export default {
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'severity',
|
||||||
|
position: 1,
|
||||||
|
encodings: [
|
||||||
|
{
|
||||||
|
type: 'visibility',
|
||||||
|
value: 'visible',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'label',
|
||||||
|
value: 'icon',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'label',
|
||||||
|
value: 'text',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'timestamp',
|
||||||
|
position: 2,
|
||||||
|
encodings: [
|
||||||
|
{
|
||||||
|
type: 'visibility',
|
||||||
|
value: 'visible',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'message',
|
||||||
|
position: 3,
|
||||||
|
encodings: [
|
||||||
|
{
|
||||||
|
type: 'visibility',
|
||||||
|
value: 'visible',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'facility',
|
||||||
|
position: 4,
|
||||||
|
encodings: [
|
||||||
|
{
|
||||||
|
type: 'visibility',
|
||||||
|
value: 'visible',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'time',
|
||||||
|
position: 0,
|
||||||
|
encodings: [
|
||||||
|
{
|
||||||
|
type: 'visibility',
|
||||||
|
value: 'hidden',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'procid',
|
||||||
|
position: 5,
|
||||||
|
encodings: [
|
||||||
|
{
|
||||||
|
type: 'visibility',
|
||||||
|
value: 'visible',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'displayName',
|
||||||
|
value: 'Proc ID',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'host',
|
||||||
|
position: 7,
|
||||||
|
encodings: [
|
||||||
|
{
|
||||||
|
type: 'visibility',
|
||||||
|
value: 'visible',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'appname',
|
||||||
|
position: 6,
|
||||||
|
encodings: [
|
||||||
|
{
|
||||||
|
type: 'visibility',
|
||||||
|
value: 'visible',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'displayName',
|
||||||
|
value: 'Application',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
text: '1 minute ago',
|
||||||
|
value: 60,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '5 minute ago',
|
||||||
|
value: 300,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '10 minute ago',
|
||||||
|
value: 600,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '30 minute ago',
|
||||||
|
value: 1800,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '1 hour ago',
|
||||||
|
value: 3600,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '3 hour ago',
|
||||||
|
value: 10800,
|
||||||
|
},
|
||||||
|
]
|
|
@ -13,7 +13,21 @@ import {
|
||||||
} from 'src/logs/actions'
|
} from 'src/logs/actions'
|
||||||
|
|
||||||
import {SeverityFormatOptions} from 'src/logs/constants'
|
import {SeverityFormatOptions} from 'src/logs/constants'
|
||||||
import {LogsState} from 'src/types/logs'
|
import {LogsState, TableData} from 'src/types/logs'
|
||||||
|
|
||||||
|
const defaultTableData: TableData = {
|
||||||
|
columns: [
|
||||||
|
'time',
|
||||||
|
'severity',
|
||||||
|
'timestamp',
|
||||||
|
'facility',
|
||||||
|
'procid',
|
||||||
|
'application',
|
||||||
|
'host',
|
||||||
|
'message',
|
||||||
|
],
|
||||||
|
values: [],
|
||||||
|
}
|
||||||
|
|
||||||
const defaultState: LogsState = {
|
const defaultState: LogsState = {
|
||||||
currentSource: null,
|
currentSource: null,
|
||||||
|
@ -32,6 +46,11 @@ const defaultState: LogsState = {
|
||||||
severityFormat: SeverityFormatOptions.dotText,
|
severityFormat: SeverityFormatOptions.dotText,
|
||||||
severityLevelColors: [],
|
severityLevelColors: [],
|
||||||
},
|
},
|
||||||
|
tableTime: {},
|
||||||
|
tableInfiniteData: {
|
||||||
|
forward: defaultTableData,
|
||||||
|
backward: defaultTableData,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeFilter = (
|
const removeFilter = (
|
||||||
|
@ -135,11 +154,31 @@ export default (state: LogsState = defaultState, action: Action) => {
|
||||||
return {...state, tableQueryConfig: action.payload.queryConfig}
|
return {...state, tableQueryConfig: action.payload.queryConfig}
|
||||||
case ActionTypes.SetTableData:
|
case ActionTypes.SetTableData:
|
||||||
return {...state, tableData: action.payload.data}
|
return {...state, tableData: action.payload.data}
|
||||||
|
case ActionTypes.SetTableForwardData:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
tableInfiniteData: {
|
||||||
|
...state.tableInfiniteData,
|
||||||
|
forward: action.payload.data,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case ActionTypes.SetTableBackwardData:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
tableInfiniteData: {
|
||||||
|
...state.tableInfiniteData,
|
||||||
|
backward: action.payload.data,
|
||||||
|
},
|
||||||
|
}
|
||||||
case ActionTypes.ChangeZoom:
|
case ActionTypes.ChangeZoom:
|
||||||
return {...state, timeRange: action.payload.timeRange}
|
return {...state, timeRange: action.payload.timeRange}
|
||||||
case ActionTypes.SetSearchTerm:
|
case ActionTypes.SetSearchTerm:
|
||||||
const {searchTerm} = action.payload
|
const {searchTerm} = action.payload
|
||||||
return {...state, searchTerm}
|
return {...state, searchTerm}
|
||||||
|
case ActionTypes.SetTableCustomTime:
|
||||||
|
return {...state, tableTime: {custom: action.payload.time}}
|
||||||
|
case ActionTypes.SetTableRelativeTime:
|
||||||
|
return {...state, tableTime: {relative: action.payload.time}}
|
||||||
case ActionTypes.AddFilter:
|
case ActionTypes.AddFilter:
|
||||||
return addFilter(state, action)
|
return addFilter(state, action)
|
||||||
case ActionTypes.RemoveFilter:
|
case ActionTypes.RemoveFilter:
|
||||||
|
|
|
@ -115,6 +115,99 @@ export const filtersClause = (filters: Filter[]): string => {
|
||||||
).join(' AND ')
|
).join(' AND ')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildInfiniteWhereClause({
|
||||||
|
lower,
|
||||||
|
upper,
|
||||||
|
tags,
|
||||||
|
areTagsAccepted,
|
||||||
|
}: QueryConfig): string {
|
||||||
|
const timeClauses = []
|
||||||
|
|
||||||
|
if (lower) {
|
||||||
|
timeClauses.push(`time >= '${lower}'`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (upper) {
|
||||||
|
timeClauses.push(`time < '${upper}'`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const tagClauses = _.keys(tags).map(k => {
|
||||||
|
const operator = areTagsAccepted ? '=' : '!='
|
||||||
|
|
||||||
|
if (tags[k].length > 1) {
|
||||||
|
const joinedOnOr = tags[k]
|
||||||
|
.map(v => `"${k}"${operator}'${v}'`)
|
||||||
|
.join(' OR ')
|
||||||
|
return `(${joinedOnOr})`
|
||||||
|
}
|
||||||
|
|
||||||
|
return `"${k}"${operator}'${tags[k]}'`
|
||||||
|
})
|
||||||
|
|
||||||
|
const subClauses = timeClauses.concat(tagClauses)
|
||||||
|
if (!subClauses.length) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
return ` WHERE ${subClauses.join(' AND ')}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildGeneralLogQuery(
|
||||||
|
condition: string,
|
||||||
|
config: QueryConfig,
|
||||||
|
filters: Filter[],
|
||||||
|
searchTerm: string | null = null
|
||||||
|
) {
|
||||||
|
const {groupBy, fill = NULL_STRING} = config
|
||||||
|
const select = buildSelect(config, '')
|
||||||
|
const dimensions = buildGroupBy(groupBy)
|
||||||
|
const fillClause = groupBy.time ? buildFill(fill) : ''
|
||||||
|
|
||||||
|
if (!_.isEmpty(searchTerm)) {
|
||||||
|
condition = `${condition} AND message =~ ${new RegExp(searchTerm)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_.isEmpty(filters)) {
|
||||||
|
condition = `${condition} AND ${filtersClause(filters)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${select}${condition}${dimensions}${fillClause}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildBackwardLogQuery(
|
||||||
|
upper: string,
|
||||||
|
config: QueryConfig,
|
||||||
|
filters: Filter[],
|
||||||
|
searchTerm: string | null = null
|
||||||
|
) {
|
||||||
|
const {tags, areTagsAccepted} = config
|
||||||
|
|
||||||
|
const condition = buildInfiniteWhereClause({
|
||||||
|
upper,
|
||||||
|
tags,
|
||||||
|
areTagsAccepted,
|
||||||
|
})
|
||||||
|
|
||||||
|
return buildGeneralLogQuery(condition, config, filters, searchTerm)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildForwardLogQuery(
|
||||||
|
lower: string,
|
||||||
|
config: QueryConfig,
|
||||||
|
filters: Filter[],
|
||||||
|
searchTerm: string | null = null
|
||||||
|
) {
|
||||||
|
const {tags, areTagsAccepted} = config
|
||||||
|
|
||||||
|
const condition = buildInfiniteWhereClause({
|
||||||
|
lower,
|
||||||
|
tags,
|
||||||
|
areTagsAccepted,
|
||||||
|
})
|
||||||
|
|
||||||
|
return buildGeneralLogQuery(condition, config, filters, searchTerm)
|
||||||
|
}
|
||||||
|
|
||||||
export function buildLogQuery(
|
export function buildLogQuery(
|
||||||
timeRange: TimeRange,
|
timeRange: TimeRange,
|
||||||
config: QueryConfig,
|
config: QueryConfig,
|
||||||
|
|
|
@ -3,6 +3,10 @@ import moment from 'moment'
|
||||||
import {getDeep} from 'src/utils/wrappers'
|
import {getDeep} from 'src/utils/wrappers'
|
||||||
import {TableData, LogsTableColumn, SeverityFormat} from 'src/types/logs'
|
import {TableData, LogsTableColumn, SeverityFormat} from 'src/types/logs'
|
||||||
import {SeverityFormatOptions} from 'src/logs/constants'
|
import {SeverityFormatOptions} from 'src/logs/constants'
|
||||||
|
import {
|
||||||
|
orderTableColumns,
|
||||||
|
filterTableColumns,
|
||||||
|
} from 'src/dashboards/utils/tableGraph'
|
||||||
|
|
||||||
export const ROW_HEIGHT = 26
|
export const ROW_HEIGHT = 26
|
||||||
const CHAR_WIDTH = 9
|
const CHAR_WIDTH = 9
|
||||||
|
@ -119,3 +123,22 @@ export const getMessageWidth = (
|
||||||
|
|
||||||
return calculatedWidth - CHAR_WIDTH
|
return calculatedWidth - CHAR_WIDTH
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const applyChangesToTableData = (
|
||||||
|
tableData: TableData,
|
||||||
|
tableColumns: LogsTableColumn[]
|
||||||
|
): TableData => {
|
||||||
|
const columns = _.get(tableData, 'columns', [])
|
||||||
|
const values = _.get(tableData, 'values', [])
|
||||||
|
const data = [columns, ...values]
|
||||||
|
|
||||||
|
const filteredData = filterTableColumns(data, tableColumns)
|
||||||
|
const orderedData = orderTableColumns(filteredData, tableColumns)
|
||||||
|
const updatedColumns: string[] = _.get(orderedData, '0', [])
|
||||||
|
const updatedValues = _.slice(orderedData, 1)
|
||||||
|
|
||||||
|
return {
|
||||||
|
columns: updatedColumns,
|
||||||
|
values: updatedValues,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,10 +3,8 @@ import rome from 'rome'
|
||||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
import {formatTimeRange} from 'src/shared/utils/time'
|
import {formatTimeRange} from 'src/shared/utils/time'
|
||||||
|
|
||||||
import {TimeRange} from 'src/types'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onSelected: (timeRange: TimeRange) => void
|
onSelected: (time: string) => void
|
||||||
time: string
|
time: string
|
||||||
timeInterval?: number
|
timeInterval?: number
|
||||||
onClose?: () => void
|
onClose?: () => void
|
||||||
|
@ -66,6 +64,7 @@ class CustomSingularTime extends Component<Props, State> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
style={{marginTop: '10px'}}
|
||||||
className="custom-time--apply btn btn-sm btn-primary"
|
className="custom-time--apply btn btn-sm btn-primary"
|
||||||
onClick={this.handleClick}
|
onClick={this.handleClick}
|
||||||
>
|
>
|
||||||
|
@ -85,8 +84,8 @@ class CustomSingularTime extends Component<Props, State> {
|
||||||
private handleClick = () => {
|
private handleClick = () => {
|
||||||
const date = this.calendar.getDate()
|
const date = this.calendar.getDate()
|
||||||
if (date) {
|
if (date) {
|
||||||
const lower = date.toISOString()
|
const time = date.toISOString()
|
||||||
this.props.onSelected({lower, upper: 'now()'})
|
this.props.onSelected(time)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.onClose) {
|
if (this.props.onClose) {
|
||||||
|
|
|
@ -32,6 +32,14 @@ export interface LogsState {
|
||||||
filters: Filter[]
|
filters: Filter[]
|
||||||
queryCount: number
|
queryCount: number
|
||||||
logConfig: LogConfig
|
logConfig: LogConfig
|
||||||
|
tableInfiniteData: {
|
||||||
|
forward: TableData
|
||||||
|
backward: TableData
|
||||||
|
}
|
||||||
|
tableTime: {
|
||||||
|
custom?: string
|
||||||
|
relative?: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LogConfig {
|
export interface LogConfig {
|
||||||
|
|
Loading…
Reference in New Issue