Merge pull request #3543 from influxdata/log-viewer/basic-search
Implements basic log viewer searchpull/10616/head
commit
03f7d277ba
|
@ -2,7 +2,11 @@ import _ from 'lodash'
|
||||||
import {Source, Namespace, TimeRange, QueryConfig} from 'src/types'
|
import {Source, Namespace, TimeRange, QueryConfig} from 'src/types'
|
||||||
import {getSource} from 'src/shared/apis'
|
import {getSource} from 'src/shared/apis'
|
||||||
import {getDatabasesWithRetentionPolicies} from 'src/shared/apis/databases'
|
import {getDatabasesWithRetentionPolicies} from 'src/shared/apis/databases'
|
||||||
import {buildHistogramQueryConfig, buildTableQueryConfig} from 'src/logs/utils'
|
import {
|
||||||
|
buildHistogramQueryConfig,
|
||||||
|
buildTableQueryConfig,
|
||||||
|
buildLogQuery,
|
||||||
|
} from 'src/logs/utils'
|
||||||
import {getDeep} from 'src/utils/wrappers'
|
import {getDeep} from 'src/utils/wrappers'
|
||||||
import buildQuery from 'src/utils/influxql'
|
import buildQuery from 'src/utils/influxql'
|
||||||
import {executeQueryAsync} from 'src/logs/api'
|
import {executeQueryAsync} from 'src/logs/api'
|
||||||
|
@ -42,6 +46,7 @@ export enum ActionTypes {
|
||||||
SetTableQueryConfig = 'LOGS_SET_TABLE_QUERY_CONFIG',
|
SetTableQueryConfig = 'LOGS_SET_TABLE_QUERY_CONFIG',
|
||||||
SetTableData = 'LOGS_SET_TABLE_DATA',
|
SetTableData = 'LOGS_SET_TABLE_DATA',
|
||||||
ChangeZoom = 'LOGS_CHANGE_ZOOM',
|
ChangeZoom = 'LOGS_CHANGE_ZOOM',
|
||||||
|
SetSearchTerm = 'LOGS_SET_SEARCH_TERM',
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SetSourceAction {
|
interface SetSourceAction {
|
||||||
|
@ -100,6 +105,13 @@ interface SetTableData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SetSearchTerm {
|
||||||
|
type: ActionTypes.SetSearchTerm
|
||||||
|
payload: {
|
||||||
|
searchTerm: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
interface ChangeZoomAction {
|
interface ChangeZoomAction {
|
||||||
type: ActionTypes.ChangeZoom
|
type: ActionTypes.ChangeZoom
|
||||||
payload: {
|
payload: {
|
||||||
|
@ -118,6 +130,7 @@ export type Action =
|
||||||
| ChangeZoomAction
|
| ChangeZoomAction
|
||||||
| SetTableData
|
| SetTableData
|
||||||
| SetTableQueryConfig
|
| SetTableQueryConfig
|
||||||
|
| SetSearchTerm
|
||||||
|
|
||||||
const getTimeRange = (state: State): TimeRange | null =>
|
const getTimeRange = (state: State): TimeRange | null =>
|
||||||
getDeep<TimeRange | null>(state, 'logs.timeRange', null)
|
getDeep<TimeRange | null>(state, 'logs.timeRange', null)
|
||||||
|
@ -134,6 +147,9 @@ const getHistogramQueryConfig = (state: State): QueryConfig | null =>
|
||||||
const getTableQueryConfig = (state: State): QueryConfig | null =>
|
const getTableQueryConfig = (state: State): QueryConfig | null =>
|
||||||
getDeep<QueryConfig | null>(state, 'logs.tableQueryConfig', null)
|
getDeep<QueryConfig | null>(state, 'logs.tableQueryConfig', null)
|
||||||
|
|
||||||
|
const getSearchTerm = (state: State): string | null =>
|
||||||
|
getDeep<string | null>(state, 'logs.searchTerm', null)
|
||||||
|
|
||||||
export const setSource = (source: Source): SetSourceAction => ({
|
export const setSource = (source: Source): SetSourceAction => ({
|
||||||
type: ActionTypes.SetSource,
|
type: ActionTypes.SetSource,
|
||||||
payload: {source},
|
payload: {source},
|
||||||
|
@ -154,9 +170,10 @@ export const executeHistogramQueryAsync = () => async (
|
||||||
const timeRange = getTimeRange(state)
|
const timeRange = getTimeRange(state)
|
||||||
const namespace = getNamespace(state)
|
const namespace = getNamespace(state)
|
||||||
const proxyLink = getProxyLink(state)
|
const proxyLink = getProxyLink(state)
|
||||||
|
const searchTerm = getSearchTerm(state)
|
||||||
|
|
||||||
if (_.every([queryConfig, timeRange, namespace, proxyLink])) {
|
if (_.every([queryConfig, timeRange, namespace, proxyLink])) {
|
||||||
const query = buildQuery(timeRange, queryConfig)
|
const query = buildLogQuery(timeRange, queryConfig, searchTerm)
|
||||||
const response = await executeQueryAsync(proxyLink, namespace, query)
|
const response = await executeQueryAsync(proxyLink, namespace, query)
|
||||||
|
|
||||||
dispatch(setHistogramData(response))
|
dispatch(setHistogramData(response))
|
||||||
|
@ -178,9 +195,10 @@ export const executeTableQueryAsync = () => async (
|
||||||
const timeRange = getTimeRange(state)
|
const timeRange = getTimeRange(state)
|
||||||
const namespace = getNamespace(state)
|
const namespace = getNamespace(state)
|
||||||
const proxyLink = getProxyLink(state)
|
const proxyLink = getProxyLink(state)
|
||||||
|
const searchTerm = getSearchTerm(state)
|
||||||
|
|
||||||
if (_.every([queryConfig, timeRange, namespace, proxyLink])) {
|
if (_.every([queryConfig, timeRange, namespace, proxyLink])) {
|
||||||
const query = buildQuery(timeRange, queryConfig)
|
const query = buildLogQuery(timeRange, queryConfig, searchTerm)
|
||||||
const response = await executeQueryAsync(proxyLink, namespace, query)
|
const response = await executeQueryAsync(proxyLink, namespace, query)
|
||||||
|
|
||||||
const series = getDeep(response, 'results.0.series.0', defaultTableData)
|
const series = getDeep(response, 'results.0.series.0', defaultTableData)
|
||||||
|
@ -194,6 +212,14 @@ export const executeQueriesAsync = () => async dispatch => {
|
||||||
dispatch(executeTableQueryAsync())
|
dispatch(executeTableQueryAsync())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const setSearchTermAsync = (searchTerm: string) => async dispatch => {
|
||||||
|
dispatch({
|
||||||
|
type: ActionTypes.SetSearchTerm,
|
||||||
|
payload: {searchTerm},
|
||||||
|
})
|
||||||
|
dispatch(executeQueriesAsync())
|
||||||
|
}
|
||||||
|
|
||||||
export const setHistogramQueryConfigAsync = () => async (
|
export const setHistogramQueryConfigAsync = () => async (
|
||||||
dispatch,
|
dispatch,
|
||||||
getState: GetState
|
getState: GetState
|
||||||
|
|
|
@ -2,13 +2,24 @@ import React, {PureComponent, ChangeEvent, KeyboardEvent} from 'react'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
searchString: string
|
searchString: string
|
||||||
onChange: (e: ChangeEvent<HTMLInputElement>) => void
|
onSearch: (value: string) => void
|
||||||
onSearch: () => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class LogsSearchBar extends PureComponent<Props> {
|
interface State {
|
||||||
|
searchTerm: string
|
||||||
|
}
|
||||||
|
|
||||||
|
class LogsSearchBar extends PureComponent<Props, State> {
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
searchTerm: props.searchString,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {searchString, onSearch, onChange} = this.props
|
const {searchTerm} = this.state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="logs-viewer--search-bar">
|
<div className="logs-viewer--search-bar">
|
||||||
|
@ -16,8 +27,8 @@ class LogsSearchBar extends PureComponent<Props> {
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search logs using Keywords or Regular Expressions..."
|
placeholder="Search logs using Keywords or Regular Expressions..."
|
||||||
value={searchString}
|
value={searchTerm}
|
||||||
onChange={onChange}
|
onChange={this.handleChange}
|
||||||
onKeyDown={this.handleInputKeyDown}
|
onKeyDown={this.handleInputKeyDown}
|
||||||
className="form-control input-sm"
|
className="form-control input-sm"
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
|
@ -25,7 +36,7 @@ class LogsSearchBar extends PureComponent<Props> {
|
||||||
/>
|
/>
|
||||||
<span className="icon search" />
|
<span className="icon search" />
|
||||||
</div>
|
</div>
|
||||||
<button className="btn btn-sm btn-primary" onClick={onSearch}>
|
<button className="btn btn-sm btn-primary" onClick={this.handleSearch}>
|
||||||
<span className="icon search" />
|
<span className="icon search" />
|
||||||
Search
|
Search
|
||||||
</button>
|
</button>
|
||||||
|
@ -33,11 +44,19 @@ class LogsSearchBar extends PureComponent<Props> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handleSearch = () => {
|
||||||
|
this.props.onSearch(this.state.searchTerm)
|
||||||
|
}
|
||||||
|
|
||||||
private handleInputKeyDown = (e: KeyboardEvent<HTMLInputElement>): void => {
|
private handleInputKeyDown = (e: KeyboardEvent<HTMLInputElement>): void => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
return this.props.onSearch()
|
return this.handleSearch()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handleChange = (e: ChangeEvent<HTMLInputElement>): void => {
|
||||||
|
this.setState({searchTerm: e.target.value})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LogsSearchBar
|
export default LogsSearchBar
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import _ from 'lodash'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import React, {PureComponent} from 'react'
|
import React, {PureComponent} from 'react'
|
||||||
import {Grid, AutoSizer} from 'react-virtualized'
|
import {Grid, AutoSizer} from 'react-virtualized'
|
||||||
|
@ -11,34 +12,18 @@ interface Props {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const FACILITY_CODES = [
|
interface State {
|
||||||
'kern',
|
scrollLeft: number
|
||||||
'user',
|
}
|
||||||
'mail',
|
|
||||||
'daemon',
|
|
||||||
'auth',
|
|
||||||
'syslog',
|
|
||||||
'lpr',
|
|
||||||
'news',
|
|
||||||
'uucp',
|
|
||||||
'clock',
|
|
||||||
'authpriv',
|
|
||||||
'ftp',
|
|
||||||
'NTP',
|
|
||||||
'log audit',
|
|
||||||
'log alert',
|
|
||||||
'cron',
|
|
||||||
'local0',
|
|
||||||
'local1',
|
|
||||||
'local2',
|
|
||||||
'local3',
|
|
||||||
'local4',
|
|
||||||
'local5',
|
|
||||||
'local6',
|
|
||||||
'local7',
|
|
||||||
]
|
|
||||||
|
|
||||||
class LogsTable extends PureComponent<Props> {
|
class LogsTable extends PureComponent<Props, State> {
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
scrollLeft: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
public render() {
|
public render() {
|
||||||
const rowCount = getDeep(this.props, 'data.values.length', 0)
|
const rowCount = getDeep(this.props, 'data.values.length', 0)
|
||||||
const columnCount = getDeep(this.props, 'data.columns.length', 1) - 1
|
const columnCount = getDeep(this.props, 'data.columns.length', 1) - 1
|
||||||
|
@ -52,6 +37,8 @@ class LogsTable extends PureComponent<Props> {
|
||||||
rowHeight={40}
|
rowHeight={40}
|
||||||
rowCount={1}
|
rowCount={1}
|
||||||
width={width}
|
width={width}
|
||||||
|
scrollLeft={this.state.scrollLeft}
|
||||||
|
onScroll={this.handleScroll}
|
||||||
cellRenderer={this.headerRenderer}
|
cellRenderer={this.headerRenderer}
|
||||||
columnCount={columnCount}
|
columnCount={columnCount}
|
||||||
columnWidth={this.getColumnWidth}
|
columnWidth={this.getColumnWidth}
|
||||||
|
@ -69,6 +56,8 @@ class LogsTable extends PureComponent<Props> {
|
||||||
rowHeight={40}
|
rowHeight={40}
|
||||||
rowCount={rowCount}
|
rowCount={rowCount}
|
||||||
width={width}
|
width={width}
|
||||||
|
scrollLeft={this.state.scrollLeft}
|
||||||
|
onScroll={this.handleScroll}
|
||||||
cellRenderer={this.cellRenderer}
|
cellRenderer={this.cellRenderer}
|
||||||
columnCount={columnCount}
|
columnCount={columnCount}
|
||||||
columnWidth={this.getColumnWidth}
|
columnWidth={this.getColumnWidth}
|
||||||
|
@ -80,24 +69,26 @@ class LogsTable extends PureComponent<Props> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private severityLevel(value: number): string {
|
private handleScroll = scrollInfo => {
|
||||||
|
const {scrollLeft} = scrollInfo
|
||||||
|
|
||||||
|
this.setState({scrollLeft})
|
||||||
|
}
|
||||||
|
|
||||||
|
private severityLevel(value: string): string {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case 0:
|
case 'emerg':
|
||||||
return 'Emergency'
|
return 'Emergency'
|
||||||
case 1:
|
case 'alert':
|
||||||
return 'Alert'
|
return 'Alert'
|
||||||
case 2:
|
case 'crit':
|
||||||
return 'Critical'
|
return 'Critical'
|
||||||
case 3:
|
case 'err':
|
||||||
return 'Error'
|
return 'Error'
|
||||||
case 4:
|
case 'info':
|
||||||
return 'Warning'
|
|
||||||
case 5:
|
|
||||||
return 'Notice'
|
|
||||||
case 6:
|
|
||||||
return 'Informational'
|
return 'Informational'
|
||||||
default:
|
default:
|
||||||
return 'Debug'
|
return _.capitalize(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,9 +97,17 @@ class LogsTable extends PureComponent<Props> {
|
||||||
|
|
||||||
switch (column) {
|
switch (column) {
|
||||||
case 'message':
|
case 'message':
|
||||||
return 700
|
return 900
|
||||||
case 'timestamp':
|
case 'timestamp':
|
||||||
return 400
|
return 200
|
||||||
|
case 'procid':
|
||||||
|
return 100
|
||||||
|
case 'facility':
|
||||||
|
return 150
|
||||||
|
case 'severity_1':
|
||||||
|
return 150
|
||||||
|
case 'severity':
|
||||||
|
return 24
|
||||||
default:
|
default:
|
||||||
return 200
|
return 200
|
||||||
}
|
}
|
||||||
|
@ -118,20 +117,17 @@ class LogsTable extends PureComponent<Props> {
|
||||||
return getDeep<string>(
|
return getDeep<string>(
|
||||||
{
|
{
|
||||||
timestamp: 'Timestamp',
|
timestamp: 'Timestamp',
|
||||||
facility_code: 'Facility',
|
|
||||||
procid: 'Proc ID',
|
procid: 'Proc ID',
|
||||||
severity_code: 'Severity',
|
|
||||||
message: 'Message',
|
message: 'Message',
|
||||||
|
appname: 'Application',
|
||||||
|
severity: '',
|
||||||
|
severity_1: 'Severity',
|
||||||
},
|
},
|
||||||
key,
|
key,
|
||||||
''
|
_.capitalize(key)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private facility(key: number): string {
|
|
||||||
return getDeep<string>(FACILITY_CODES, key, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
private headerRenderer = ({key, style, columnIndex}) => {
|
private headerRenderer = ({key, style, columnIndex}) => {
|
||||||
const value = getDeep<string>(
|
const value = getDeep<string>(
|
||||||
this.props,
|
this.props,
|
||||||
|
@ -159,12 +155,18 @@ class LogsTable extends PureComponent<Props> {
|
||||||
case 'timestamp':
|
case 'timestamp':
|
||||||
value = moment(+value / 1000000).format('YYYY/MM/DD HH:mm:ss')
|
value = moment(+value / 1000000).format('YYYY/MM/DD HH:mm:ss')
|
||||||
break
|
break
|
||||||
case 'severity_code':
|
case 'severity_1':
|
||||||
value = this.severityLevel(+value)
|
value = this.severityLevel(value)
|
||||||
break
|
|
||||||
case 'facility_code':
|
|
||||||
value = this.facility(+value)
|
|
||||||
break
|
break
|
||||||
|
case 'severity':
|
||||||
|
return (
|
||||||
|
<div style={style} key={key}>
|
||||||
|
<div
|
||||||
|
className={`logs-viewer--dot ${value}-severity`}
|
||||||
|
title={this.severityLevel(value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, {PureComponent, ChangeEvent} from 'react'
|
import React, {PureComponent} from 'react'
|
||||||
import {connect} from 'react-redux'
|
import {connect} from 'react-redux'
|
||||||
import {
|
import {
|
||||||
getSourceAndPopulateNamespacesAsync,
|
getSourceAndPopulateNamespacesAsync,
|
||||||
|
@ -6,6 +6,7 @@ import {
|
||||||
setNamespaceAsync,
|
setNamespaceAsync,
|
||||||
executeQueriesAsync,
|
executeQueriesAsync,
|
||||||
changeZoomAsync,
|
changeZoomAsync,
|
||||||
|
setSearchTermAsync,
|
||||||
} from 'src/logs/actions'
|
} from 'src/logs/actions'
|
||||||
import {getSourcesAsync} from 'src/shared/actions/sources'
|
import {getSourcesAsync} from 'src/shared/actions/sources'
|
||||||
import LogViewerHeader from 'src/logs/components/LogViewerHeader'
|
import LogViewerHeader from 'src/logs/components/LogViewerHeader'
|
||||||
|
@ -37,12 +38,14 @@ interface Props {
|
||||||
setNamespaceAsync: (namespace: Namespace) => void
|
setNamespaceAsync: (namespace: Namespace) => void
|
||||||
changeZoomAsync: (timeRange: TimeRange) => void
|
changeZoomAsync: (timeRange: TimeRange) => void
|
||||||
executeQueriesAsync: () => void
|
executeQueriesAsync: () => void
|
||||||
|
setSearchTermAsync: (searchTerm: string) => void
|
||||||
timeRange: TimeRange
|
timeRange: TimeRange
|
||||||
histogramData: object[]
|
histogramData: object[]
|
||||||
tableData: {
|
tableData: {
|
||||||
columns: string[]
|
columns: string[]
|
||||||
values: string[]
|
values: string[]
|
||||||
}
|
}
|
||||||
|
searchTerm: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
|
@ -85,7 +88,8 @@ class LogsPage extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {searchString, filters} = this.state
|
const {filters} = this.state
|
||||||
|
const {searchTerm} = this.props
|
||||||
|
|
||||||
const count = getDeep(this.props, 'tableData.values.length', 0)
|
const count = getDeep(this.props, 'tableData.values.length', 0)
|
||||||
|
|
||||||
|
@ -95,8 +99,7 @@ class LogsPage extends PureComponent<Props, State> {
|
||||||
<div className="page-contents logs-viewer">
|
<div className="page-contents logs-viewer">
|
||||||
<Graph>{this.chart}</Graph>
|
<Graph>{this.chart}</Graph>
|
||||||
<SearchBar
|
<SearchBar
|
||||||
searchString={searchString}
|
searchString={searchTerm}
|
||||||
onChange={this.handleSearchInputChange}
|
|
||||||
onSearch={this.handleSubmitSearch}
|
onSearch={this.handleSubmitSearch}
|
||||||
/>
|
/>
|
||||||
<FilterBar
|
<FilterBar
|
||||||
|
@ -144,14 +147,8 @@ class LogsPage extends PureComponent<Props, State> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleSearchInputChange = (
|
private handleSubmitSearch = (value: string): void => {
|
||||||
e: ChangeEvent<HTMLInputElement>
|
this.props.setSearchTermAsync(value)
|
||||||
): void => {
|
|
||||||
this.setState({searchString: e.target.value})
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleSubmitSearch = (): void => {
|
|
||||||
// do the thing
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleUpdateFilters = (filters: Filter[]): void => {
|
private handleUpdateFilters = (filters: Filter[]): void => {
|
||||||
|
@ -187,6 +184,7 @@ const mapStateToProps = ({
|
||||||
currentNamespace,
|
currentNamespace,
|
||||||
histogramData,
|
histogramData,
|
||||||
tableData,
|
tableData,
|
||||||
|
searchTerm,
|
||||||
},
|
},
|
||||||
}) => ({
|
}) => ({
|
||||||
sources,
|
sources,
|
||||||
|
@ -196,6 +194,7 @@ const mapStateToProps = ({
|
||||||
currentNamespace,
|
currentNamespace,
|
||||||
histogramData,
|
histogramData,
|
||||||
tableData,
|
tableData,
|
||||||
|
searchTerm,
|
||||||
})
|
})
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
|
@ -205,6 +204,7 @@ const mapDispatchToProps = {
|
||||||
setNamespaceAsync,
|
setNamespaceAsync,
|
||||||
executeQueriesAsync,
|
executeQueriesAsync,
|
||||||
changeZoomAsync,
|
changeZoomAsync,
|
||||||
|
setSearchTermAsync,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(LogsPage)
|
export default connect(mapStateToProps, mapDispatchToProps)(LogsPage)
|
||||||
|
|
|
@ -10,6 +10,7 @@ const defaultState: LogsState = {
|
||||||
tableQueryConfig: null,
|
tableQueryConfig: null,
|
||||||
tableData: [],
|
tableData: [],
|
||||||
histogramData: [],
|
histogramData: [],
|
||||||
|
searchTerm: null,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (state: LogsState = defaultState, action: Action) => {
|
export default (state: LogsState = defaultState, action: Action) => {
|
||||||
|
@ -33,6 +34,9 @@ export default (state: LogsState = defaultState, action: Action) => {
|
||||||
case ActionTypes.ChangeZoom:
|
case ActionTypes.ChangeZoom:
|
||||||
const {timeRange, data} = action.payload
|
const {timeRange, data} = action.payload
|
||||||
return {...state, timeRange, histogramData: data}
|
return {...state, timeRange, histogramData: data}
|
||||||
|
case ActionTypes.SetSearchTerm:
|
||||||
|
const {searchTerm} = action.payload
|
||||||
|
return {...state, searchTerm}
|
||||||
default:
|
default:
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,15 @@
|
||||||
|
import _ from 'lodash'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import uuid from 'uuid'
|
import uuid from 'uuid'
|
||||||
import {TimeRange, Namespace, QueryConfig} from 'src/types'
|
import {TimeRange, Namespace, QueryConfig} from 'src/types'
|
||||||
|
import {NULL_STRING} from 'src/shared/constants/queryFillOptions'
|
||||||
|
import {
|
||||||
|
quoteIfTimestamp,
|
||||||
|
buildSelect,
|
||||||
|
buildWhereClause,
|
||||||
|
buildGroupBy,
|
||||||
|
buildFill,
|
||||||
|
} from 'src/utils/influxql'
|
||||||
|
|
||||||
const BIN_COUNT = 30
|
const BIN_COUNT = 30
|
||||||
|
|
||||||
|
@ -9,7 +18,7 @@ const histogramFields = [
|
||||||
alias: '',
|
alias: '',
|
||||||
args: [
|
args: [
|
||||||
{
|
{
|
||||||
alias: '',
|
alias: 'message',
|
||||||
type: 'field',
|
type: 'field',
|
||||||
value: 'message',
|
value: 'message',
|
||||||
},
|
},
|
||||||
|
@ -20,15 +29,25 @@ const histogramFields = [
|
||||||
]
|
]
|
||||||
|
|
||||||
const tableFields = [
|
const tableFields = [
|
||||||
|
{
|
||||||
|
alias: 'severity',
|
||||||
|
type: 'field',
|
||||||
|
value: 'severity',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
alias: 'timestamp',
|
alias: 'timestamp',
|
||||||
type: 'field',
|
type: 'field',
|
||||||
value: 'timestamp',
|
value: 'timestamp',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
alias: 'facility_code',
|
alias: 'severity_text',
|
||||||
type: 'field',
|
type: 'field',
|
||||||
value: 'facility_code',
|
value: 'severity',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
alias: 'facility',
|
||||||
|
type: 'field',
|
||||||
|
value: 'facility',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
alias: 'procid',
|
alias: 'procid',
|
||||||
|
@ -36,9 +55,14 @@ const tableFields = [
|
||||||
value: 'procid',
|
value: 'procid',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
alias: 'severity_code',
|
alias: 'appname',
|
||||||
type: 'field',
|
type: 'field',
|
||||||
value: 'severity_code',
|
value: 'appname',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
alias: 'host',
|
||||||
|
type: 'field',
|
||||||
|
value: 'host',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
alias: 'message',
|
alias: 'message',
|
||||||
|
@ -56,6 +80,25 @@ const defaultQueryConfig = {
|
||||||
tags: {},
|
tags: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildLogQuery(
|
||||||
|
timeRange: TimeRange,
|
||||||
|
config: QueryConfig,
|
||||||
|
searchTerm: string | null = null
|
||||||
|
): string {
|
||||||
|
const {groupBy, fill = NULL_STRING, tags, areTagsAccepted} = config
|
||||||
|
const {upper, lower} = quoteIfTimestamp(timeRange)
|
||||||
|
const select = buildSelect(config, '')
|
||||||
|
const dimensions = buildGroupBy(groupBy)
|
||||||
|
const fillClause = groupBy.time ? buildFill(fill) : ''
|
||||||
|
|
||||||
|
let condition = buildWhereClause({lower, upper, tags, areTagsAccepted})
|
||||||
|
if (!_.isEmpty(searchTerm)) {
|
||||||
|
condition = `${condition} AND message =~ ${new RegExp(searchTerm)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${select}${condition}${dimensions}${fillClause}`
|
||||||
|
}
|
||||||
|
|
||||||
const computeSeconds = (range: TimeRange) => {
|
const computeSeconds = (range: TimeRange) => {
|
||||||
const {upper, lower, seconds} = range
|
const {upper, lower, seconds} = range
|
||||||
|
|
||||||
|
|
|
@ -201,4 +201,37 @@ $logs-viewer-gutter: 60px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logs-viewer--dot {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-block;
|
||||||
|
background-color: $g0-obsidian;
|
||||||
|
|
||||||
|
&.emerg-severity {
|
||||||
|
@include gradient-diag-up($c-ruby, $c-fire);
|
||||||
|
}
|
||||||
|
&.alert-severity {
|
||||||
|
@include gradient-diag-up($c-fire, $c-curacao);
|
||||||
|
}
|
||||||
|
&.crit-severity {
|
||||||
|
@include gradient-diag-up($c-curacao, $c-tiger);
|
||||||
|
}
|
||||||
|
&.err-severity {
|
||||||
|
@include gradient-diag-up($c-tiger, $c-pineapple);
|
||||||
|
}
|
||||||
|
&.warning-severity {
|
||||||
|
@include gradient-diag-up($c-pineapple, $c-thunder);
|
||||||
|
}
|
||||||
|
&.notice-severity {
|
||||||
|
@include gradient-diag-up($c-rainforest, $c-honeydew);
|
||||||
|
}
|
||||||
|
&.info-severity {
|
||||||
|
@include gradient-diag-up($c-star, $c-comet);
|
||||||
|
}
|
||||||
|
&.debug-severity {
|
||||||
|
@include gradient-diag-up($g5-pepper, $g6-smoke);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -9,6 +9,7 @@ export interface LogsState {
|
||||||
histogramData: object[]
|
histogramData: object[]
|
||||||
tableQueryConfig: QueryConfig | null
|
tableQueryConfig: QueryConfig | null
|
||||||
tableData: object[]
|
tableData: object[]
|
||||||
|
searchTerm: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LocalStorage {
|
export interface LocalStorage {
|
||||||
|
|
|
@ -42,7 +42,7 @@ export default function buildInfluxQLQuery(
|
||||||
return `${select}${condition}${dimensions}${fillClause}`
|
return `${select}${condition}${dimensions}${fillClause}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildSelect(
|
export function buildSelect(
|
||||||
{fields, database, retentionPolicy, measurement}: QueryConfig,
|
{fields, database, retentionPolicy, measurement}: QueryConfig,
|
||||||
shift: string | null = null
|
shift: string | null = null
|
||||||
): string {
|
): string {
|
||||||
|
@ -122,7 +122,7 @@ function buildFields(fieldFuncs: Field[], shift = ''): string {
|
||||||
.join(', ')
|
.join(', ')
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildWhereClause({
|
export function buildWhereClause({
|
||||||
lower,
|
lower,
|
||||||
upper,
|
upper,
|
||||||
tags,
|
tags,
|
||||||
|
@ -163,7 +163,7 @@ function buildWhereClause({
|
||||||
return ` WHERE ${subClauses.join(' AND ')}`
|
return ` WHERE ${subClauses.join(' AND ')}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildGroupBy(groupBy: GroupBy): string {
|
export function buildGroupBy(groupBy: GroupBy): string {
|
||||||
return `${buildGroupByTime(groupBy)}${buildGroupByTags(groupBy)}`
|
return `${buildGroupByTime(groupBy)}${buildGroupByTags(groupBy)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,7 +191,7 @@ function buildGroupByTags(groupBy: GroupBy): string {
|
||||||
return ` GROUP BY ${tags}`
|
return ` GROUP BY ${tags}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildFill(fill: string): string {
|
export function buildFill(fill: string): string {
|
||||||
return ` FILL(${fill})`
|
return ` FILL(${fill})`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue