Connect UI and Server for Log Viewer table configs
parent
a974a9ea46
commit
7bc2e9c274
|
@ -1,5 +1,7 @@
|
|||
import moment from 'moment'
|
||||
import _ from 'lodash'
|
||||
import {Dispatch} from 'redux'
|
||||
|
||||
import {Source, Namespace, TimeRange, QueryConfig} from 'src/types'
|
||||
import {getSource} from 'src/shared/apis'
|
||||
import {getDatabasesWithRetentionPolicies} from 'src/shared/apis/databases'
|
||||
|
@ -9,16 +11,20 @@ import {
|
|||
buildLogQuery,
|
||||
parseHistogramQueryResponse,
|
||||
} from 'src/logs/utils'
|
||||
import {logConfigServerToUI, logConfigUIToServer} from 'src/logs/utils/config'
|
||||
import {getDeep} from 'src/utils/wrappers'
|
||||
import {executeQueryAsync} from 'src/logs/api'
|
||||
import {LogsState, Filter, TableData} from 'src/types/logs'
|
||||
import {
|
||||
executeQueryAsync,
|
||||
getLogConfig as getLogConfigAJAX,
|
||||
updateLogConfig as updateLogConfigAJAX,
|
||||
} from 'src/logs/api'
|
||||
import {LogsState, Filter, TableData, LogConfig} from 'src/types/logs'
|
||||
|
||||
const defaultTableData: TableData = {
|
||||
columns: [
|
||||
'time',
|
||||
'severity',
|
||||
'timestamp',
|
||||
'severity_1',
|
||||
'facility',
|
||||
'procid',
|
||||
'application',
|
||||
|
@ -51,6 +57,7 @@ export enum ActionTypes {
|
|||
IncrementQueryCount = 'LOGS_INCREMENT_QUERY_COUNT',
|
||||
DecrementQueryCount = 'LOGS_DECREMENT_QUERY_COUNT',
|
||||
ConcatMoreLogs = 'LOGS_CONCAT_MORE_LOGS',
|
||||
SetConfig = 'SET_CONFIG',
|
||||
}
|
||||
|
||||
export interface ConcatMoreLogsAction {
|
||||
|
@ -160,6 +167,13 @@ interface ChangeZoomAction {
|
|||
}
|
||||
}
|
||||
|
||||
export interface SetConfigsAction {
|
||||
type: ActionTypes.SetConfig
|
||||
payload: {
|
||||
logConfig: LogConfig
|
||||
}
|
||||
}
|
||||
|
||||
export type Action =
|
||||
| SetSourceAction
|
||||
| SetNamespacesAction
|
||||
|
@ -177,6 +191,7 @@ export type Action =
|
|||
| DecrementQueryCountAction
|
||||
| IncrementQueryCountAction
|
||||
| ConcatMoreLogsAction
|
||||
| SetConfigsAction
|
||||
|
||||
const getTimeRange = (state: State): TimeRange | null =>
|
||||
getDeep<TimeRange | null>(state, 'logs.timeRange', null)
|
||||
|
@ -485,3 +500,37 @@ export const changeZoomAsync = (timeRange: TimeRange) => async (
|
|||
await dispatch(setTimeRangeAsync(timeRange))
|
||||
}
|
||||
}
|
||||
|
||||
export const getLogConfigAsync = (url: string) => async (
|
||||
dispatch: Dispatch<SetConfigsAction>
|
||||
): Promise<void> => {
|
||||
url = url
|
||||
try {
|
||||
const {data} = await getLogConfigAJAX(url) // TODO: uncomment and replace following line when backend is ready
|
||||
const logConfig = logConfigServerToUI(data)
|
||||
dispatch(setConfig(logConfig))
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
export const updateLogConfigAsync = (url: string, config: LogConfig) => async (
|
||||
dispatch: Dispatch<SetConfigsAction>
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const configForServer = logConfigUIToServer(config)
|
||||
await updateLogConfigAJAX(url, configForServer) // TODO: uncomment when backend is ready
|
||||
dispatch(setConfig(config))
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
export const setConfig = (logConfig: LogConfig): SetConfigsAction => {
|
||||
return {
|
||||
type: ActionTypes.SetConfig,
|
||||
payload: {
|
||||
logConfig,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import {proxy} from 'src/utils/queryUrlGenerator'
|
||||
import AJAX from 'src/utils/ajax'
|
||||
import {Namespace} from 'src/types'
|
||||
import {TimeSeriesResponse} from 'src/types/series'
|
||||
import {ServerLogConfig} from 'src/types/logs'
|
||||
|
||||
export const executeQueryAsync = async (
|
||||
proxyLink: string,
|
||||
|
@ -20,3 +22,31 @@ export const executeQueryAsync = async (
|
|||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export const getLogConfig = async (url: string) => {
|
||||
try {
|
||||
return await AJAX({
|
||||
method: 'GET',
|
||||
url,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export const updateLogConfig = async (
|
||||
url: string,
|
||||
logConfig: ServerLogConfig
|
||||
) => {
|
||||
try {
|
||||
return await AJAX({
|
||||
method: 'PUT',
|
||||
url,
|
||||
data: logConfig,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ class LogViewerHeader extends PureComponent<Props> {
|
|||
}
|
||||
|
||||
private get optionsComponents(): JSX.Element {
|
||||
const {timeRange} = this.props
|
||||
const {timeRange, onShowOptionsOverlay} = this.props
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -66,12 +66,7 @@ class LogsFilter extends PureComponent<Props, State> {
|
|||
filter: {key, operator, value},
|
||||
} = this.props
|
||||
|
||||
let displayKey = key
|
||||
if (key === 'severity_1') {
|
||||
displayKey = 'severity'
|
||||
}
|
||||
|
||||
return <span>{`${displayKey} ${operator} ${value}`}</span>
|
||||
return <span>{`${key} ${operator} ${value}`}</span>
|
||||
}
|
||||
|
||||
private get renderEditor(): JSX.Element {
|
||||
|
|
|
@ -22,16 +22,15 @@ import {
|
|||
} from 'src/logs/utils/table'
|
||||
|
||||
import timeRanges from 'src/logs/data/timeRanges'
|
||||
import {SeverityFormatOptions} from 'src/logs/constants'
|
||||
|
||||
import {TimeRange} from 'src/types'
|
||||
import {TableData, LogsTableColumn, SeverityFormat} from 'src/types/logs'
|
||||
|
||||
const ROW_HEIGHT = 26
|
||||
const CHAR_WIDTH = 9
|
||||
interface Props {
|
||||
data: {
|
||||
columns: string[]
|
||||
values: string[]
|
||||
}
|
||||
data: TableData
|
||||
isScrolledToTop: boolean
|
||||
onScrollVertical: () => void
|
||||
onScrolledToTop: () => void
|
||||
|
@ -39,6 +38,8 @@ interface Props {
|
|||
fetchMore: (queryTimeEnd: string, time: number) => Promise<void>
|
||||
count: number
|
||||
timeRange: TimeRange
|
||||
tableColumns: LogsTableColumn[]
|
||||
severityFormat: SeverityFormat
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
@ -69,7 +70,11 @@ class LogsTable extends Component<Props, State> {
|
|||
scrollTop,
|
||||
scrollLeft,
|
||||
currentRow: -1,
|
||||
currentMessageWidth: getMessageWidth(props.data),
|
||||
currentMessageWidth: getMessageWidth(
|
||||
props.data,
|
||||
props.tableColumns,
|
||||
props.severityFormat
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,10 +126,7 @@ class LogsTable extends Component<Props, State> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const columnCount = Math.max(
|
||||
getColumnsFromData(this.props.data).length - 1,
|
||||
0
|
||||
)
|
||||
const columnCount = Math.max(getColumnsFromData(this.props.data).length, 0)
|
||||
|
||||
if (this.isTableEmpty) {
|
||||
return this.emptyTable
|
||||
|
@ -273,21 +275,32 @@ class LogsTable extends Component<Props, State> {
|
|||
}
|
||||
|
||||
private handleWindowResize = () => {
|
||||
this.setState({currentMessageWidth: getMessageWidth(this.props.data)})
|
||||
this.setState({
|
||||
currentMessageWidth: getMessageWidth(
|
||||
this.props.data,
|
||||
this.props.tableColumns,
|
||||
this.props.severityFormat
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
private handleHeaderScroll = ({scrollLeft}): void =>
|
||||
this.setState({scrollLeft})
|
||||
|
||||
private getColumnWidth = ({index}: {index: number}): number => {
|
||||
const column = getColumnFromData(this.props.data, index + 1)
|
||||
const {severityFormat} = this.props
|
||||
const column = getColumnFromData(this.props.data, index)
|
||||
const {currentMessageWidth} = this.state
|
||||
|
||||
switch (column) {
|
||||
case 'message':
|
||||
return currentMessageWidth
|
||||
default:
|
||||
return getColumnWidth(column)
|
||||
let columnKey = column
|
||||
if (column === 'severity') {
|
||||
columnKey = `${column}_${severityFormat}`
|
||||
}
|
||||
return getColumnWidth(columnKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -327,33 +340,67 @@ class LogsTable extends Component<Props, State> {
|
|||
}
|
||||
|
||||
private headerRenderer = ({key, style, columnIndex}) => {
|
||||
const column = getColumnFromData(this.props.data, columnIndex + 1)
|
||||
const column = getColumnFromData(this.props.data, columnIndex)
|
||||
const classes = 'logs-viewer--cell logs-viewer--cell-header'
|
||||
|
||||
let columnKey: string = column
|
||||
|
||||
if (column === 'severity') {
|
||||
columnKey = this.getSeverityColumn(column)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes} style={style} key={key}>
|
||||
{header(column)}
|
||||
{header(columnKey, this.props.tableColumns)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private getSeverityColumn(column: string): string {
|
||||
const {severityFormat} = this.props
|
||||
if (severityFormat === SeverityFormatOptions.dot) {
|
||||
return SeverityFormatOptions.dot
|
||||
}
|
||||
return column
|
||||
}
|
||||
|
||||
private getSeverityDotText(text: string): JSX.Element {
|
||||
const {severityFormat} = this.props
|
||||
if (severityFormat === SeverityFormatOptions.dotText) {
|
||||
return <span style={{padding: '5px'}}>{text}</span>
|
||||
}
|
||||
}
|
||||
|
||||
private cellRenderer = ({key, style, rowIndex, columnIndex}) => {
|
||||
const column = getColumnFromData(this.props.data, columnIndex + 1)
|
||||
const value = getValueFromData(this.props.data, rowIndex, columnIndex + 1)
|
||||
const {severityFormat} = this.props
|
||||
|
||||
const column = getColumnFromData(this.props.data, columnIndex)
|
||||
const value = getValueFromData(this.props.data, rowIndex, columnIndex)
|
||||
|
||||
let formattedValue: string | JSX.Element
|
||||
if (column === 'severity') {
|
||||
const isDotNeeded =
|
||||
severityFormat === SeverityFormatOptions.dot ||
|
||||
severityFormat === SeverityFormatOptions.dotText
|
||||
|
||||
let title: string
|
||||
|
||||
if (column === 'severity' && isDotNeeded) {
|
||||
title = value
|
||||
formattedValue = (
|
||||
<div
|
||||
className={`logs-viewer--dot ${value}-severity`}
|
||||
title={value}
|
||||
onMouseOver={this.handleMouseEnter}
|
||||
data-index={rowIndex}
|
||||
style={this.severityDotStyle(value)}
|
||||
/>
|
||||
<>
|
||||
<div
|
||||
className={`logs-viewer--dot ${value}-severity`}
|
||||
title={value}
|
||||
onMouseOver={this.handleMouseEnter}
|
||||
data-index={rowIndex}
|
||||
style={this.severityDotStyle(value)}
|
||||
/>
|
||||
{this.getSeverityDotText(value)}
|
||||
</>
|
||||
)
|
||||
} else {
|
||||
formattedValue = formatColumnValue(column, value, this.rowCharLimit)
|
||||
title = formattedValue
|
||||
}
|
||||
|
||||
const highlightRow = rowIndex === this.state.currentRow
|
||||
|
@ -364,7 +411,7 @@ class LogsTable extends Component<Props, State> {
|
|||
className={classnames('logs-viewer--cell', {
|
||||
highlight: highlightRow,
|
||||
})}
|
||||
title={`Filter by '${formattedValue}'`}
|
||||
title={`Filter by '${title}'`}
|
||||
style={{...style, padding: '5px'}}
|
||||
key={key}
|
||||
data-index={rowIndex}
|
||||
|
@ -418,9 +465,10 @@ class LogsTable extends Component<Props, State> {
|
|||
private handleTagClick = (e: MouseEvent<HTMLElement>) => {
|
||||
const {onTagSelection} = this.props
|
||||
const target = e.target as HTMLElement
|
||||
|
||||
const selection = {
|
||||
tag: target.dataset.tagValue,
|
||||
key: target.dataset.tagKey,
|
||||
tag: target.dataset.tagValue || target.parentElement.dataset.tagValue,
|
||||
key: target.dataset.tagKey || target.parentElement.dataset.tagKey,
|
||||
}
|
||||
|
||||
onTagSelection(selection)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React, {Component} from 'react'
|
||||
import _ from 'lodash'
|
||||
|
||||
import Container from 'src/shared/components/overlay/OverlayContainer'
|
||||
import Heading from 'src/shared/components/overlay/OverlayHeading'
|
||||
import Body from 'src/shared/components/overlay/OverlayBody'
|
||||
import Container from 'src/reusable_ui/components/overlays/OverlayContainer'
|
||||
import Heading from 'src/reusable_ui/components/overlays/OverlayHeading'
|
||||
import Body from 'src/reusable_ui/components/overlays/OverlayBody'
|
||||
import SeverityOptions from 'src/logs/components/SeverityOptions'
|
||||
import ColumnsOptions from 'src/logs/components/ColumnsOptions'
|
||||
import {
|
||||
|
@ -109,7 +109,7 @@ class OptionsOverlay extends Component<Props, State> {
|
|||
return true
|
||||
}
|
||||
|
||||
private handleSave = () => {
|
||||
private handleSave = async () => {
|
||||
const {
|
||||
onUpdateSeverityLevels,
|
||||
onDismissOverlay,
|
||||
|
@ -118,9 +118,9 @@ class OptionsOverlay extends Component<Props, State> {
|
|||
} = this.props
|
||||
const {workingSeverityLevels, workingFormat, workingColumns} = this.state
|
||||
|
||||
onUpdateSeverityFormat(workingFormat)
|
||||
onUpdateSeverityLevels(workingSeverityLevels)
|
||||
onUpdateColumns(workingColumns)
|
||||
await onUpdateSeverityFormat(workingFormat)
|
||||
await onUpdateSeverityLevels(workingSeverityLevels)
|
||||
await onUpdateColumns(workingColumns)
|
||||
onDismissOverlay()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import React, {SFC} from 'react'
|
||||
|
||||
import {SeverityFormatOptions} from 'src/logs/constants'
|
||||
import {SeverityFormat} from 'src/types/logs'
|
||||
|
||||
interface Props {
|
||||
|
@ -18,18 +20,21 @@ const SeverityFormat: SFC<Props> = ({format, onChangeFormat}) => (
|
|||
<div className="graph-options-group">
|
||||
<label className="form-label">Severity Format</label>
|
||||
<ul className="nav nav-tablist nav-tablist-sm stretch">
|
||||
<li onClick={onChangeFormat('dot')} className={className('dot', format)}>
|
||||
<li
|
||||
onClick={onChangeFormat(SeverityFormatOptions.dot)}
|
||||
className={className(SeverityFormatOptions.dot, format)}
|
||||
>
|
||||
Dot
|
||||
</li>
|
||||
<li
|
||||
onClick={onChangeFormat('dotText')}
|
||||
className={className('dotText', format)}
|
||||
onClick={onChangeFormat(SeverityFormatOptions.dotText)}
|
||||
className={className(SeverityFormatOptions.dotText, format)}
|
||||
>
|
||||
Dot + Text
|
||||
</li>
|
||||
<li
|
||||
onClick={onChangeFormat('text')}
|
||||
className={className('text', format)}
|
||||
onClick={onChangeFormat(SeverityFormatOptions.text)}
|
||||
className={className(SeverityFormatOptions.text, format)}
|
||||
>
|
||||
Text
|
||||
</li>
|
||||
|
|
|
@ -123,3 +123,25 @@ export const DEFAULT_SEVERITY_LEVELS = [
|
|||
override: null,
|
||||
},
|
||||
]
|
||||
|
||||
export enum SeverityFormatOptions {
|
||||
dot = 'dot',
|
||||
dotText = 'dotText',
|
||||
text = 'text',
|
||||
}
|
||||
|
||||
export enum EncodingTypes {
|
||||
visibility = 'visibility',
|
||||
display = 'displayName',
|
||||
label = 'label',
|
||||
}
|
||||
|
||||
export enum EncodingLabelOptions {
|
||||
text = 'text',
|
||||
icon = 'icon',
|
||||
}
|
||||
|
||||
export enum EncodingVisibilityOptions {
|
||||
visible = 'visible',
|
||||
hidden = 'hidden',
|
||||
}
|
||||
|
|
|
@ -15,24 +15,26 @@ import {
|
|||
removeFilter,
|
||||
changeFilter,
|
||||
fetchMoreAsync,
|
||||
getLogConfigAsync,
|
||||
updateLogConfigAsync,
|
||||
} from 'src/logs/actions'
|
||||
import {
|
||||
showOverlay as showOverlayAction,
|
||||
ShowOverlay,
|
||||
} from 'src/shared/actions/overlayTechnology'
|
||||
import {getSourcesAsync} from 'src/shared/actions/sources'
|
||||
import LogViewerHeader from 'src/logs/components/LogViewerHeader'
|
||||
import HistogramChart from 'src/shared/components/HistogramChart'
|
||||
import LogsGraphContainer from 'src/logs/components/LogsGraphContainer'
|
||||
import OptionsOverlay from 'src/logs/components/OptionsOverlay'
|
||||
import Graph from 'src/logs/components/LogsGraph'
|
||||
import SearchBar from 'src/logs/components/LogsSearchBar'
|
||||
import FilterBar from 'src/logs/components/LogsFilterBar'
|
||||
import LogsTable from 'src/logs/components/LogsTable'
|
||||
import {getDeep} from 'src/utils/wrappers'
|
||||
import {colorForSeverity} from 'src/logs/utils/colors'
|
||||
import {OverlayContext} from 'src/shared/components/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 {Source, Namespace, TimeRange} from 'src/types'
|
||||
|
||||
import {HistogramData, TimePeriod} from 'src/types/histogram'
|
||||
|
@ -41,6 +43,8 @@ import {
|
|||
SeverityLevel,
|
||||
SeverityFormat,
|
||||
LogsTableColumn,
|
||||
LogConfig,
|
||||
TableData,
|
||||
} from 'src/types/logs'
|
||||
|
||||
// Mock
|
||||
|
@ -62,21 +66,22 @@ interface Props {
|
|||
addFilter: (filter: Filter) => void
|
||||
removeFilter: (id: string) => void
|
||||
changeFilter: (id: string, operator: string, value: string) => void
|
||||
getConfig: (url: string) => Promise<void>
|
||||
updateConfig: (url: string, config: LogConfig) => Promise<void>
|
||||
timeRange: TimeRange
|
||||
histogramData: HistogramData
|
||||
tableData: {
|
||||
columns: string[]
|
||||
values: string[]
|
||||
}
|
||||
tableData: TableData
|
||||
searchTerm: string
|
||||
filters: Filter[]
|
||||
queryCount: number
|
||||
showOverlay: ShowOverlay
|
||||
logConfig: LogConfig
|
||||
logConfigLink: string
|
||||
}
|
||||
|
||||
interface State {
|
||||
searchString: string
|
||||
liveUpdating: boolean
|
||||
isOverlayVisible: boolean
|
||||
}
|
||||
|
||||
class LogsPage extends PureComponent<Props, State> {
|
||||
|
@ -88,6 +93,7 @@ class LogsPage extends PureComponent<Props, State> {
|
|||
this.state = {
|
||||
searchString: '',
|
||||
liveUpdating: false,
|
||||
isOverlayVisible: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,6 +105,7 @@ class LogsPage extends PureComponent<Props, State> {
|
|||
|
||||
public componentDidMount() {
|
||||
this.props.getSources()
|
||||
this.props.getConfig(this.logConfigLink)
|
||||
|
||||
if (this.props.currentNamespace) {
|
||||
this.fetchNewDataset()
|
||||
|
@ -116,36 +123,65 @@ class LogsPage extends PureComponent<Props, State> {
|
|||
const {searchTerm, filters, queryCount, timeRange} = this.props
|
||||
|
||||
return (
|
||||
<div className="page">
|
||||
{this.header}
|
||||
<div className="page-contents logs-viewer">
|
||||
<LogsGraphContainer>{this.chart}</LogsGraphContainer>
|
||||
<SearchBar
|
||||
searchString={searchTerm}
|
||||
onSearch={this.handleSubmitSearch}
|
||||
/>
|
||||
<FilterBar
|
||||
numResults={this.histogramTotal}
|
||||
filters={filters || []}
|
||||
onDelete={this.handleFilterDelete}
|
||||
onFilterChange={this.handleFilterChange}
|
||||
queryCount={queryCount}
|
||||
/>
|
||||
<LogsTable
|
||||
count={this.histogramTotal}
|
||||
data={this.props.tableData}
|
||||
onScrollVertical={this.handleVerticalScroll}
|
||||
onScrolledToTop={this.handleScrollToTop}
|
||||
isScrolledToTop={liveUpdating}
|
||||
onTagSelection={this.handleTagSelection}
|
||||
fetchMore={this.props.fetchMoreAsync}
|
||||
timeRange={timeRange}
|
||||
/>
|
||||
<>
|
||||
<div className="page">
|
||||
{this.header}
|
||||
<div className="page-contents logs-viewer">
|
||||
<LogsGraphContainer>{this.chart}</LogsGraphContainer>
|
||||
<SearchBar
|
||||
searchString={searchTerm}
|
||||
onSearch={this.handleSubmitSearch}
|
||||
/>
|
||||
<FilterBar
|
||||
numResults={this.histogramTotal}
|
||||
filters={filters || []}
|
||||
onDelete={this.handleFilterDelete}
|
||||
onFilterChange={this.handleFilterChange}
|
||||
queryCount={queryCount}
|
||||
/>
|
||||
<LogsTable
|
||||
count={this.histogramTotal}
|
||||
data={this.tableData}
|
||||
onScrollVertical={this.handleVerticalScroll}
|
||||
onScrolledToTop={this.handleScrollToTop}
|
||||
isScrolledToTop={liveUpdating}
|
||||
onTagSelection={this.handleTagSelection}
|
||||
fetchMore={this.props.fetchMoreAsync}
|
||||
timeRange={timeRange}
|
||||
tableColumns={this.tableColumns}
|
||||
severityFormat={this.severityFormat}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{this.renderImportOverlay()}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
private get tableData(): TableData {
|
||||
const {logConfig, tableData} = this.props
|
||||
const tableColumns = _.get(logConfig, 'tableColumns', [])
|
||||
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}
|
||||
}
|
||||
|
||||
private get logConfigLink(): string {
|
||||
return this.props.logConfigLink
|
||||
}
|
||||
|
||||
private get tableColumns(): LogsTableColumn[] {
|
||||
const {logConfig} = this.props
|
||||
return _.get(logConfig, 'tableColumns', [])
|
||||
}
|
||||
|
||||
private get isSpecificTimeRange(): boolean {
|
||||
return !!getDeep(this.props, 'timeRange.upper', false)
|
||||
}
|
||||
|
@ -175,7 +211,6 @@ class LogsPage extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
private handleTagSelection = (selection: {tag: string; key: string}) => {
|
||||
// Do something with the tag
|
||||
this.props.addFilter({
|
||||
id: uuid.v4(),
|
||||
key: selection.key,
|
||||
|
@ -236,7 +271,7 @@ class LogsPage extends PureComponent<Props, State> {
|
|||
currentNamespaces={currentNamespaces}
|
||||
currentNamespace={currentNamespace}
|
||||
onChangeLiveUpdatingStatus={this.handleChangeLiveUpdatingStatus}
|
||||
onShowOptionsOverlay={this.handleShowOptionsOverlay}
|
||||
onShowOptionsOverlay={this.handleToggleOverlay}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -301,57 +336,72 @@ class LogsPage extends PureComponent<Props, State> {
|
|||
this.setState({liveUpdating: true})
|
||||
}
|
||||
|
||||
private handleShowOptionsOverlay = (): void => {
|
||||
const {showOverlay} = this.props
|
||||
const options = {
|
||||
dismissOnClickOutside: false,
|
||||
dismissOnEscape: false,
|
||||
}
|
||||
private handleToggleOverlay = (): void => {
|
||||
this.setState({isOverlayVisible: !this.state.isOverlayVisible})
|
||||
}
|
||||
|
||||
showOverlay(
|
||||
<OverlayContext.Consumer>
|
||||
{({onDismissOverlay}) => (
|
||||
<OptionsOverlay
|
||||
severityLevels={DEFAULT_SEVERITY_LEVELS} // Todo: replace with real
|
||||
onUpdateSeverityLevels={this.handleUpdateSeverityLevels}
|
||||
onDismissOverlay={onDismissOverlay}
|
||||
columns={this.fakeColumns}
|
||||
onUpdateColumns={this.handleUpdateColumns}
|
||||
onUpdateSeverityFormat={this.handleUpdateSeverityFormat}
|
||||
severityFormat="dotText" // Todo: repleace with real value
|
||||
/>
|
||||
)}
|
||||
</OverlayContext.Consumer>,
|
||||
options
|
||||
private renderImportOverlay = (): JSX.Element => {
|
||||
const {isOverlayVisible} = this.state
|
||||
|
||||
return (
|
||||
<OverlayTechnology visible={isOverlayVisible}>
|
||||
<OptionsOverlay
|
||||
severityLevels={DEFAULT_SEVERITY_LEVELS} // Todo: replace with real
|
||||
onUpdateSeverityLevels={this.handleUpdateSeverityLevels}
|
||||
onDismissOverlay={this.handleToggleOverlay}
|
||||
columns={this.columns}
|
||||
onUpdateColumns={this.handleUpdateColumns}
|
||||
onUpdateSeverityFormat={this.handleUpdateSeverityFormat}
|
||||
severityFormat={this.severityFormat}
|
||||
/>
|
||||
</OverlayTechnology>
|
||||
)
|
||||
}
|
||||
|
||||
private handleUpdateSeverityLevels = (levels: SeverityLevel[]) => {
|
||||
console.log(levels) // tslint:disable-line
|
||||
// Todo: Handle saving of these new severity colors here
|
||||
levels = levels
|
||||
}
|
||||
|
||||
private handleUpdateSeverityFormat = (format: SeverityFormat) => {
|
||||
console.log(format) // tslint:disable-line
|
||||
// Todo: Handle saving of the new format here
|
||||
private handleUpdateSeverityFormat = async (format: SeverityFormat) => {
|
||||
const {logConfig} = this.props
|
||||
await this.props.updateConfig(this.logConfigLink, {
|
||||
...logConfig,
|
||||
severityFormat: format,
|
||||
})
|
||||
}
|
||||
|
||||
private get fakeColumns(): LogsTableColumn[] {
|
||||
const {
|
||||
tableData: {columns},
|
||||
} = this.props
|
||||
private get columns(): LogsTableColumn[] {
|
||||
const {logConfig} = this.props
|
||||
const tableColumns = _.get(logConfig, 'tableColumns', [])
|
||||
|
||||
return columns.map(c => ({internalName: c, displayName: '', visible: true}))
|
||||
return tableColumns
|
||||
}
|
||||
|
||||
private handleUpdateColumns = (columns: LogsTableColumn[]) => {
|
||||
console.log(columns) // tslint:disable-line
|
||||
// Todo: Handle saving of column names, ordering, and visibility
|
||||
private get severityFormat(): SeverityFormat {
|
||||
const {logConfig} = this.props
|
||||
const severityFormat = _.get(
|
||||
logConfig,
|
||||
'severityFormat',
|
||||
SeverityFormatOptions.dotText
|
||||
)
|
||||
return severityFormat
|
||||
}
|
||||
|
||||
private handleUpdateColumns = async (tableColumns: LogsTableColumn[]) => {
|
||||
const {logConfig} = this.props
|
||||
await this.props.updateConfig(this.logConfigLink, {
|
||||
...logConfig,
|
||||
tableColumns,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = ({
|
||||
sources,
|
||||
links: {
|
||||
config: {logViewer},
|
||||
},
|
||||
logs: {
|
||||
currentSource,
|
||||
currentNamespaces,
|
||||
|
@ -362,6 +412,7 @@ const mapStateToProps = ({
|
|||
searchTerm,
|
||||
filters,
|
||||
queryCount,
|
||||
logConfig,
|
||||
},
|
||||
}) => ({
|
||||
sources,
|
||||
|
@ -374,12 +425,13 @@ const mapStateToProps = ({
|
|||
searchTerm,
|
||||
filters,
|
||||
queryCount,
|
||||
logConfig,
|
||||
logConfigLink: logViewer,
|
||||
})
|
||||
|
||||
const mapDispatchToProps = {
|
||||
getSource: getSourceAndPopulateNamespacesAsync,
|
||||
getSources: getSourcesAsync,
|
||||
showOverlay: showOverlayAction,
|
||||
setTimeRangeAsync,
|
||||
setNamespaceAsync,
|
||||
executeQueriesAsync,
|
||||
|
@ -389,6 +441,8 @@ const mapDispatchToProps = {
|
|||
removeFilter,
|
||||
changeFilter,
|
||||
fetchMoreAsync,
|
||||
getConfig: getLogConfigAsync,
|
||||
updateConfig: updateLogConfigAsync,
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(LogsPage)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import _ from 'lodash'
|
||||
|
||||
import {
|
||||
ActionTypes,
|
||||
Action,
|
||||
|
@ -8,8 +9,10 @@ import {
|
|||
DecrementQueryCountAction,
|
||||
IncrementQueryCountAction,
|
||||
ConcatMoreLogsAction,
|
||||
SetConfigsAction,
|
||||
} from 'src/logs/actions'
|
||||
|
||||
import {SeverityFormatOptions} from 'src/logs/constants'
|
||||
import {LogsState} from 'src/types/logs'
|
||||
|
||||
const defaultState: LogsState = {
|
||||
|
@ -24,6 +27,10 @@ const defaultState: LogsState = {
|
|||
searchTerm: '',
|
||||
filters: [],
|
||||
queryCount: 0,
|
||||
logConfig: {
|
||||
tableColumns: [],
|
||||
severityFormat: SeverityFormatOptions.dotText,
|
||||
},
|
||||
}
|
||||
|
||||
const removeFilter = (
|
||||
|
@ -95,6 +102,15 @@ const concatMoreLogs = (
|
|||
}
|
||||
}
|
||||
|
||||
export const setConfigs = (state: LogsState, action: SetConfigsAction) => {
|
||||
const {logConfig} = state
|
||||
const {
|
||||
logConfig: {tableColumns, severityFormat},
|
||||
} = action.payload
|
||||
const updatedLogConfig = {...logConfig, tableColumns, severityFormat}
|
||||
return {...state, logConfig: updatedLogConfig}
|
||||
}
|
||||
|
||||
export default (state: LogsState = defaultState, action: Action) => {
|
||||
switch (action.type) {
|
||||
case ActionTypes.SetSource:
|
||||
|
@ -130,6 +146,8 @@ export default (state: LogsState = defaultState, action: Action) => {
|
|||
return decrementQueryCount(state, action)
|
||||
case ActionTypes.ConcatMoreLogs:
|
||||
return concatMoreLogs(state, action)
|
||||
case ActionTypes.SetConfig:
|
||||
return setConfigs(state, action)
|
||||
default:
|
||||
return state
|
||||
}
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
import _ from 'lodash'
|
||||
import {
|
||||
LogConfig,
|
||||
ServerLogConfig,
|
||||
ServerColumn,
|
||||
LogsTableColumn,
|
||||
ServerEncoding,
|
||||
SeverityFormat,
|
||||
} from 'src/types/logs'
|
||||
import {
|
||||
SeverityFormatOptions,
|
||||
EncodingTypes,
|
||||
EncodingLabelOptions,
|
||||
EncodingVisibilityOptions,
|
||||
} from 'src/logs/constants'
|
||||
|
||||
export const logConfigServerToUI = (
|
||||
serverConfig: ServerLogConfig
|
||||
): LogConfig => {
|
||||
const columns = _.get(serverConfig, 'columns', [])
|
||||
if (_.isEmpty(columns)) {
|
||||
return
|
||||
}
|
||||
|
||||
const sortedColumns = sortColumns(columns)
|
||||
|
||||
let severityFormat: SeverityFormatOptions
|
||||
const convertedColumns = sortedColumns.map(c => {
|
||||
if (c.name === 'severity') {
|
||||
severityFormat = getFormatFromColumn(c)
|
||||
}
|
||||
|
||||
return columnServerToUI(c)
|
||||
})
|
||||
|
||||
return {
|
||||
tableColumns: convertedColumns,
|
||||
severityFormat,
|
||||
}
|
||||
}
|
||||
|
||||
export const sortColumns = (columns: ServerColumn[]): ServerColumn[] => {
|
||||
return _.sortBy(columns, c => c.position)
|
||||
}
|
||||
|
||||
export const columnServerToUI = (column: ServerColumn): LogsTableColumn => {
|
||||
const internalName = column.name
|
||||
const encodings: LogsTableColumn = column.encodings.reduce(
|
||||
(acc, e) => {
|
||||
if (e.type === EncodingTypes.visibility) {
|
||||
if (e.value === 'visible') {
|
||||
acc.visible = true
|
||||
}
|
||||
} else if (e.type === EncodingTypes.display) {
|
||||
acc.displayName = e.value
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{visible: false, displayName: '', internalName}
|
||||
)
|
||||
return {...encodings, internalName}
|
||||
}
|
||||
|
||||
export const getFormatFromColumn = (
|
||||
column: ServerColumn
|
||||
): SeverityFormatOptions => {
|
||||
let hasText = false
|
||||
let hasIcon = false
|
||||
|
||||
column.encodings.forEach(e => {
|
||||
if (e.type === EncodingTypes.label) {
|
||||
if (e.value === EncodingLabelOptions.icon) {
|
||||
hasIcon = true
|
||||
}
|
||||
if (e.value === EncodingLabelOptions.text) {
|
||||
hasText = true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (hasText && hasIcon) {
|
||||
return SeverityFormatOptions.dotText
|
||||
} else if (hasText) {
|
||||
return SeverityFormatOptions.text
|
||||
} else {
|
||||
return SeverityFormatOptions.dot
|
||||
}
|
||||
}
|
||||
|
||||
export const logConfigUIToServer = (config: LogConfig): ServerLogConfig => {
|
||||
const tableColumns = _.get(config, 'tableColumns')
|
||||
const severityFormat = _.get(config, 'severityFormat')
|
||||
|
||||
if (_.isEmpty(tableColumns)) {
|
||||
return {columns: []}
|
||||
}
|
||||
|
||||
const columns = tableColumns.map((c, i) => {
|
||||
const encodings = getFullEncodings(c, severityFormat)
|
||||
const name = c.internalName
|
||||
const position = i
|
||||
|
||||
return {name, position, encodings}
|
||||
})
|
||||
|
||||
return {columns}
|
||||
}
|
||||
|
||||
export const getDisplayAndVisibleEncodings = (
|
||||
tableColumn: LogsTableColumn
|
||||
): ServerEncoding[] => {
|
||||
const encodings: ServerEncoding[] = []
|
||||
|
||||
if (tableColumn.visible) {
|
||||
encodings.push({
|
||||
type: EncodingTypes.visibility,
|
||||
value: EncodingVisibilityOptions.visible,
|
||||
})
|
||||
} else {
|
||||
encodings.push({
|
||||
type: EncodingTypes.visibility,
|
||||
value: EncodingVisibilityOptions.hidden,
|
||||
})
|
||||
}
|
||||
|
||||
if (!_.isEmpty(tableColumn.displayName)) {
|
||||
encodings.push({
|
||||
type: EncodingTypes.display,
|
||||
value: tableColumn.displayName,
|
||||
})
|
||||
}
|
||||
|
||||
return encodings
|
||||
}
|
||||
|
||||
export const getLabelEncodings = (format: SeverityFormat): ServerEncoding[] => {
|
||||
switch (format) {
|
||||
case SeverityFormatOptions.dot:
|
||||
return [{type: 'label', value: EncodingLabelOptions.icon}]
|
||||
case SeverityFormatOptions.text:
|
||||
return [{type: 'label', value: EncodingLabelOptions.text}]
|
||||
case SeverityFormatOptions.dotText:
|
||||
return [
|
||||
{type: 'label', value: EncodingLabelOptions.icon},
|
||||
{type: 'label', value: EncodingLabelOptions.text},
|
||||
]
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export const getFullEncodings = (
|
||||
tableColumn: LogsTableColumn,
|
||||
format: SeverityFormat
|
||||
) => {
|
||||
let encodings = getDisplayAndVisibleEncodings(tableColumn)
|
||||
if (tableColumn.internalName === 'severity') {
|
||||
encodings = [...encodings, ...getLabelEncodings(format)]
|
||||
}
|
||||
|
||||
return encodings
|
||||
}
|
|
@ -48,11 +48,6 @@ const tableFields = [
|
|||
type: 'field',
|
||||
value: 'message',
|
||||
},
|
||||
{
|
||||
alias: 'severity_text',
|
||||
type: 'field',
|
||||
value: 'severity',
|
||||
},
|
||||
{
|
||||
alias: 'facility',
|
||||
type: 'field',
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import _ from 'lodash'
|
||||
import moment from 'moment'
|
||||
import {getDeep} from 'src/utils/wrappers'
|
||||
import {TableData} from 'src/types/logs'
|
||||
import {TableData, LogsTableColumn, SeverityFormat} from 'src/types/logs'
|
||||
import {SeverityFormatOptions} from 'src/logs/constants'
|
||||
|
||||
const CHAR_WIDTH = 9
|
||||
|
||||
|
@ -21,7 +22,7 @@ export const getColumnFromData = (data: TableData, index: number): string =>
|
|||
getDeep(data, `columns.${index}`, '')
|
||||
|
||||
export const isClickable = (column: string): boolean =>
|
||||
_.includes(['appname', 'facility', 'host', 'hostname', 'severity_1'], column)
|
||||
_.includes(['appname', 'facility', 'host', 'hostname', 'severity'], column)
|
||||
|
||||
export const formatColumnValue = (
|
||||
column: string,
|
||||
|
@ -31,6 +32,8 @@ export const formatColumnValue = (
|
|||
switch (column) {
|
||||
case 'timestamp':
|
||||
return moment(+value / 1000000).format('YYYY/MM/DD HH:mm:ss')
|
||||
case 'time':
|
||||
return moment(+value / 1000000).format('YYYY/MM/DD HH:mm:ss')
|
||||
case 'message':
|
||||
value = (value || 'No Message Provided').replace('\\n', '')
|
||||
if (value.indexOf(' ') > charLimit - 5) {
|
||||
|
@ -40,19 +43,16 @@ export const formatColumnValue = (
|
|||
}
|
||||
return value
|
||||
}
|
||||
export const header = (key: string): string => {
|
||||
return getDeep<string>(
|
||||
{
|
||||
timestamp: 'Timestamp',
|
||||
procid: 'Proc ID',
|
||||
message: 'Message',
|
||||
appname: 'Application',
|
||||
severity: '',
|
||||
severity_1: 'Severity',
|
||||
},
|
||||
key,
|
||||
_.capitalize(key)
|
||||
)
|
||||
export const header = (
|
||||
key: string,
|
||||
headerOptions: LogsTableColumn[]
|
||||
): string => {
|
||||
if (key === SeverityFormatOptions.dot) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const headerOption = _.find(headerOptions, h => h.internalName === key)
|
||||
return _.get(headerOption, 'displayName') || _.capitalize(key)
|
||||
}
|
||||
|
||||
export const getColumnWidth = (column: string): number => {
|
||||
|
@ -61,8 +61,9 @@ export const getColumnWidth = (column: string): number => {
|
|||
timestamp: 160,
|
||||
procid: 80,
|
||||
facility: 120,
|
||||
severity: 22,
|
||||
severity_1: 120,
|
||||
severity_dot: 25,
|
||||
severity_text: 120,
|
||||
severity_dotText: 120,
|
||||
host: 300,
|
||||
},
|
||||
column,
|
||||
|
@ -70,14 +71,25 @@ export const getColumnWidth = (column: string): number => {
|
|||
)
|
||||
}
|
||||
|
||||
export const getMessageWidth = (data: TableData): number => {
|
||||
export const getMessageWidth = (
|
||||
data: TableData,
|
||||
tableColumns: LogsTableColumn[],
|
||||
severityFormat: SeverityFormat
|
||||
): number => {
|
||||
const columns = getColumnsFromData(data)
|
||||
const otherWidth = columns.reduce((acc, col) => {
|
||||
if (col === 'message' || col === 'time') {
|
||||
const colConfig = tableColumns.find(c => c.internalName === col)
|
||||
const isColVisible = colConfig && colConfig.visible
|
||||
if (col === 'message' || !isColVisible) {
|
||||
return acc
|
||||
}
|
||||
|
||||
return acc + getColumnWidth(col)
|
||||
let columnName = col
|
||||
if (col === 'severity') {
|
||||
columnName = `${col}_${severityFormat}`
|
||||
}
|
||||
|
||||
return acc + getColumnWidth(columnName)
|
||||
}, 0)
|
||||
|
||||
const calculatedWidth = Math.max(
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import {SeverityFormatOptions} from 'src/logs/constants'
|
||||
import {QueryConfig, TimeRange, Namespace, Source} from 'src/types'
|
||||
import {FieldOption} from 'src/types/dashboards'
|
||||
import {TimeSeriesValue} from 'src/types/series'
|
||||
|
||||
export interface Filter {
|
||||
id: string
|
||||
|
@ -9,7 +12,7 @@ export interface Filter {
|
|||
|
||||
export interface TableData {
|
||||
columns: string[]
|
||||
values: string[]
|
||||
values: TimeSeriesValue[][]
|
||||
}
|
||||
|
||||
export interface LogsState {
|
||||
|
@ -24,6 +27,12 @@ export interface LogsState {
|
|||
searchTerm: string | null
|
||||
filters: Filter[]
|
||||
queryCount: number
|
||||
logConfig: LogConfig
|
||||
}
|
||||
|
||||
export interface LogConfig {
|
||||
tableColumns: LogsTableColumn[]
|
||||
severityFormat: SeverityFormat
|
||||
}
|
||||
|
||||
export interface SeverityLevel {
|
||||
|
@ -37,10 +46,21 @@ export interface SeverityColor {
|
|||
name: string
|
||||
}
|
||||
|
||||
export type SeverityFormat = 'dot' | 'dotText' | 'text'
|
||||
export type SeverityFormat = SeverityFormatOptions
|
||||
|
||||
export interface LogsTableColumn {
|
||||
internalName: string
|
||||
displayName: string
|
||||
visible: boolean
|
||||
export type LogsTableColumn = FieldOption
|
||||
|
||||
export interface ServerLogConfig {
|
||||
columns: ServerColumn[]
|
||||
}
|
||||
|
||||
export interface ServerColumn {
|
||||
name: string
|
||||
position: number
|
||||
encodings: ServerEncoding[]
|
||||
}
|
||||
|
||||
export interface ServerEncoding {
|
||||
type: string
|
||||
value: string
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
CellType,
|
||||
} from 'src/types/dashboards'
|
||||
import {LineColor, ColorNumber} from 'src/types/colors'
|
||||
import {ServerLogConfig, ServerColumn} from 'src/types/logs'
|
||||
|
||||
export const sourceLinks: SourceLinks = {
|
||||
services: '/chronograf/v1/sources/4',
|
||||
|
@ -381,3 +382,106 @@ export const gaugeColors: ColorNumber[] = [
|
|||
value: 100,
|
||||
},
|
||||
]
|
||||
|
||||
export const serverLogColumns: ServerColumn[] = [
|
||||
{
|
||||
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',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
export const serverLogConfig: ServerLogConfig = {
|
||||
columns: serverLogColumns,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,348 @@
|
|||
import {
|
||||
logConfigServerToUI,
|
||||
logConfigUIToServer,
|
||||
columnServerToUI,
|
||||
getFormatFromColumn,
|
||||
sortColumns,
|
||||
getDisplayAndVisibleEncodings,
|
||||
getLabelEncodings,
|
||||
getFullEncodings,
|
||||
} from 'src/logs/utils/config'
|
||||
import {serverLogConfig, serverLogColumns} from 'test/fixtures'
|
||||
import {SeverityFormatOptions} from 'src/logs/constants'
|
||||
|
||||
const sortedServerColumns = () => {
|
||||
return [
|
||||
{
|
||||
name: 'time',
|
||||
position: 0,
|
||||
encodings: [
|
||||
{
|
||||
type: 'visibility',
|
||||
value: 'hidden',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
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: 'procid',
|
||||
position: 5,
|
||||
encodings: [
|
||||
{
|
||||
type: 'visibility',
|
||||
value: 'visible',
|
||||
},
|
||||
{
|
||||
type: 'displayName',
|
||||
value: 'Proc ID',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'appname',
|
||||
position: 6,
|
||||
encodings: [
|
||||
{
|
||||
type: 'visibility',
|
||||
value: 'visible',
|
||||
},
|
||||
{
|
||||
type: 'displayName',
|
||||
value: 'Application',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'host',
|
||||
position: 7,
|
||||
encodings: [
|
||||
{
|
||||
type: 'visibility',
|
||||
value: 'visible',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
describe('Logs.Config', () => {
|
||||
describe('logConfigServerToUI', () => {
|
||||
it('Converts columns to tableColumns', () => {
|
||||
const serverColumn = {
|
||||
name: 'appname',
|
||||
position: 6,
|
||||
encodings: [
|
||||
{
|
||||
type: 'visibility',
|
||||
value: 'visible',
|
||||
},
|
||||
{
|
||||
type: 'displayName',
|
||||
value: 'Application',
|
||||
},
|
||||
],
|
||||
}
|
||||
const serverColumn2 = {
|
||||
name: 'procid',
|
||||
position: 0,
|
||||
encodings: [
|
||||
{
|
||||
type: 'visibility',
|
||||
value: 'hidden',
|
||||
},
|
||||
],
|
||||
}
|
||||
const uiColumn = columnServerToUI(serverColumn)
|
||||
const uiColumn2 = columnServerToUI(serverColumn2)
|
||||
const expectedColumn = {
|
||||
internalName: 'appname',
|
||||
displayName: 'Application',
|
||||
visible: true,
|
||||
}
|
||||
const expectedColumn2 = {
|
||||
internalName: 'procid',
|
||||
displayName: '',
|
||||
visible: false,
|
||||
}
|
||||
|
||||
expect(uiColumn).toEqual(expectedColumn)
|
||||
expect(uiColumn2).toEqual(expectedColumn2)
|
||||
})
|
||||
|
||||
it('Gets severity format from columns', () => {
|
||||
const serverColumnDotText = {
|
||||
name: 'severity',
|
||||
position: 2,
|
||||
encodings: [
|
||||
{
|
||||
type: 'label',
|
||||
value: 'icon',
|
||||
},
|
||||
{
|
||||
type: 'label',
|
||||
value: 'text',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const serverColumnDot = {
|
||||
name: 'severity',
|
||||
position: 2,
|
||||
encodings: [
|
||||
{
|
||||
type: 'label',
|
||||
value: 'icon',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const severityFormatDotText = getFormatFromColumn(serverColumnDotText)
|
||||
const severityFormatDot = getFormatFromColumn(serverColumnDot)
|
||||
|
||||
expect(severityFormatDotText).toBe(SeverityFormatOptions.dotText)
|
||||
expect(severityFormatDot).toBe(SeverityFormatOptions.dot)
|
||||
})
|
||||
|
||||
it('Sorts the columns by position', () => {
|
||||
const sortedColumns = sortColumns(serverLogColumns)
|
||||
const expected = sortedServerColumns()
|
||||
|
||||
expect(sortedColumns).toEqual(expected)
|
||||
})
|
||||
|
||||
it('Converts the config from server to the format used by UI', () => {
|
||||
const uiLogConfig = logConfigServerToUI(serverLogConfig)
|
||||
|
||||
const expected = {
|
||||
tableColumns: [
|
||||
{internalName: 'time', displayName: '', visible: false},
|
||||
{internalName: 'severity', displayName: '', visible: true},
|
||||
{internalName: 'timestamp', displayName: '', visible: true},
|
||||
{internalName: 'message', displayName: '', visible: true},
|
||||
{internalName: 'facility', displayName: '', visible: true},
|
||||
{internalName: 'procid', displayName: 'Proc ID', visible: true},
|
||||
{
|
||||
internalName: 'appname',
|
||||
displayName: 'Application',
|
||||
visible: true,
|
||||
},
|
||||
{internalName: 'host', displayName: '', visible: true},
|
||||
],
|
||||
severityFormat: SeverityFormatOptions.dotText,
|
||||
}
|
||||
|
||||
expect(uiLogConfig).toEqual(expected)
|
||||
})
|
||||
})
|
||||
|
||||
describe('logConfigUIToServer', () => {
|
||||
it('generates visibility and displayName encodings from column', () => {
|
||||
const tableColumn = {
|
||||
internalName: 'appname',
|
||||
displayName: 'Application',
|
||||
visible: true,
|
||||
}
|
||||
const encodings = getDisplayAndVisibleEncodings(tableColumn)
|
||||
const expected = [
|
||||
{
|
||||
type: 'visibility',
|
||||
value: 'visible',
|
||||
},
|
||||
{
|
||||
type: 'displayName',
|
||||
value: 'Application',
|
||||
},
|
||||
]
|
||||
|
||||
expect(encodings).toEqual(expected)
|
||||
})
|
||||
|
||||
it('generates label encodings from serverFormat', () => {
|
||||
const severityFormatDotText = SeverityFormatOptions.dotText
|
||||
const severityFromatDot = SeverityFormatOptions.dot
|
||||
|
||||
const encodingsDotText = getLabelEncodings(severityFormatDotText)
|
||||
const encodingsDot = getLabelEncodings(severityFromatDot)
|
||||
|
||||
const expectedDotText = [
|
||||
{
|
||||
type: 'label',
|
||||
value: 'icon',
|
||||
},
|
||||
{
|
||||
type: 'label',
|
||||
value: 'text',
|
||||
},
|
||||
]
|
||||
const expectedDot = [
|
||||
{
|
||||
type: 'label',
|
||||
value: 'icon',
|
||||
},
|
||||
]
|
||||
|
||||
expect(encodingsDotText).toEqual(expectedDotText)
|
||||
expect(encodingsDot).toEqual(expectedDot)
|
||||
})
|
||||
|
||||
it('gets all encodings when appropriate', () => {
|
||||
const displayName = 'SEVERITY'
|
||||
const tableColumnSeverity = {
|
||||
internalName: 'severity',
|
||||
displayName,
|
||||
visible: true,
|
||||
}
|
||||
const tableColumnOther = {
|
||||
internalName: 'host',
|
||||
displayName: '',
|
||||
visible: true,
|
||||
}
|
||||
const severityFormat = SeverityFormatOptions.dotText
|
||||
const encodingsSeverity = getFullEncodings(
|
||||
tableColumnSeverity,
|
||||
severityFormat
|
||||
)
|
||||
const encodingsOther = getFullEncodings(tableColumnOther, severityFormat)
|
||||
const expectedSeverity = [
|
||||
{
|
||||
type: 'visibility',
|
||||
value: 'visible',
|
||||
},
|
||||
{
|
||||
type: 'displayName',
|
||||
value: displayName,
|
||||
},
|
||||
{
|
||||
type: 'label',
|
||||
value: 'icon',
|
||||
},
|
||||
{
|
||||
type: 'label',
|
||||
value: 'text',
|
||||
},
|
||||
]
|
||||
|
||||
const expectedOther = [
|
||||
{
|
||||
type: 'visibility',
|
||||
value: 'visible',
|
||||
},
|
||||
]
|
||||
|
||||
expect(encodingsSeverity).toEqual(expectedSeverity)
|
||||
expect(encodingsOther).toEqual(expectedOther)
|
||||
})
|
||||
|
||||
it('Converts the config from what the UI uses to what the server takes', () => {
|
||||
const uiLogConfig = {
|
||||
tableColumns: [
|
||||
{internalName: 'time', displayName: '', visible: false},
|
||||
{internalName: 'severity', displayName: '', visible: true},
|
||||
{internalName: 'timestamp', displayName: '', visible: true},
|
||||
{internalName: 'message', displayName: '', visible: true},
|
||||
{internalName: 'facility', displayName: '', visible: true},
|
||||
{internalName: 'procid', displayName: 'Proc ID', visible: true},
|
||||
{
|
||||
internalName: 'appname',
|
||||
displayName: 'Application',
|
||||
visible: true,
|
||||
},
|
||||
{internalName: 'host', displayName: '', visible: true},
|
||||
],
|
||||
severityFormat: SeverityFormatOptions.dotText,
|
||||
}
|
||||
|
||||
const convertedServerLogConfig = logConfigUIToServer(uiLogConfig)
|
||||
const expected = {columns: sortedServerColumns()}
|
||||
|
||||
expect(convertedServerLogConfig).toEqual(expected)
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue