diff --git a/ui/src/logs/actions/index.ts b/ui/src/logs/actions/index.ts index 60bffbf38e..e2769d3d55 100644 --- a/ui/src/logs/actions/index.ts +++ b/ui/src/logs/actions/index.ts @@ -9,6 +9,8 @@ import { buildHistogramQueryConfig, buildTableQueryConfig, buildLogQuery, + buildForwardLogQuery, + buildBackwardLogQuery, parseHistogramQueryResponse, } from 'src/logs/utils' import { @@ -21,8 +23,11 @@ import { // getLogConfig as getLogConfigAJAX, // updateLogConfig as updateLogConfigAJAX, } from 'src/logs/api' +import serverLogData from 'src/logs/data/serverLogData' import {LogsState, Filter, TableData, LogConfig} from 'src/types/logs' +const INITIAL_LIMIT = 1000 + const defaultTableData: TableData = { columns: [ 'time', @@ -61,7 +66,40 @@ export enum ActionTypes { DecrementQueryCount = 'LOGS_DECREMENT_QUERY_COUNT', ConcatMoreLogs = 'LOGS_CONCAT_MORE_LOGS', 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 { type: ActionTypes.ConcatMoreLogs payload: { @@ -194,6 +232,10 @@ export type Action = | IncrementQueryCountAction | ConcatMoreLogsAction | SetConfigsAction + | SetTableCustomTimeAction + | SetTableRelativeTimeAction + | SetTableForwardDataAction + | SetTableBackwardDataAction const getTimeRange = (state: State): TimeRange | null => getDeep(state, 'logs.timeRange', null) @@ -216,6 +258,131 @@ const getSearchTerm = (state: State): string | null => const getFilters = (state: State): Filter[] => getDeep(state, 'logs.filters', []) +const getTableSelectedTime = (state: State): string => { + const custom = getDeep(state, 'logs.tableTime.custom', '') + + if (!_.isEmpty(custom)) { + return custom + } + + const relative = getDeep(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) => ({ type: ActionTypes.ChangeFilter, payload: {id, operator, value}, @@ -271,44 +438,11 @@ export const executeHistogramQueryAsync = () => async ( } } -const setTableData = (series: TableData): SetTableData => ({ - type: ActionTypes.SetTableData, - payload: {data: {columns: series.columns, values: series.values}}, -}) - -export const executeTableQueryAsync = () => async ( - dispatch, - getState: GetState -): Promise => { - 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 executeTableQueryAsync = () => async (dispatch): Promise => { + await Promise.all([ + dispatch(executeTableForwardQueryAsync()), + dispatch(executeTableBackwardQueryAsync()), + ]) } export const decrementQueryCount = () => ({ diff --git a/ui/src/logs/components/LogsTable.tsx b/ui/src/logs/components/LogsTable.tsx index db878a03f8..200208a81f 100644 --- a/ui/src/logs/components/LogsTable.tsx +++ b/ui/src/logs/components/LogsTable.tsx @@ -323,6 +323,7 @@ class LogsTable extends Component { } private loadMoreRows = async () => { + return const data = getValuesFromData(this.props.data) const {timeRange} = this.props const lastTime = getDeep( diff --git a/ui/src/logs/components/PointInTimeDropDown.tsx b/ui/src/logs/components/PointInTimeDropDown.tsx new file mode 100644 index 0000000000..15d8a7af2f --- /dev/null +++ b/ui/src/logs/components/PointInTimeDropDown.tsx @@ -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 { + constructor(props) { + super(props) + + this.state = { + isOpen: false, + isTimeSelectorOpen: false, + } + } + + public render() { + const {isTimeSelectorOpen} = this.state + + return ( + +
+
+
+ + {this.timeInputValue} + +
+ +
+ {isTimeSelectorOpen ? ( + +
+ +
+
+ ) : null} +
+
+ ) + } + + 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) => { + 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 diff --git a/ui/src/logs/components/TimeRangeDropdown.tsx b/ui/src/logs/components/TimeRangeDropdown.tsx index d34ae2a3df..9bcb7debe1 100644 --- a/ui/src/logs/components/TimeRangeDropdown.tsx +++ b/ui/src/logs/components/TimeRangeDropdown.tsx @@ -8,7 +8,7 @@ import timeRanges from 'src/logs/data/timeRanges' 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' +import CustomTimeRange from 'src/shared/components/CustomTimeRange' import {TimeRange} from 'src/types' @@ -23,8 +23,6 @@ interface Props { } onChooseTimeRange: (timeRange: TimeRange) => void - preventCustomTimeRange?: boolean - page?: string } interface State { @@ -36,10 +34,6 @@ interface State { @ErrorHandling class TimeRangeDropdown extends Component { - public static defaultProps = { - page: 'default', - } - constructor(props) { super(props) const {lower, upper} = props.selected @@ -56,7 +50,7 @@ class TimeRangeDropdown extends Component { } public render() { - const {selected, preventCustomTimeRange} = this.props + const {selected} = this.props const {customTimeRange, isCustomTimeRangeOpen} = this.state return ( @@ -79,25 +73,21 @@ class TimeRangeDropdown extends Component { autoHeight={true} maxHeight={DROPDOWN_MENU_MAX_HEIGHT} > - {preventCustomTimeRange ? null : ( -
-
  • Absolute Time
  • -
  • - - Date Picker - -
  • -
    - )} -
  • - {preventCustomTimeRange ? '' : 'Relative '}Time -
  • +
    +
  • Absolute Time
  • +
  • + + Date Picker + +
  • +
    +
  • Relative Time
  • {timeRanges.map(item => { return (
  • @@ -113,11 +103,13 @@ class TimeRangeDropdown extends Component { {isCustomTimeRangeOpen ? (
    -
    @@ -129,9 +121,7 @@ class TimeRangeDropdown extends Component { private get dropdownClassName(): string { const {isOpen} = this.state - const {lower, upper} = _.get(this.props, 'selected', {upper: '', lower: ''}) - const absoluteTimeRange = !_.isEmpty(lower) && !_.isEmpty(upper) return classnames('dropdown', { diff --git a/ui/src/logs/containers/LogsPage.tsx b/ui/src/logs/containers/LogsPage.tsx index 26c2127f90..4105d31c5d 100644 --- a/ui/src/logs/containers/LogsPage.tsx +++ b/ui/src/logs/containers/LogsPage.tsx @@ -5,6 +5,8 @@ import {connect} from 'react-redux' import {AutoSizer} from 'react-virtualized' import { + setTableCustomTimeAsync, + setTableRelativeTimeAsync, getSourceAndPopulateNamespacesAsync, setTimeRangeAsync, setNamespaceAsync, @@ -26,14 +28,10 @@ import OptionsOverlay from 'src/logs/components/OptionsOverlay' import SearchBar from 'src/logs/components/LogsSearchBar' import FilterBar from 'src/logs/components/LogsFilterBar' import LogsTable from 'src/logs/components/LogsTable' +import PointInTimeDropDown from 'src/logs/components/PointInTimeDropDown' import {getDeep} from 'src/utils/wrappers' import {colorForSeverity} from 'src/logs/utils/colors' import OverlayTechnology from 'src/reusable_ui/components/overlays/OverlayTechnology' -import { - orderTableColumns, - filterTableColumns, -} from 'src/dashboards/utils/tableGraph' - import {SeverityFormatOptions} from 'src/logs/constants' import {Source, Namespace, TimeRange} from 'src/types' @@ -46,6 +44,7 @@ import { LogConfig, TableData, } from 'src/types/logs' +import {applyChangesToTableData} from 'src/logs/utils/table' interface Props { sources: Source[] @@ -59,6 +58,8 @@ interface Props { changeZoomAsync: (timeRange: TimeRange) => void executeQueriesAsync: () => void setSearchTermAsync: (searchTerm: string) => void + setTableRelativeTime: (time: number) => void + setTableCustomTime: (time: string) => void fetchMoreAsync: (queryTimeEnd: string, lastTime: number) => Promise addFilter: (filter: Filter) => void removeFilter: (id: string) => void @@ -73,6 +74,14 @@ interface Props { queryCount: number logConfig: LogConfig logConfigLink: string + tableInfiniteData: { + forward: TableData + backward: TableData + } + tableTime: { + custom: string + relative: number + } } interface State { @@ -134,7 +143,7 @@ class LogsPage extends PureComponent { public render() { const {liveUpdating} = this.state - const {searchTerm, filters, queryCount, timeRange} = this.props + const {searchTerm, filters, queryCount, timeRange, tableTime} = this.props return ( <> @@ -142,6 +151,17 @@ class LogsPage extends PureComponent { {this.header}
    {this.chart} +
    +
    + Go to + +
    +
    { ) } - private get tableData(): TableData { - const {tableData} = this.props - const tableColumns = this.tableColumns - const columns = _.get(tableData, 'columns', []) - const values = _.get(tableData, 'values', []) - const data = [columns, ...values] + private handleChooseCustomTime = (time: string) => { + this.props.setTableCustomTime(time) + } - const filteredData = filterTableColumns(data, tableColumns) - const orderedData = orderTableColumns(filteredData, tableColumns) - const updatedColumns: string[] = _.get(orderedData, '0', []) - const updatedValues = _.slice(orderedData, 1) + private handleChooseRelativeTime = (time: number) => { + this.props.setTableRelativeTime(time) + } + + private get tableData(): TableData { + const forwardData = applyChangesToTableData( + this.props.tableInfiniteData.forward, + this.tableColumns + ) + + const backwardData = applyChangesToTableData( + this.props.tableInfiniteData.backward, + this.tableColumns + ) return { - columns: updatedColumns, - values: updatedValues, + columns: forwardData.columns, + values: [...forwardData.values, ...backwardData.values], } } @@ -436,6 +463,8 @@ const mapStateToProps = ({ filters, queryCount, logConfig, + tableTime, + tableInfiniteData, }, }) => ({ sources, @@ -449,7 +478,9 @@ const mapStateToProps = ({ filters, queryCount, logConfig, + tableTime, logConfigLink: logViewer, + tableInfiniteData, }) const mapDispatchToProps = { @@ -464,6 +495,8 @@ const mapDispatchToProps = { removeFilter, changeFilter, fetchMoreAsync, + setTableCustomTime: setTableCustomTimeAsync, + setTableRelativeTime: setTableRelativeTimeAsync, getConfig: getLogConfigAsync, updateConfig: updateLogConfigAsync, } diff --git a/ui/src/logs/data/serverLogData.ts b/ui/src/logs/data/serverLogData.ts new file mode 100644 index 0000000000..ab006ee2e6 --- /dev/null +++ b/ui/src/logs/data/serverLogData.ts @@ -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', + }, + ], + }, + ], +} diff --git a/ui/src/logs/data/timePoints.ts b/ui/src/logs/data/timePoints.ts new file mode 100644 index 0000000000..fa07be2dac --- /dev/null +++ b/ui/src/logs/data/timePoints.ts @@ -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, + }, +] diff --git a/ui/src/logs/reducers/index.ts b/ui/src/logs/reducers/index.ts index e930904d05..fcd7683f2e 100644 --- a/ui/src/logs/reducers/index.ts +++ b/ui/src/logs/reducers/index.ts @@ -13,7 +13,21 @@ import { } from 'src/logs/actions' 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 = { currentSource: null, @@ -32,6 +46,11 @@ const defaultState: LogsState = { severityFormat: SeverityFormatOptions.dotText, severityLevelColors: [], }, + tableTime: {}, + tableInfiniteData: { + forward: defaultTableData, + backward: defaultTableData, + }, } const removeFilter = ( @@ -135,11 +154,31 @@ export default (state: LogsState = defaultState, action: Action) => { return {...state, tableQueryConfig: action.payload.queryConfig} case ActionTypes.SetTableData: 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: return {...state, timeRange: action.payload.timeRange} case ActionTypes.SetSearchTerm: const {searchTerm} = action.payload 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: return addFilter(state, action) case ActionTypes.RemoveFilter: diff --git a/ui/src/logs/utils/index.ts b/ui/src/logs/utils/index.ts index 989f089c3c..5dec529e9a 100644 --- a/ui/src/logs/utils/index.ts +++ b/ui/src/logs/utils/index.ts @@ -115,6 +115,99 @@ export const filtersClause = (filters: Filter[]): string => { ).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( timeRange: TimeRange, config: QueryConfig, diff --git a/ui/src/logs/utils/table.ts b/ui/src/logs/utils/table.ts index 786d4a9d68..02464e4f28 100644 --- a/ui/src/logs/utils/table.ts +++ b/ui/src/logs/utils/table.ts @@ -3,6 +3,10 @@ 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 { + orderTableColumns, + filterTableColumns, +} from 'src/dashboards/utils/tableGraph' export const ROW_HEIGHT = 26 const CHAR_WIDTH = 9 @@ -119,3 +123,22 @@ export const getMessageWidth = ( 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, + } +} diff --git a/ui/src/shared/components/CustomSingularTime.tsx b/ui/src/shared/components/CustomSingularTime.tsx index c6757216a9..b2c119ec19 100644 --- a/ui/src/shared/components/CustomSingularTime.tsx +++ b/ui/src/shared/components/CustomSingularTime.tsx @@ -3,10 +3,8 @@ import rome from 'rome' import {ErrorHandling} from 'src/shared/decorators/errors' import {formatTimeRange} from 'src/shared/utils/time' -import {TimeRange} from 'src/types' - interface Props { - onSelected: (timeRange: TimeRange) => void + onSelected: (time: string) => void time: string timeInterval?: number onClose?: () => void @@ -66,6 +64,7 @@ class CustomSingularTime extends Component {
    @@ -85,8 +84,8 @@ class CustomSingularTime extends Component { private handleClick = () => { const date = this.calendar.getDate() if (date) { - const lower = date.toISOString() - this.props.onSelected({lower, upper: 'now()'}) + const time = date.toISOString() + this.props.onSelected(time) } if (this.props.onClose) { diff --git a/ui/src/types/logs.ts b/ui/src/types/logs.ts index 404032261b..c08441cdb8 100644 --- a/ui/src/types/logs.ts +++ b/ui/src/types/logs.ts @@ -32,6 +32,14 @@ export interface LogsState { filters: Filter[] queryCount: number logConfig: LogConfig + tableInfiniteData: { + forward: TableData + backward: TableData + } + tableTime: { + custom?: string + relative?: string + } } export interface LogConfig {