feat(logs/overlay): Add log options overlay (#969)
feat(logs/overlay): Add log options overlay Adds the log options overlay components in preparation for showing log config for table columns.pull/10616/head
parent
610faf18e7
commit
4a9f0959b8
|
@ -0,0 +1,217 @@
|
|||
import React, {Component, ChangeEvent} from 'react'
|
||||
import {findDOMNode} from 'react-dom'
|
||||
import {
|
||||
DragSourceSpec,
|
||||
DropTargetConnector,
|
||||
DragSourceMonitor,
|
||||
DragSource,
|
||||
DropTarget,
|
||||
DragSourceConnector,
|
||||
ConnectDragSource,
|
||||
ConnectDropTarget,
|
||||
ConnectDragPreview,
|
||||
} from 'react-dnd'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {LogsTableColumn} from 'src/types/logs'
|
||||
|
||||
const columnType = 'column'
|
||||
|
||||
interface Props {
|
||||
internalName: string
|
||||
displayName: string
|
||||
visible: boolean
|
||||
index: number
|
||||
id: string
|
||||
key: string
|
||||
onUpdateColumn: (column: LogsTableColumn) => void
|
||||
isDragging?: boolean
|
||||
connectDragSource?: ConnectDragSource
|
||||
connectDropTarget?: ConnectDropTarget
|
||||
connectDragPreview?: ConnectDragPreview
|
||||
onMoveColumn: (dragIndex: number, hoverIndex: number) => void
|
||||
}
|
||||
|
||||
const columnSource: DragSourceSpec<Props> = {
|
||||
beginDrag(props) {
|
||||
return {
|
||||
id: props.id,
|
||||
index: props.index,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const columnTarget = {
|
||||
hover(props, monitor, component) {
|
||||
const dragIndex = monitor.getItem().index
|
||||
const hoverIndex = props.index
|
||||
|
||||
// Don't replace items with themselves
|
||||
if (dragIndex === hoverIndex) {
|
||||
return
|
||||
}
|
||||
|
||||
// Determine rectangle on screen
|
||||
const domNode = findDOMNode(component) as Element
|
||||
const hoverBoundingRect = domNode.getBoundingClientRect()
|
||||
|
||||
// Get vertical middle
|
||||
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2
|
||||
|
||||
// Determine mouse position
|
||||
const clientOffset = monitor.getClientOffset()
|
||||
|
||||
// Get pixels to the top
|
||||
const hoverClientY = clientOffset.y - hoverBoundingRect.top
|
||||
|
||||
// Only perform the move when the mouse has crossed half of the items height
|
||||
// When dragging downwards, only move when the cursor is below 50%
|
||||
// When dragging upwards, only move when the cursor is above 50%
|
||||
|
||||
// Dragging downwards
|
||||
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
|
||||
return
|
||||
}
|
||||
|
||||
// Dragging upwards
|
||||
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
|
||||
return
|
||||
}
|
||||
// Time to actually perform the action
|
||||
props.onMoveColumn(dragIndex, hoverIndex)
|
||||
|
||||
// Note: we're mutating the monitor item here!
|
||||
// Generally it's better to avoid mutations,
|
||||
// but it's good here for the sake of performance
|
||||
// to avoid expensive index searches.
|
||||
monitor.getItem().index = hoverIndex
|
||||
},
|
||||
}
|
||||
|
||||
function ColumnDropTarget(dropColumnType, dropColumnTarget, dropHandler) {
|
||||
return target =>
|
||||
DropTarget(dropColumnType, dropColumnTarget, dropHandler)(target) as any
|
||||
}
|
||||
|
||||
function ColumnDragSource(dragColumnType, dragColumnSource, dragHandler) {
|
||||
return target =>
|
||||
DragSource(dragColumnType, dragColumnSource, dragHandler)(target) as any
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
@ColumnDropTarget(columnType, columnTarget, (connect: DropTargetConnector) => ({
|
||||
connectDropTarget: connect.dropTarget(),
|
||||
}))
|
||||
@ColumnDragSource(
|
||||
columnType,
|
||||
columnSource,
|
||||
(connect: DragSourceConnector, monitor: DragSourceMonitor) => ({
|
||||
connectDragSource: connect.dragSource(),
|
||||
connectDragPreview: connect.dragPreview(),
|
||||
isDragging: monitor.isDragging(),
|
||||
})
|
||||
)
|
||||
export default class DraggableColumn extends Component<Props> {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.handleColumnRename = this.handleColumnRename.bind(this)
|
||||
this.handleToggleVisible = this.handleToggleVisible.bind(this)
|
||||
}
|
||||
public render(): JSX.Element | null {
|
||||
const {
|
||||
internalName,
|
||||
displayName,
|
||||
connectDragPreview,
|
||||
connectDropTarget,
|
||||
visible,
|
||||
} = this.props
|
||||
|
||||
return connectDragPreview(
|
||||
connectDropTarget(
|
||||
<div className={this.columnClassName}>
|
||||
<div className={this.labelClassName}>
|
||||
{this.dragHandle}
|
||||
{this.visibilityToggle}
|
||||
<div className="customizable-field--name">{internalName}</div>
|
||||
</div>
|
||||
<input
|
||||
className="form-control input-sm customizable-field--input"
|
||||
type="text"
|
||||
spellCheck={false}
|
||||
id="internalName"
|
||||
value={displayName}
|
||||
onChange={this.handleColumnRename}
|
||||
placeholder={`Rename ${internalName}`}
|
||||
disabled={!visible}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private get dragHandle(): JSX.Element {
|
||||
const {connectDragSource} = this.props
|
||||
|
||||
return connectDragSource(
|
||||
<div className="customizable-field--drag">
|
||||
<span className="hamburger" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private get visibilityToggle(): JSX.Element {
|
||||
const {visible, internalName} = this.props
|
||||
|
||||
if (visible) {
|
||||
return (
|
||||
<div
|
||||
className="customizable-field--visibility"
|
||||
onClick={this.handleToggleVisible}
|
||||
title={`Click to HIDE ${internalName}`}
|
||||
>
|
||||
<span className="icon eye-open" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="customizable-field--visibility"
|
||||
onClick={this.handleToggleVisible}
|
||||
title={`Click to SHOW ${internalName}`}
|
||||
>
|
||||
<span className="icon eye-closed" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private get labelClassName(): string {
|
||||
const {visible} = this.props
|
||||
|
||||
if (visible) {
|
||||
return 'customizable-field--label'
|
||||
}
|
||||
|
||||
return 'customizable-field--label__hidden'
|
||||
}
|
||||
|
||||
private get columnClassName(): string {
|
||||
const {isDragging} = this.props
|
||||
|
||||
if (isDragging) {
|
||||
return 'customizable-field dragging'
|
||||
}
|
||||
|
||||
return 'customizable-field'
|
||||
}
|
||||
|
||||
private handleColumnRename = (e: ChangeEvent<HTMLInputElement>): void => {
|
||||
const {onUpdateColumn, internalName, visible} = this.props
|
||||
onUpdateColumn({internalName, displayName: e.target.value, visible})
|
||||
}
|
||||
|
||||
private handleToggleVisible = (): void => {
|
||||
const {onUpdateColumn, internalName, displayName, visible} = this.props
|
||||
onUpdateColumn({internalName, displayName, visible: !visible})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
import React, {Component} from 'react'
|
||||
import {DragDropContext} from 'react-dnd'
|
||||
import HTML5Backend from 'react-dnd-html5-backend'
|
||||
|
||||
import DraggableColumn from 'src/logs/components/draggable_column/DraggableColumn'
|
||||
|
||||
import {LogsTableColumn} from 'src/types/logs'
|
||||
|
||||
interface Props {
|
||||
columns: LogsTableColumn[]
|
||||
onMoveColumn: (dragIndex: number, hoverIndex: number) => void
|
||||
onUpdateColumn: (column: LogsTableColumn) => void
|
||||
}
|
||||
|
||||
class ColumnsOptions extends Component<Props> {
|
||||
public render() {
|
||||
const {columns} = this.props
|
||||
|
||||
return (
|
||||
<>
|
||||
<label className="form-label">Table Columns</label>
|
||||
<div className="logs-options--columns">
|
||||
{columns.map((c, i) => this.getDraggableColumn(c, i))}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
private getDraggableColumn(column: LogsTableColumn, i: number): JSX.Element {
|
||||
const {onMoveColumn, onUpdateColumn} = this.props
|
||||
if (column.internalName !== 'time') {
|
||||
return (
|
||||
<DraggableColumn
|
||||
key={column.internalName}
|
||||
index={i}
|
||||
id={column.internalName}
|
||||
internalName={column.internalName}
|
||||
displayName={column.displayName}
|
||||
visible={column.visible}
|
||||
onUpdateColumn={onUpdateColumn}
|
||||
onMoveColumn={onMoveColumn}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default DragDropContext(HTML5Backend)(ColumnsOptions)
|
|
@ -0,0 +1,201 @@
|
|||
import React, {Component} from 'react'
|
||||
import _ from 'lodash'
|
||||
|
||||
import {Button, ComponentStatus} from 'src/clockface'
|
||||
import Container from 'src/clockface/components/overlays/OverlayContainer'
|
||||
import Heading from 'src/clockface/components/overlays/OverlayHeading'
|
||||
import Body from 'src/clockface/components/overlays/OverlayBody'
|
||||
import SeverityOptions from 'src/logs/components/options_overlay/SeverityOptions'
|
||||
import ColumnsOptions from 'src/logs/components/options_overlay/ColumnsOptions'
|
||||
|
||||
import {
|
||||
SeverityLevelColor,
|
||||
SeverityColor,
|
||||
SeverityFormat,
|
||||
LogsTableColumn,
|
||||
SeverityLevelOptions,
|
||||
LogConfig,
|
||||
} from 'src/types/logs'
|
||||
|
||||
import {DEFAULT_SEVERITY_LEVELS} from 'src/logs/constants'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
interface Props {
|
||||
severityLevelColors: SeverityLevelColor[]
|
||||
columns: LogsTableColumn[]
|
||||
severityFormat: SeverityFormat
|
||||
onDismissOverlay: () => void
|
||||
onSave: (config: Partial<LogConfig>) => Promise<void>
|
||||
}
|
||||
|
||||
interface State {
|
||||
workingLevelColumns: SeverityLevelColor[]
|
||||
workingColumns: LogsTableColumn[]
|
||||
workingFormat: SeverityFormat
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
class OptionsOverlay extends Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
workingLevelColumns: this.props.severityLevelColors,
|
||||
workingColumns: this.props.columns,
|
||||
workingFormat: this.props.severityFormat,
|
||||
}
|
||||
}
|
||||
|
||||
public shouldComponentUpdate(__, nextState: State) {
|
||||
const isColorsDifferent = !_.isEqual(
|
||||
nextState.workingLevelColumns,
|
||||
this.state.workingLevelColumns
|
||||
)
|
||||
const isFormatDifferent = !_.isEqual(
|
||||
nextState.workingFormat,
|
||||
this.state.workingFormat
|
||||
)
|
||||
const isColumnsDifferent = !_.isEqual(
|
||||
nextState.workingColumns,
|
||||
this.state.workingColumns
|
||||
)
|
||||
|
||||
if (isColorsDifferent || isFormatDifferent || isColumnsDifferent) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {workingLevelColumns, workingColumns, workingFormat} = this.state
|
||||
|
||||
return (
|
||||
<Container maxWidth={800}>
|
||||
<Heading title="Configure Log Viewer">
|
||||
{this.overlayActionButtons}
|
||||
</Heading>
|
||||
<Body>
|
||||
<div className="row">
|
||||
<div className="col-sm-5">
|
||||
<SeverityOptions
|
||||
severityLevelColors={workingLevelColumns}
|
||||
onReset={this.handleResetSeverityLevels}
|
||||
onChangeSeverityLevel={this.handleChangeSeverityLevel}
|
||||
severityFormat={workingFormat}
|
||||
onChangeSeverityFormat={this.handleChangeSeverityFormat}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-sm-7">
|
||||
<ColumnsOptions
|
||||
columns={workingColumns}
|
||||
onMoveColumn={this.handleMoveColumn}
|
||||
onUpdateColumn={this.handleUpdateColumn}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Body>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
private get overlayActionButtons(): JSX.Element {
|
||||
const {onDismissOverlay} = this.props
|
||||
|
||||
return (
|
||||
<div className="btn-group--right">
|
||||
<Button text="Cancel" onClick={onDismissOverlay} />
|
||||
<Button
|
||||
onClick={this.handleSave}
|
||||
status={this.isSaveDisabled}
|
||||
text="Save"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private get isSaveDisabled(): ComponentStatus {
|
||||
const {workingLevelColumns, workingColumns, workingFormat} = this.state
|
||||
const {severityLevelColors, columns, severityFormat} = this.props
|
||||
|
||||
const severityChanged = !_.isEqual(workingLevelColumns, severityLevelColors)
|
||||
const columnsChanged = !_.isEqual(workingColumns, columns)
|
||||
const formatChanged = !_.isEqual(workingFormat, severityFormat)
|
||||
|
||||
if (severityChanged || columnsChanged || formatChanged) {
|
||||
return ComponentStatus.Default
|
||||
}
|
||||
|
||||
return ComponentStatus.Disabled
|
||||
}
|
||||
|
||||
private handleSave = async () => {
|
||||
const {onDismissOverlay, onSave} = this.props
|
||||
const {workingLevelColumns, workingFormat, workingColumns} = this.state
|
||||
|
||||
await onSave({
|
||||
tableColumns: workingColumns,
|
||||
severityFormat: workingFormat,
|
||||
severityLevelColors: workingLevelColumns,
|
||||
})
|
||||
onDismissOverlay()
|
||||
}
|
||||
|
||||
private handleResetSeverityLevels = (): void => {
|
||||
const defaults = _.map(DEFAULT_SEVERITY_LEVELS, (color, level) => {
|
||||
return {level: SeverityLevelOptions[level], color}
|
||||
})
|
||||
this.setState({workingLevelColumns: defaults})
|
||||
}
|
||||
|
||||
private handleChangeSeverityLevel = (
|
||||
severityLevel: string,
|
||||
override: SeverityColor
|
||||
): void => {
|
||||
const workingLevelColumns = this.state.workingLevelColumns.map(config => {
|
||||
if (config.level === severityLevel) {
|
||||
return {...config, color: override.name}
|
||||
}
|
||||
|
||||
return config
|
||||
})
|
||||
|
||||
this.setState({workingLevelColumns})
|
||||
}
|
||||
|
||||
private handleChangeSeverityFormat = (format: SeverityFormat) => {
|
||||
this.setState({workingFormat: format})
|
||||
}
|
||||
|
||||
private handleMoveColumn = (dragIndex, hoverIndex) => {
|
||||
const {workingColumns} = this.state
|
||||
|
||||
const draggedField = workingColumns[dragIndex]
|
||||
|
||||
const columnsRemoved = _.concat(
|
||||
_.slice(workingColumns, 0, dragIndex),
|
||||
_.slice(workingColumns, dragIndex + 1)
|
||||
)
|
||||
|
||||
const columnsAdded = _.concat(
|
||||
_.slice(columnsRemoved, 0, hoverIndex),
|
||||
[draggedField],
|
||||
_.slice(columnsRemoved, hoverIndex)
|
||||
)
|
||||
|
||||
this.setState({workingColumns: columnsAdded})
|
||||
}
|
||||
|
||||
private handleUpdateColumn = (column: LogsTableColumn) => {
|
||||
const workingColumns = this.state.workingColumns.map(wc => {
|
||||
if (wc.internalName === column.internalName) {
|
||||
return column
|
||||
}
|
||||
|
||||
return wc
|
||||
})
|
||||
|
||||
this.setState({workingColumns})
|
||||
}
|
||||
}
|
||||
|
||||
export default OptionsOverlay
|
|
@ -0,0 +1,67 @@
|
|||
// Libraries
|
||||
import React, {PureComponent} from 'react'
|
||||
|
||||
// Components
|
||||
import {Radio, ButtonShape} from 'src/clockface'
|
||||
|
||||
// Constants
|
||||
import {SeverityFormatOptions} from 'src/types/logs'
|
||||
|
||||
// Types
|
||||
import {SeverityFormat} from 'src/types/logs'
|
||||
|
||||
// Decorators
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
interface Props {
|
||||
format: SeverityFormat
|
||||
onChangeFormat: (format: SeverityFormat) => void
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
class SeverityColumnFormat extends PureComponent<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {format, onChangeFormat} = this.props
|
||||
|
||||
return (
|
||||
<div className="graph-options-group">
|
||||
<label className="form-label">Severity Format</label>
|
||||
<Radio shape={ButtonShape.StretchToFit}>
|
||||
<Radio.Button
|
||||
active={format === SeverityFormatOptions.Dot}
|
||||
id="severity-format-option--dot"
|
||||
value={SeverityFormatOptions.Dot}
|
||||
onClick={onChangeFormat}
|
||||
titleText="Show only a dot in the severity column"
|
||||
>
|
||||
Dot
|
||||
</Radio.Button>
|
||||
<Radio.Button
|
||||
active={format === SeverityFormatOptions.DotText}
|
||||
id="severity-format-option--dot-text"
|
||||
value={SeverityFormatOptions.DotText}
|
||||
onClick={onChangeFormat}
|
||||
titleText="Show both a dot and the severity name in the severity column"
|
||||
>
|
||||
Dot + Text
|
||||
</Radio.Button>
|
||||
<Radio.Button
|
||||
active={format === SeverityFormatOptions.Text}
|
||||
id="severity-format-option--text"
|
||||
value={SeverityFormatOptions.Text}
|
||||
onClick={onChangeFormat}
|
||||
titleText="Show only the severity name in the severity column"
|
||||
>
|
||||
Text
|
||||
</Radio.Button>
|
||||
</Radio>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default SeverityColumnFormat
|
|
@ -0,0 +1,65 @@
|
|||
import React, {SFC} from 'react'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import {Button, IconFont, ButtonShape} from 'src/clockface'
|
||||
import ColorDropdown from 'src/shared/components/color_dropdown/ColorDropdown'
|
||||
import SeverityColumnFormat from 'src/logs/components/options_overlay/SeverityColumnFormat'
|
||||
|
||||
import {
|
||||
SeverityLevelColor,
|
||||
SeverityColor,
|
||||
SeverityFormat,
|
||||
SeverityColorValues,
|
||||
} from 'src/types/logs'
|
||||
|
||||
interface Props {
|
||||
severityLevelColors: SeverityLevelColor[]
|
||||
onReset: () => void
|
||||
onChangeSeverityLevel: (severity: string, override: SeverityColor) => void
|
||||
severityFormat: SeverityFormat
|
||||
onChangeSeverityFormat: (format: SeverityFormat) => void
|
||||
}
|
||||
|
||||
const SeverityConfig: SFC<Props> = ({
|
||||
severityLevelColors,
|
||||
onReset,
|
||||
onChangeSeverityLevel,
|
||||
severityFormat,
|
||||
onChangeSeverityFormat,
|
||||
}) => (
|
||||
<>
|
||||
<label className="form-label">Severity Colors</label>
|
||||
<div className="logs-options--color-list">
|
||||
{severityLevelColors.map(lc => {
|
||||
const color = {name: lc.color, hex: SeverityColorValues[lc.color]}
|
||||
return (
|
||||
<div key={uuid.v4()} className="logs-options--color-row">
|
||||
<div className="logs-options--color-column">
|
||||
<div className="logs-options--color-label">{lc.level}</div>
|
||||
</div>
|
||||
<div className="logs-options--color-column">
|
||||
<ColorDropdown
|
||||
selected={color}
|
||||
onChoose={onChangeSeverityLevel}
|
||||
stretchToFit={true}
|
||||
severityLevel={lc.level}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<Button
|
||||
shape={ButtonShape.Default}
|
||||
icon={IconFont.Refresh}
|
||||
onClick={onReset}
|
||||
text="Reset to Defaults"
|
||||
/>
|
||||
<SeverityColumnFormat
|
||||
format={severityFormat}
|
||||
onChangeFormat={onChangeSeverityFormat}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
export default SeverityConfig
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
Styles for Logs Viewer Options Overlay
|
||||
----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
.logs-options--color-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.logs-options--color-row {
|
||||
display: flex;
|
||||
height: 30px;
|
||||
align-items: stretch;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.logs-options--color-column {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.logs-options--color-label {
|
||||
height: 30px;
|
||||
border-radius: $radius;
|
||||
background-color: $g3-castle;
|
||||
padding: 0 11px;
|
||||
line-height: 30px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
color: $g13-mist;
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
margin-right: 4px;
|
||||
@include no-user-select();
|
||||
}
|
||||
|
||||
// Not very clean way of slightly darkening the disabled state
|
||||
// of draggable columns in the overlay
|
||||
.logs-options--columns .customizable-field--label__hidden {
|
||||
background-color: $g3-castle;
|
||||
}
|
||||
|
|
@ -1,4 +1,9 @@
|
|||
import {LogViewerView, ViewType, ViewShape} from 'src/types/v2/dashboards'
|
||||
import {
|
||||
SeverityColorValues,
|
||||
SeverityColorOptions,
|
||||
SeverityLevelOptions,
|
||||
} from 'src/types/logs'
|
||||
|
||||
export const NOW = 0
|
||||
export const DEFAULT_TRUNCATION = true
|
||||
|
@ -11,3 +16,97 @@ export const EMPTY_VIEW_PROPERTIES: LogViewerView = {
|
|||
type: ViewType.LogViewer,
|
||||
shape: ViewShape.ChronografV2,
|
||||
}
|
||||
|
||||
export const DEFAULT_SEVERITY_LEVELS = {
|
||||
[SeverityLevelOptions.Emerg]: SeverityColorOptions.Ruby,
|
||||
[SeverityLevelOptions.Alert]: SeverityColorOptions.Fire,
|
||||
[SeverityLevelOptions.Crit]: SeverityColorOptions.Curacao,
|
||||
[SeverityLevelOptions.Err]: SeverityColorOptions.Tiger,
|
||||
[SeverityLevelOptions.Warning]: SeverityColorOptions.Pineapple,
|
||||
[SeverityLevelOptions.Notice]: SeverityColorOptions.Rainforest,
|
||||
[SeverityLevelOptions.Info]: SeverityColorOptions.Star,
|
||||
[SeverityLevelOptions.Debug]: SeverityColorOptions.Wolf,
|
||||
}
|
||||
|
||||
export const SEVERITY_COLORS = [
|
||||
{
|
||||
hex: SeverityColorValues[SeverityColorOptions.Ruby],
|
||||
name: SeverityColorOptions.Ruby,
|
||||
},
|
||||
{
|
||||
hex: SeverityColorValues[SeverityColorOptions.Fire],
|
||||
name: SeverityColorOptions.Fire,
|
||||
},
|
||||
{
|
||||
hex: SeverityColorValues[SeverityColorOptions.Curacao],
|
||||
name: SeverityColorOptions.Curacao,
|
||||
},
|
||||
{
|
||||
hex: SeverityColorValues[SeverityColorOptions.Tiger],
|
||||
name: SeverityColorOptions.Tiger,
|
||||
},
|
||||
{
|
||||
hex: SeverityColorValues[SeverityColorOptions.Pineapple],
|
||||
name: SeverityColorOptions.Pineapple,
|
||||
},
|
||||
{
|
||||
hex: SeverityColorValues[SeverityColorOptions.Thunder],
|
||||
name: SeverityColorOptions.Thunder,
|
||||
},
|
||||
{
|
||||
hex: SeverityColorValues[SeverityColorOptions.Sulfur],
|
||||
name: SeverityColorOptions.Sulfur,
|
||||
},
|
||||
{
|
||||
hex: SeverityColorValues[SeverityColorOptions.Viridian],
|
||||
name: SeverityColorOptions.Viridian,
|
||||
},
|
||||
{
|
||||
hex: SeverityColorValues[SeverityColorOptions.Rainforest],
|
||||
name: SeverityColorOptions.Rainforest,
|
||||
},
|
||||
{
|
||||
hex: SeverityColorValues[SeverityColorOptions.Honeydew],
|
||||
name: SeverityColorOptions.Honeydew,
|
||||
},
|
||||
{
|
||||
hex: SeverityColorValues[SeverityColorOptions.Ocean],
|
||||
name: SeverityColorOptions.Ocean,
|
||||
},
|
||||
{
|
||||
hex: SeverityColorValues[SeverityColorOptions.Pool],
|
||||
name: SeverityColorOptions.Pool,
|
||||
},
|
||||
{
|
||||
hex: SeverityColorValues[SeverityColorOptions.Laser],
|
||||
name: SeverityColorOptions.Laser,
|
||||
},
|
||||
{
|
||||
hex: SeverityColorValues[SeverityColorOptions.Planet],
|
||||
name: SeverityColorOptions.Planet,
|
||||
},
|
||||
{
|
||||
hex: SeverityColorValues[SeverityColorOptions.Star],
|
||||
name: SeverityColorOptions.Star,
|
||||
},
|
||||
{
|
||||
hex: SeverityColorValues[SeverityColorOptions.Comet],
|
||||
name: SeverityColorOptions.Comet,
|
||||
},
|
||||
{
|
||||
hex: SeverityColorValues[SeverityColorOptions.Graphite],
|
||||
name: SeverityColorOptions.Graphite,
|
||||
},
|
||||
{
|
||||
hex: SeverityColorValues[SeverityColorOptions.Wolf],
|
||||
name: SeverityColorOptions.Wolf,
|
||||
},
|
||||
{
|
||||
hex: SeverityColorValues[SeverityColorOptions.Mist],
|
||||
name: SeverityColorOptions.Mist,
|
||||
},
|
||||
{
|
||||
hex: SeverityColorValues[SeverityColorOptions.Pearl],
|
||||
name: SeverityColorOptions.Pearl,
|
||||
},
|
||||
]
|
||||
|
|
|
@ -23,7 +23,7 @@ export const defaultState: LogsState = {
|
|||
id: null,
|
||||
link: null,
|
||||
tableColumns: [],
|
||||
severityFormat: SeverityFormatOptions.dotText,
|
||||
severityFormat: SeverityFormatOptions.DotText,
|
||||
severityLevelColors: [],
|
||||
isTruncated: DEFAULT_TRUNCATION,
|
||||
},
|
||||
|
|
|
@ -212,8 +212,8 @@ describe('Logs.Config', () => {
|
|||
const severityFormatDotText = generateColumnFormatConfig(viewDotText)
|
||||
const severityFormatDot = generateColumnFormatConfig(viewColumnDot)
|
||||
|
||||
expect(severityFormatDotText).toBe(SeverityFormatOptions.dotText)
|
||||
expect(severityFormatDot).toBe(SeverityFormatOptions.dot)
|
||||
expect(severityFormatDotText).toBe(SeverityFormatOptions.DotText)
|
||||
expect(severityFormatDot).toBe(SeverityFormatOptions.Dot)
|
||||
})
|
||||
|
||||
it('sorts columns by column position', () => {
|
||||
|
@ -256,12 +256,12 @@ describe('Logs.Config', () => {
|
|||
|
||||
const expectedColors = [
|
||||
{
|
||||
level: SeverityLevelOptions.emerg,
|
||||
color: SeverityColorOptions.pineapple,
|
||||
level: SeverityLevelOptions.Emerg,
|
||||
color: SeverityColorOptions.Pineapple,
|
||||
},
|
||||
{
|
||||
level: SeverityLevelOptions.err,
|
||||
color: SeverityColorOptions.fire,
|
||||
level: SeverityLevelOptions.Err,
|
||||
color: SeverityColorOptions.Fire,
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -285,15 +285,15 @@ describe('Logs.Config', () => {
|
|||
{internalName: 'appname', displayName: 'Application', visible: true},
|
||||
{internalName: 'host', displayName: '', visible: true},
|
||||
],
|
||||
severityFormat: SeverityFormatOptions.dotText,
|
||||
severityFormat: SeverityFormatOptions.DotText,
|
||||
severityLevelColors: [
|
||||
{
|
||||
level: SeverityLevelOptions.alert,
|
||||
color: SeverityColorOptions.pearl,
|
||||
level: SeverityLevelOptions.Alert,
|
||||
color: SeverityColorOptions.Pearl,
|
||||
},
|
||||
{
|
||||
level: SeverityLevelOptions.warning,
|
||||
color: SeverityColorOptions.wolf,
|
||||
level: SeverityLevelOptions.Warning,
|
||||
color: SeverityColorOptions.Wolf,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
@ -325,8 +325,8 @@ describe('Logs.Config', () => {
|
|||
})
|
||||
|
||||
it('generates label settings from view column settings', () => {
|
||||
const severityFormatDotText = SeverityFormatOptions.dotText
|
||||
const severityFormatDot = SeverityFormatOptions.dot
|
||||
const severityFormatDotText = SeverityFormatOptions.DotText
|
||||
const severityFormatDot = SeverityFormatOptions.Dot
|
||||
|
||||
const settingsDotText = generateViewColumnSeverityLabels(
|
||||
severityFormatDotText
|
||||
|
@ -357,20 +357,20 @@ describe('Logs.Config', () => {
|
|||
it('generates color settings from severityLevelColors', () => {
|
||||
const severityLevelColors = [
|
||||
{
|
||||
level: SeverityLevelOptions.emerg,
|
||||
color: SeverityColorOptions.pearl,
|
||||
level: SeverityLevelOptions.Emerg,
|
||||
color: SeverityColorOptions.Pearl,
|
||||
},
|
||||
{
|
||||
level: SeverityLevelOptions.alert,
|
||||
color: SeverityColorOptions.mist,
|
||||
level: SeverityLevelOptions.Alert,
|
||||
color: SeverityColorOptions.Mist,
|
||||
},
|
||||
{
|
||||
level: SeverityLevelOptions.crit,
|
||||
color: SeverityColorOptions.wolf,
|
||||
level: SeverityLevelOptions.Crit,
|
||||
color: SeverityColorOptions.Wolf,
|
||||
},
|
||||
{
|
||||
level: SeverityLevelOptions.err,
|
||||
color: SeverityColorOptions.graphite,
|
||||
level: SeverityLevelOptions.Err,
|
||||
color: SeverityColorOptions.Graphite,
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -381,22 +381,22 @@ describe('Logs.Config', () => {
|
|||
{
|
||||
type: 'color',
|
||||
name: 'emerg',
|
||||
value: SeverityColorOptions.pearl,
|
||||
value: SeverityColorOptions.Pearl,
|
||||
},
|
||||
{
|
||||
type: 'color',
|
||||
name: 'alert',
|
||||
value: SeverityColorOptions.mist,
|
||||
value: SeverityColorOptions.Mist,
|
||||
},
|
||||
{
|
||||
type: 'color',
|
||||
name: 'crit',
|
||||
value: SeverityColorOptions.wolf,
|
||||
value: SeverityColorOptions.Wolf,
|
||||
},
|
||||
{
|
||||
type: 'color',
|
||||
name: 'err',
|
||||
value: SeverityColorOptions.graphite,
|
||||
value: SeverityColorOptions.Graphite,
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -415,12 +415,12 @@ describe('Logs.Config', () => {
|
|||
displayName: '',
|
||||
visible: true,
|
||||
}
|
||||
const severityFormat = SeverityFormatOptions.dotText
|
||||
const severityFormat = SeverityFormatOptions.DotText
|
||||
const severityLevelColors = [
|
||||
{level: SeverityLevelOptions.emerg, color: SeverityColorOptions.pearl},
|
||||
{level: SeverityLevelOptions.alert, color: SeverityColorOptions.mist},
|
||||
{level: SeverityLevelOptions.crit, color: SeverityColorOptions.wolf},
|
||||
{level: SeverityLevelOptions.err, color: SeverityColorOptions.graphite},
|
||||
{level: SeverityLevelOptions.Emerg, color: SeverityColorOptions.Pearl},
|
||||
{level: SeverityLevelOptions.Alert, color: SeverityColorOptions.Mist},
|
||||
{level: SeverityLevelOptions.Crit, color: SeverityColorOptions.Wolf},
|
||||
{level: SeverityLevelOptions.Err, color: SeverityColorOptions.Graphite},
|
||||
]
|
||||
const settingsSeverity = generateViewColumnSettings(
|
||||
tableColumnSeverity,
|
||||
|
@ -452,22 +452,22 @@ describe('Logs.Config', () => {
|
|||
{
|
||||
type: 'color',
|
||||
name: 'emerg',
|
||||
value: SeverityColorOptions.pearl,
|
||||
value: SeverityColorOptions.Pearl,
|
||||
},
|
||||
{
|
||||
type: 'color',
|
||||
name: 'alert',
|
||||
value: SeverityColorOptions.mist,
|
||||
value: SeverityColorOptions.Mist,
|
||||
},
|
||||
{
|
||||
type: 'color',
|
||||
name: 'crit',
|
||||
value: SeverityColorOptions.wolf,
|
||||
value: SeverityColorOptions.Wolf,
|
||||
},
|
||||
{
|
||||
type: 'color',
|
||||
name: 'err',
|
||||
value: SeverityColorOptions.graphite,
|
||||
value: SeverityColorOptions.Graphite,
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -497,15 +497,15 @@ describe('Logs.Config', () => {
|
|||
{internalName: 'appname', displayName: 'Application', visible: true},
|
||||
{internalName: 'host', displayName: '', visible: true},
|
||||
],
|
||||
severityFormat: SeverityFormatOptions.dotText,
|
||||
severityFormat: SeverityFormatOptions.DotText,
|
||||
severityLevelColors: [
|
||||
{
|
||||
level: SeverityLevelOptions.alert,
|
||||
color: SeverityColorOptions.pearl,
|
||||
level: SeverityLevelOptions.Alert,
|
||||
color: SeverityColorOptions.Pearl,
|
||||
},
|
||||
{
|
||||
level: SeverityLevelOptions.warning,
|
||||
color: SeverityColorOptions.wolf,
|
||||
level: SeverityLevelOptions.Warning,
|
||||
color: SeverityColorOptions.Wolf,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
|
@ -73,11 +73,11 @@ export const generateColumnConfig = (
|
|||
const settings: LogsTableColumn = column.settings.reduce(
|
||||
(acc, e) => {
|
||||
if (
|
||||
e.type === ColumnSettingTypes.visibility &&
|
||||
e.value === ColumnSettingVisibilityOptions.visible
|
||||
e.type === ColumnSettingTypes.Visibility &&
|
||||
e.value === ColumnSettingVisibilityOptions.Visible
|
||||
) {
|
||||
acc.visible = true
|
||||
} else if (e.type === ColumnSettingTypes.display) {
|
||||
} else if (e.type === ColumnSettingTypes.Display) {
|
||||
acc.displayName = e.value
|
||||
}
|
||||
return acc
|
||||
|
@ -94,22 +94,22 @@ export const generateColumnFormatConfig = (
|
|||
let hasIcon = false
|
||||
|
||||
column.settings.forEach(e => {
|
||||
if (e.type === ColumnSettingTypes.label) {
|
||||
if (e.value === ColumnSettingLabelOptions.icon) {
|
||||
if (e.type === ColumnSettingTypes.Label) {
|
||||
if (e.value === ColumnSettingLabelOptions.Icon) {
|
||||
hasIcon = true
|
||||
}
|
||||
if (e.value === ColumnSettingLabelOptions.text) {
|
||||
if (e.value === ColumnSettingLabelOptions.Text) {
|
||||
hasText = true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (hasText && hasIcon) {
|
||||
return SeverityFormatOptions.dotText
|
||||
return SeverityFormatOptions.DotText
|
||||
} else if (hasText) {
|
||||
return SeverityFormatOptions.text
|
||||
return SeverityFormatOptions.Text
|
||||
} else {
|
||||
return SeverityFormatOptions.dot
|
||||
return SeverityFormatOptions.Dot
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,15 +117,19 @@ export const generateColumnColorsConfig = (
|
|||
column: LogViewerColumn
|
||||
): SeverityLevelColor[] => {
|
||||
const colors = column.settings.filter(
|
||||
e => e.type === ColumnSettingTypes.color
|
||||
e => e.type === ColumnSettingTypes.Color
|
||||
)
|
||||
return colors.map(c => {
|
||||
const level: SeverityLevelOptions = SeverityLevelOptions[c.name]
|
||||
const color: SeverityColorOptions = SeverityColorOptions[c.value]
|
||||
const level: SeverityLevelOptions = SeverityLevelOptions[capitalize(c.name)]
|
||||
const color: SeverityColorOptions =
|
||||
SeverityColorOptions[capitalize(c.value)]
|
||||
return {level, color}
|
||||
})
|
||||
}
|
||||
|
||||
const capitalize = (word: string): string =>
|
||||
word.charAt(0).toUpperCase() + word.slice(1)
|
||||
|
||||
export const uiToServerConfig = (config: LogConfig): View => {
|
||||
const properties: LogViewerView = generateViewProperties(config)
|
||||
|
||||
|
@ -174,19 +178,19 @@ export const generateViewColumns = (
|
|||
|
||||
if (tableColumn.visible) {
|
||||
settings.push({
|
||||
type: ColumnSettingTypes.visibility,
|
||||
value: ColumnSettingVisibilityOptions.visible,
|
||||
type: ColumnSettingTypes.Visibility,
|
||||
value: ColumnSettingVisibilityOptions.Visible,
|
||||
})
|
||||
} else {
|
||||
settings.push({
|
||||
type: ColumnSettingTypes.visibility,
|
||||
value: ColumnSettingVisibilityOptions.hidden,
|
||||
type: ColumnSettingTypes.Visibility,
|
||||
value: ColumnSettingVisibilityOptions.Hidden,
|
||||
})
|
||||
}
|
||||
|
||||
if (!_.isEmpty(tableColumn.displayName)) {
|
||||
settings.push({
|
||||
type: ColumnSettingTypes.display,
|
||||
type: ColumnSettingTypes.Display,
|
||||
value: tableColumn.displayName,
|
||||
})
|
||||
}
|
||||
|
@ -215,18 +219,18 @@ export const generateViewColumnSeverityLabels = (
|
|||
format: SeverityFormat
|
||||
): LogViewerColumnSetting[] => {
|
||||
switch (format) {
|
||||
case SeverityFormatOptions.dot:
|
||||
case SeverityFormatOptions.Dot:
|
||||
return [
|
||||
{type: ColumnSettingTypes.label, value: ColumnSettingLabelOptions.icon},
|
||||
{type: ColumnSettingTypes.Label, value: ColumnSettingLabelOptions.Icon},
|
||||
]
|
||||
case SeverityFormatOptions.text:
|
||||
case SeverityFormatOptions.Text:
|
||||
return [
|
||||
{type: ColumnSettingTypes.label, value: ColumnSettingLabelOptions.text},
|
||||
{type: ColumnSettingTypes.Label, value: ColumnSettingLabelOptions.Text},
|
||||
]
|
||||
case SeverityFormatOptions.dotText:
|
||||
case SeverityFormatOptions.DotText:
|
||||
return [
|
||||
{type: ColumnSettingTypes.label, value: ColumnSettingLabelOptions.icon},
|
||||
{type: ColumnSettingTypes.label, value: ColumnSettingLabelOptions.text},
|
||||
{type: ColumnSettingTypes.Label, value: ColumnSettingLabelOptions.Icon},
|
||||
{type: ColumnSettingTypes.Label, value: ColumnSettingLabelOptions.Text},
|
||||
]
|
||||
}
|
||||
return null
|
||||
|
@ -236,6 +240,6 @@ export const generateViewColumnSeverityColors = (
|
|||
levelColors: SeverityLevelColor[]
|
||||
): LogViewerColumnSetting[] => {
|
||||
return levelColors.map(({color, level}) => {
|
||||
return {type: ColumnSettingTypes.color, value: color, name: level}
|
||||
return {type: ColumnSettingTypes.Color, value: color, name: level}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -15,19 +15,19 @@ describe('Logs.searchToFilters', () => {
|
|||
id: isUUID,
|
||||
key: 'message',
|
||||
value: 'seq_!@.#',
|
||||
operator: Operator.LIKE,
|
||||
operator: Operator.Like,
|
||||
},
|
||||
{
|
||||
id: isUUID,
|
||||
key: 'message',
|
||||
value: 'TERMS',
|
||||
operator: Operator.LIKE,
|
||||
operator: Operator.Like,
|
||||
},
|
||||
{
|
||||
id: isUUID,
|
||||
key: 'message',
|
||||
value: '/api/search',
|
||||
operator: Operator.LIKE,
|
||||
operator: Operator.Like,
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -43,25 +43,25 @@ describe('Logs.searchToFilters', () => {
|
|||
id: isUUID,
|
||||
key: 'severity',
|
||||
value: 'info',
|
||||
operator: Operator.EQUAL,
|
||||
operator: Operator.Equal,
|
||||
},
|
||||
{
|
||||
id: isUUID,
|
||||
key: 'message',
|
||||
value: ':TERMS',
|
||||
operator: Operator.LIKE,
|
||||
operator: Operator.Like,
|
||||
},
|
||||
{
|
||||
id: isUUID,
|
||||
key: 'host',
|
||||
value: 'del.local',
|
||||
operator: Operator.NOT_EQUAL,
|
||||
operator: Operator.NotEqual,
|
||||
},
|
||||
{
|
||||
id: isUUID,
|
||||
key: 'message',
|
||||
value: 'foo:',
|
||||
operator: Operator.LIKE,
|
||||
operator: Operator.Like,
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -77,19 +77,19 @@ describe('Logs.searchToFilters', () => {
|
|||
id: isUUID,
|
||||
key: 'message',
|
||||
value: '/api/search',
|
||||
operator: Operator.LIKE,
|
||||
operator: Operator.Like,
|
||||
},
|
||||
{
|
||||
id: isUUID,
|
||||
key: 'message',
|
||||
value: 'status_bad',
|
||||
operator: Operator.NOT_LIKE,
|
||||
operator: Operator.NotLike,
|
||||
},
|
||||
{
|
||||
id: isUUID,
|
||||
key: 'message',
|
||||
value: '@123!',
|
||||
operator: Operator.NOT_LIKE,
|
||||
operator: Operator.NotLike,
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -105,13 +105,13 @@ describe('Logs.searchToFilters', () => {
|
|||
id: isUUID,
|
||||
key: 'message',
|
||||
value: '/api/search status:200',
|
||||
operator: Operator.LIKE,
|
||||
operator: Operator.Like,
|
||||
},
|
||||
{
|
||||
id: isUUID,
|
||||
key: 'message',
|
||||
value: 'a success',
|
||||
operator: Operator.LIKE,
|
||||
operator: Operator.Like,
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -127,13 +127,13 @@ describe('Logs.searchToFilters', () => {
|
|||
id: isUUID,
|
||||
key: 'message',
|
||||
value: '/api/search status:200',
|
||||
operator: Operator.NOT_LIKE,
|
||||
operator: Operator.NotLike,
|
||||
},
|
||||
{
|
||||
id: isUUID,
|
||||
key: 'message',
|
||||
value: 'a success',
|
||||
operator: Operator.NOT_LIKE,
|
||||
operator: Operator.NotLike,
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -149,25 +149,25 @@ describe('Logs.searchToFilters', () => {
|
|||
id: isUUID,
|
||||
key: 'severity',
|
||||
value: '4\\d{2}',
|
||||
operator: Operator.EQUAL,
|
||||
operator: Operator.Equal,
|
||||
},
|
||||
{
|
||||
id: isUUID,
|
||||
key: 'message',
|
||||
value: 'NOT FOUND',
|
||||
operator: Operator.NOT_LIKE,
|
||||
operator: Operator.NotLike,
|
||||
},
|
||||
{
|
||||
id: isUUID,
|
||||
key: 'message',
|
||||
value: 'some "quote"',
|
||||
operator: Operator.LIKE,
|
||||
operator: Operator.Like,
|
||||
},
|
||||
{
|
||||
id: isUUID,
|
||||
key: 'message',
|
||||
value: 'thing',
|
||||
operator: Operator.NOT_LIKE,
|
||||
operator: Operator.NotLike,
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -183,7 +183,7 @@ describe('Logs.searchToFilters', () => {
|
|||
id: isUUID,
|
||||
key: 'message',
|
||||
value: "some 'quote'",
|
||||
operator: Operator.LIKE,
|
||||
operator: Operator.Like,
|
||||
},
|
||||
]
|
||||
|
||||
|
|
|
@ -16,31 +16,31 @@ const APP_NAME = 'appname'
|
|||
|
||||
export const createRule = (
|
||||
part: TermPart,
|
||||
type: TermType = TermType.INCLUDE
|
||||
type: TermType = TermType.Include
|
||||
): TermRule => ({
|
||||
type,
|
||||
pattern: getPattern(type, part),
|
||||
})
|
||||
|
||||
const getPattern = (type: TermType, phrase: TermPart): RegExp => {
|
||||
const {ATTRIBUTE, COLON, EXCLUSION} = TermPart
|
||||
const PHRASE = `(${ATTRIBUTE}${COLON})?${phrase}`
|
||||
const {Attribute, Colon, Exclusion} = TermPart
|
||||
const phrasePattern = `(${Attribute}${Colon})?${phrase}`
|
||||
|
||||
switch (type) {
|
||||
case TermType.EXCLUDE:
|
||||
return new RegExp(`^${EXCLUSION}${PHRASE}`)
|
||||
case TermType.Exclude:
|
||||
return new RegExp(`^${Exclusion}${phrasePattern}`)
|
||||
default:
|
||||
return new RegExp(`^${PHRASE}`)
|
||||
return new RegExp(`^${phrasePattern}`)
|
||||
}
|
||||
}
|
||||
|
||||
export const LOG_SEARCH_TERMS: TermRule[] = [
|
||||
createRule(TermPart.SINGLE_QUOTED, TermType.EXCLUDE),
|
||||
createRule(TermPart.DOUBLE_QUOTED, TermType.EXCLUDE),
|
||||
createRule(TermPart.SINGLE_QUOTED),
|
||||
createRule(TermPart.DOUBLE_QUOTED),
|
||||
createRule(TermPart.UNQUOTED_WORD, TermType.EXCLUDE),
|
||||
createRule(TermPart.UNQUOTED_WORD),
|
||||
createRule(TermPart.SingleQuoted, TermType.Exclude),
|
||||
createRule(TermPart.DoubleQuoted, TermType.Exclude),
|
||||
createRule(TermPart.SingleQuoted),
|
||||
createRule(TermPart.DoubleQuoted),
|
||||
createRule(TermPart.UnquotedWord, TermType.Exclude),
|
||||
createRule(TermPart.UnquotedWord),
|
||||
]
|
||||
|
||||
export const searchToFilters = (searchTerm: string): Filter[] => {
|
||||
|
@ -116,9 +116,9 @@ const termToOp = (term: Term): Operator => {
|
|||
switch (term.attribute) {
|
||||
case MESSAGE_KEY:
|
||||
case APP_NAME:
|
||||
return handleOpExclusion(term, Operator.LIKE, Operator.NOT_LIKE)
|
||||
return handleOpExclusion(term, Operator.Like, Operator.NotLike)
|
||||
default:
|
||||
return handleOpExclusion(term, Operator.EQUAL, Operator.NOT_EQUAL)
|
||||
return handleOpExclusion(term, Operator.Equal, Operator.NotEqual)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,9 +128,9 @@ const handleOpExclusion = (
|
|||
exclusion: Operator
|
||||
): Operator => {
|
||||
switch (term.type) {
|
||||
case TermType.EXCLUDE:
|
||||
case TermType.Exclude:
|
||||
return exclusion
|
||||
case TermType.INCLUDE:
|
||||
case TermType.Include:
|
||||
return inclusion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
import React, {Component, MouseEvent} from 'react'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import {ClickOutside} from 'src/shared/components/ClickOutside'
|
||||
import FancyScrollbar from 'src/shared/components/fancy_scrollbar/FancyScrollbar'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
import {DROPDOWN_MENU_MAX_HEIGHT} from 'src/shared/constants/index'
|
||||
import {SEVERITY_COLORS} from 'src/logs/constants'
|
||||
|
||||
import {SeverityColor, SeverityColorOptions} from 'src/types/logs'
|
||||
|
||||
interface Props {
|
||||
selected: SeverityColor
|
||||
disabled?: boolean
|
||||
stretchToFit?: boolean
|
||||
onChoose: (severityLevel: string, colors: SeverityColor) => void
|
||||
severityLevel: string
|
||||
}
|
||||
|
||||
interface State {
|
||||
expanded: boolean
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
export default class ColorDropdown extends Component<Props, State> {
|
||||
public static defaultProps: Partial<Props> = {
|
||||
stretchToFit: false,
|
||||
disabled: false,
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
expanded: false,
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {expanded} = this.state
|
||||
const {selected} = this.props
|
||||
|
||||
return (
|
||||
<ClickOutside onClickOutside={this.handleClickOutside}>
|
||||
<div className={this.dropdownClassNames}>
|
||||
<div
|
||||
className={this.buttonClassNames}
|
||||
onClick={this.handleToggleMenu}
|
||||
>
|
||||
<div
|
||||
className="color-dropdown--swatch"
|
||||
style={{backgroundColor: selected.hex}}
|
||||
/>
|
||||
<div className="color-dropdown--name">{selected.name}</div>
|
||||
<span className="caret" />
|
||||
</div>
|
||||
{expanded && this.renderMenu}
|
||||
</div>
|
||||
</ClickOutside>
|
||||
)
|
||||
}
|
||||
|
||||
private get dropdownClassNames(): string {
|
||||
const {stretchToFit} = this.props
|
||||
const {expanded} = this.state
|
||||
|
||||
return classnames('color-dropdown', {
|
||||
open: expanded,
|
||||
'color-dropdown--stretch': stretchToFit,
|
||||
})
|
||||
}
|
||||
|
||||
private get buttonClassNames(): string {
|
||||
const {disabled} = this.props
|
||||
const {expanded} = this.state
|
||||
|
||||
return classnames('btn btn-sm btn-default color-dropdown--toggle', {
|
||||
active: expanded,
|
||||
'color-dropdown__disabled': disabled,
|
||||
})
|
||||
}
|
||||
|
||||
private get renderMenu(): JSX.Element {
|
||||
const {selected} = this.props
|
||||
|
||||
return (
|
||||
<div className="color-dropdown--menu">
|
||||
<FancyScrollbar
|
||||
autoHide={false}
|
||||
autoHeight={true}
|
||||
maxHeight={DROPDOWN_MENU_MAX_HEIGHT}
|
||||
>
|
||||
{SEVERITY_COLORS.map((color, i) => (
|
||||
<div
|
||||
className={classnames('color-dropdown--item', {
|
||||
active: color.name === selected.name,
|
||||
})}
|
||||
data-tag-key={color.name}
|
||||
data-tag-value={color.hex}
|
||||
key={i}
|
||||
onClick={this.handleColorClick}
|
||||
title={color.name}
|
||||
>
|
||||
<span
|
||||
className="color-dropdown--swatch"
|
||||
style={{backgroundColor: color.hex}}
|
||||
/>
|
||||
<span className="color-dropdown--name">{color.name}</span>
|
||||
</div>
|
||||
))}
|
||||
</FancyScrollbar>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private handleToggleMenu = (): void => {
|
||||
const {disabled} = this.props
|
||||
|
||||
if (disabled) {
|
||||
return
|
||||
}
|
||||
this.setState({expanded: !this.state.expanded})
|
||||
}
|
||||
|
||||
private handleClickOutside = (): void => {
|
||||
this.setState({expanded: false})
|
||||
}
|
||||
|
||||
private handleColorClick = (e: MouseEvent<HTMLElement>): void => {
|
||||
const target = e.target as HTMLElement
|
||||
const hex = target.dataset.tagValue || target.parentElement.dataset.tagValue
|
||||
const nameString =
|
||||
target.dataset.tagKey || target.parentElement.dataset.tagKey
|
||||
const name = SeverityColorOptions[nameString]
|
||||
|
||||
const color: SeverityColor = {name, hex}
|
||||
this.props.onChoose(this.props.severityLevel, color)
|
||||
this.setState({expanded: false})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
Color Dropdown
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$color-dropdown--circle: 14px;
|
||||
$color-dropdown--bar: 104px;
|
||||
$color-dropdown--bar-height: 10px;
|
||||
$color-dropdown--left-padding: 11px;
|
||||
$color-dropdown--name-padding: 20px;
|
||||
|
||||
.color-dropdown {
|
||||
width: 140px;
|
||||
height: 30px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.color-dropdown.color-dropdown--stretch {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.color-dropdown--toggle {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.color-dropdown--toggle span.caret {
|
||||
font-style: normal !important;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 11px;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.color-dropdown--menu {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 0;
|
||||
z-index: 5;
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 5px 0.6px fade-out($g0-obsidian, 0.7);
|
||||
@include gradient-h($g0-obsidian, $g2-kevlar);
|
||||
}
|
||||
.color-dropdown--item {
|
||||
@include no-user-select();
|
||||
width: 100%;
|
||||
height: 28px;
|
||||
position: relative;
|
||||
color: $g11-sidewalk;
|
||||
transition: color 0.25s ease, background-color 0.25s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: $g4-onyx;
|
||||
color: $g18-cloud;
|
||||
}
|
||||
&:hover,
|
||||
&:hover > * {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
&.active {
|
||||
background-color: $g3-castle;
|
||||
color: $g15-platinum;
|
||||
}
|
||||
&:first-child {
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
&:last-child {
|
||||
border-radius: 0 0 4px 4px;
|
||||
}
|
||||
}
|
||||
.color-dropdown--swatch,
|
||||
.color-dropdown--swatches,
|
||||
.color-dropdown--name {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
.color-dropdown--swatch {
|
||||
width: $color-dropdown--circle;
|
||||
height: $color-dropdown--circle;
|
||||
border-radius: 50%;
|
||||
left: $color-dropdown--left-padding;
|
||||
}
|
||||
.color-dropdown--swatches {
|
||||
width: $color-dropdown--bar;
|
||||
height: $color-dropdown--bar-height;
|
||||
border-radius: $color-dropdown--bar-height / 2;
|
||||
left: $color-dropdown--left-padding;
|
||||
}
|
||||
.color-dropdown--name {
|
||||
text-align: left;
|
||||
right: $color-dropdown--name-padding;
|
||||
left: $color-dropdown--circle + $color-dropdown--name-padding;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
text-transform: capitalize;
|
||||
|
||||
.color-dropdown--swatches + & {
|
||||
left: $color-dropdown--bar + $color-dropdown--name-padding;
|
||||
}
|
||||
}
|
||||
.color-dropdown
|
||||
.color-dropdown--menu
|
||||
.fancy-scroll--container
|
||||
.fancy-scroll--track-v
|
||||
.fancy-scroll--thumb-v {
|
||||
@include gradient-v($g9-mountain, $g7-graphite);
|
||||
}
|
||||
.color-dropdown--toggle.color-dropdown__disabled {
|
||||
color: $g7-graphite;
|
||||
font-style: italic;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.color-dropdown--toggle.color-dropdown__disabled > .color-dropdown--swatch {
|
||||
background-color: $g7-graphite !important;
|
||||
}
|
|
@ -22,6 +22,7 @@
|
|||
// Components
|
||||
// TODO: Import these styles into their respective components instead of this stylesheet
|
||||
@import 'src/page_layout/PageLayout';
|
||||
@import 'src/shared/components/color_dropdown/color-dropdown';
|
||||
@import 'src/shared/components/index_views/IndexList';
|
||||
@import 'src/shared/components/dropdown_auto_refresh/AutoRefreshDropdown';
|
||||
@import 'src/shared/components/profile_page/ProfilePage';
|
||||
|
@ -56,3 +57,4 @@
|
|||
@import 'src/logs/containers/logs_page/logs-viewer';
|
||||
@import 'src/logs/components/loading_status/loading-status';
|
||||
@import 'src/logs/components/logs_filter_bar/logs-filter-bar';
|
||||
@import 'src/logs/components/options_overlay/logs-viewer-options';
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import {QueryConfig, Namespace, Source} from 'src/types'
|
||||
|
||||
import {FieldOption} from 'src/types/v2/dashboards'
|
||||
|
||||
export enum SearchStatus {
|
||||
|
@ -44,6 +43,7 @@ export interface LogConfig {
|
|||
isTruncated: boolean
|
||||
}
|
||||
|
||||
// Severity Colors
|
||||
export interface SeverityLevelColor {
|
||||
level: SeverityLevelOptions
|
||||
color: SeverityColorOptions
|
||||
|
@ -60,84 +60,84 @@ export type LogsTableColumn = FieldOption
|
|||
|
||||
// Log Severity
|
||||
export enum SeverityLevelOptions {
|
||||
emerg = 'emerg',
|
||||
alert = 'alert',
|
||||
crit = 'crit',
|
||||
err = 'err',
|
||||
warning = 'warning',
|
||||
notice = 'notice',
|
||||
info = 'info',
|
||||
debug = 'debug',
|
||||
Emerg = 'emerg',
|
||||
Alert = 'alert',
|
||||
Crit = 'crit',
|
||||
Err = 'err',
|
||||
Warning = 'warning',
|
||||
Notice = 'notice',
|
||||
Info = 'info',
|
||||
Debug = 'debug',
|
||||
}
|
||||
|
||||
export enum SeverityFormatOptions {
|
||||
dot = 'dot',
|
||||
dotText = 'dotText',
|
||||
text = 'text',
|
||||
Dot = 'dot',
|
||||
DotText = 'dotText',
|
||||
Text = 'text',
|
||||
}
|
||||
|
||||
export enum SeverityColorOptions {
|
||||
ruby = 'ruby',
|
||||
fire = 'fire',
|
||||
curacao = 'curacao',
|
||||
tiger = 'tiger',
|
||||
pineapple = 'pineapple',
|
||||
thunder = 'thunder',
|
||||
sulfur = 'sulfur',
|
||||
viridian = 'viridian',
|
||||
rainforest = 'rainforest',
|
||||
honeydew = 'honeydew',
|
||||
ocean = 'ocean',
|
||||
pool = 'pool',
|
||||
laser = 'laser',
|
||||
planet = 'planet',
|
||||
star = 'star',
|
||||
comet = 'comet',
|
||||
graphite = 'graphite',
|
||||
wolf = 'wolf',
|
||||
mist = 'mist',
|
||||
pearl = 'pearl',
|
||||
Ruby = 'ruby',
|
||||
Fire = 'fire',
|
||||
Curacao = 'curacao',
|
||||
Tiger = 'tiger',
|
||||
Pineapple = 'pineapple',
|
||||
Thunder = 'thunder',
|
||||
Sulfur = 'sulfur',
|
||||
Viridian = 'viridian',
|
||||
Rainforest = 'rainforest',
|
||||
Honeydew = 'honeydew',
|
||||
Ocean = 'ocean',
|
||||
Pool = 'pool',
|
||||
Laser = 'laser',
|
||||
Planet = 'planet',
|
||||
Star = 'star',
|
||||
Comet = 'comet',
|
||||
Graphite = 'graphite',
|
||||
Wolf = 'wolf',
|
||||
Mist = 'mist',
|
||||
Pearl = 'pearl',
|
||||
}
|
||||
|
||||
export const SeverityColorValues = {
|
||||
[SeverityColorOptions.ruby]: '#BF3D5E',
|
||||
[SeverityColorOptions.fire]: '#DC4E58',
|
||||
[SeverityColorOptions.curacao]: '#F95F53',
|
||||
[SeverityColorOptions.tiger]: '#F48D38',
|
||||
[SeverityColorOptions.pineapple]: '#FFB94A',
|
||||
[SeverityColorOptions.thunder]: '#FFD255',
|
||||
[SeverityColorOptions.sulfur]: '#FFE480',
|
||||
[SeverityColorOptions.viridian]: '#32B08C',
|
||||
[SeverityColorOptions.rainforest]: '#4ED8A0',
|
||||
[SeverityColorOptions.honeydew]: '#7CE490',
|
||||
[SeverityColorOptions.ocean]: '#4591ED',
|
||||
[SeverityColorOptions.pool]: '#22ADF6',
|
||||
[SeverityColorOptions.laser]: '#00C9FF',
|
||||
[SeverityColorOptions.planet]: '#513CC6',
|
||||
[SeverityColorOptions.star]: '#7A65F2',
|
||||
[SeverityColorOptions.comet]: '#9394FF',
|
||||
[SeverityColorOptions.graphite]: '#545667',
|
||||
[SeverityColorOptions.wolf]: '#8E91A1',
|
||||
[SeverityColorOptions.mist]: '#BEC2CC',
|
||||
[SeverityColorOptions.pearl]: '#E7E8EB',
|
||||
[SeverityColorOptions.Ruby]: '#BF3D5E',
|
||||
[SeverityColorOptions.Fire]: '#DC4E58',
|
||||
[SeverityColorOptions.Curacao]: '#F95F53',
|
||||
[SeverityColorOptions.Tiger]: '#F48D38',
|
||||
[SeverityColorOptions.Pineapple]: '#FFB94A',
|
||||
[SeverityColorOptions.Thunder]: '#FFD255',
|
||||
[SeverityColorOptions.Sulfur]: '#FFE480',
|
||||
[SeverityColorOptions.Viridian]: '#32B08C',
|
||||
[SeverityColorOptions.Rainforest]: '#4ED8A0',
|
||||
[SeverityColorOptions.Honeydew]: '#7CE490',
|
||||
[SeverityColorOptions.Ocean]: '#4591ED',
|
||||
[SeverityColorOptions.Pool]: '#22ADF6',
|
||||
[SeverityColorOptions.Laser]: '#00C9FF',
|
||||
[SeverityColorOptions.Planet]: '#513CC6',
|
||||
[SeverityColorOptions.Star]: '#7A65F2',
|
||||
[SeverityColorOptions.Comet]: '#9394FF',
|
||||
[SeverityColorOptions.Graphite]: '#545667',
|
||||
[SeverityColorOptions.Wolf]: '#8E91A1',
|
||||
[SeverityColorOptions.Mist]: '#BEC2CC',
|
||||
[SeverityColorOptions.Pearl]: '#E7E8EB',
|
||||
}
|
||||
|
||||
// Log Column Settings
|
||||
export enum ColumnSettingTypes {
|
||||
visibility = 'visibility',
|
||||
display = 'displayName',
|
||||
label = 'label',
|
||||
color = 'color',
|
||||
Visibility = 'visibility',
|
||||
Display = 'displayName',
|
||||
Label = 'label',
|
||||
Color = 'color',
|
||||
}
|
||||
|
||||
export enum ColumnSettingLabelOptions {
|
||||
text = 'text',
|
||||
icon = 'icon',
|
||||
Text = 'text',
|
||||
Icon = 'icon',
|
||||
}
|
||||
|
||||
export enum ColumnSettingVisibilityOptions {
|
||||
visible = 'visible',
|
||||
hidden = 'hidden',
|
||||
Visible = 'visible',
|
||||
Hidden = 'hidden',
|
||||
}
|
||||
|
||||
// Time
|
||||
|
@ -174,29 +174,29 @@ export interface TermRule {
|
|||
}
|
||||
|
||||
export enum TermType {
|
||||
EXCLUDE,
|
||||
INCLUDE,
|
||||
Exclude,
|
||||
Include,
|
||||
}
|
||||
|
||||
export enum TermPart {
|
||||
EXCLUSION = '-',
|
||||
SINGLE_QUOTED = "'([^']+)'",
|
||||
DOUBLE_QUOTED = '"([^"]+)"',
|
||||
ATTRIBUTE = '(\\w+(?=\\:))',
|
||||
COLON = '(?::)',
|
||||
UNQUOTED_WORD = '([\\S]+)',
|
||||
Exclusion = '-',
|
||||
SingleQuoted = "'([^']+)'",
|
||||
DoubleQuoted = '"([^"]+)"',
|
||||
Attribute = '(\\w+(?=\\:))',
|
||||
Colon = '(?::)',
|
||||
UnquotedWord = '([\\S]+)',
|
||||
}
|
||||
|
||||
export enum Operator {
|
||||
NOT_LIKE = '!~',
|
||||
LIKE = '=~',
|
||||
EQUAL = '==',
|
||||
NOT_EQUAL = '!=',
|
||||
NotLike = '!~',
|
||||
Like = '=~',
|
||||
Equal = '==',
|
||||
NotEqual = '!=',
|
||||
}
|
||||
|
||||
export enum MatchType {
|
||||
NONE = 'no-match',
|
||||
MATCH = 'match',
|
||||
None = 'no-match',
|
||||
Match = 'match',
|
||||
}
|
||||
|
||||
export interface MatchSection {
|
||||
|
|
Loading…
Reference in New Issue