Skip to content

Commit

Permalink
add log filtering
Browse files Browse the repository at this point in the history
  • Loading branch information
matt-deboer committed Oct 31, 2017
1 parent a45696b commit 546c07b
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 31 deletions.
89 changes: 81 additions & 8 deletions pkg/ui/src/components/LogViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ import React from 'react'
import PropTypes from 'prop-types'
import { grey100, grey300, grey500, grey900, grey800} from 'material-ui/styles/colors'
import {Toolbar, ToolbarGroup} from 'material-ui/Toolbar'
import Checkbox from 'material-ui/Checkbox'
import Menu from 'material-ui/Menu'
import MenuItem from 'material-ui/MenuItem'
import IconExpand from 'material-ui/svg-icons/navigation/more-vert'
import IconChevronRight from 'material-ui/svg-icons/navigation/chevron-right'
import IconChecked from 'material-ui/svg-icons/toggle/check-box'
import IconUnchecked from 'material-ui/svg-icons/toggle/check-box-outline-blank'
import IconSearch from 'material-ui/svg-icons/action/search'
import FlatButton from 'material-ui/FlatButton'
import TextField from 'material-ui/TextField'
import Popover from 'material-ui/Popover'
import { connect } from 'react-redux'
import { selectLogsFor } from '../state/actions/logs'
Expand All @@ -17,7 +20,6 @@ import XTerm from './xterm/XTerm'
import lcs from 'longest-common-subsequence'
import './LogViewer.scss'


const mapStateToProps = function(store) {

return {
Expand All @@ -38,6 +40,18 @@ const checkedIcon = <IconChecked style={{height: 18, width: 18, fill: grey300}}/
const uncheckedIcon = <IconUnchecked style={{height: 18, width: 18, fill: grey300}}/>

const styles = {
checkbox: {
width: '10%',
marginLeft: 15,
},
checkboxLabel: {
marginLeft: -15,
color: grey500,
},
checkboxIcon: {
color: grey500,
fill: grey500,
},
label: {
paddingLeft: 16,
lineHeight: '24px',
Expand Down Expand Up @@ -96,6 +110,7 @@ class LogViewer extends React.Component {
podsOpen: false,
containersOpen: false,
selectedContainers: props.selectedContainers,
filterErrorText: null,
}
this.logs = props.logs
}
Expand All @@ -113,7 +128,6 @@ class LogViewer extends React.Component {
let {props} = this
if (value.length > 0) {
props.selectLogsFor(value)
console.log(`menu changed => ${value}`)
}
}

Expand All @@ -123,6 +137,44 @@ class LogViewer extends React.Component {
this.setState(state)
}

handleFilterKeydown = (event) => {
let keyCode = ('which' in event) ? event.which : event.keyCode
if (keyCode === 13) {
let result
if (event.shiftKey) {
result = this.term.getXTerm().findPrevious(this.filterLogsInput.input.value)
} else {
result = this.term.getXTerm().findNext(this.filterLogsInput.input.value)
}
if (!result) {
this.setState({
filterErrorText: 'not found',
})
}
} else if (this.state.filterErrorText) {
this.setState({
filterErrorText: null,
})
}
}

toggleFilter = () => {
this.setState({
filterChecked: !this.state.filterChecked,
})
}

pushLogs = (e) => {
if (!this.state.filterChecked
|| (this.filterLogsInput && this.filterLogsInput.input.value
&& e.toLowerCase().indexOf(this.filterLogsInput.input.value) != -1)
) {

this.term.writeln(e)
}
return true
}

componentDidUpdate = () => {
this.term && this.term.fit()
}
Expand All @@ -132,7 +184,7 @@ class LogViewer extends React.Component {
let { props } = this
return (
<div style={{paddingBottom: 10, backgroundColor: grey900}}>
<Toolbar style={{height: '36px', padding: 6, backgroundColor: grey900, margin: 0}}>
<Toolbar style={{height: '36px', padding: '6px 10px', backgroundColor: grey900, margin: 0}}>
{this.renderContainerMenu()}
</Toolbar>
<XTerm
Expand Down Expand Up @@ -163,10 +215,7 @@ class LogViewer extends React.Component {
this.term.writeln(line)
}
this.logs.length = 0
this.logs.onPush = function(e) {
ref.writeln(e)
return true
}
this.logs.onPush = this.pushLogs
ref.fit()
}
}}
Expand Down Expand Up @@ -217,7 +266,7 @@ class LogViewer extends React.Component {
}

return (
<ToolbarGroup style={{width: '100%'}}>
<ToolbarGroup style={{width: '100%'}} className={'log-controls'}>
<span style={styles.label}>following container(s)</span>
<IconChevronRight style={{height: 22, width: 22, fill: grey500}}/>
<FlatButton
Expand Down Expand Up @@ -253,6 +302,30 @@ class LogViewer extends React.Component {
{menuItems}
</Menu>
</Popover>
<IconSearch style={{color: grey500, height: 40, width: 40, marginRight: 10}}/>
<TextField
id={'filter-logs'}
style={{height: 24, marginTop: 0, width: '35%', }}
inputStyle={{color: grey100, fontSize: 15, backgroundColor: grey800,}}
ref={(ref) => {
if (ref) {
this.filterLogsInput=ref
this.filterLogsInput.input.onkeydown = this.handleFilterKeydown
}
}}
errorText={this.state.filterErrorText}
underlineStyle={{bottom: 2, borderWidth: 0}}
underlineFocusStyle={{bottom: 2, borderWidth: 2}}

/>
<Checkbox
label="filter"
checked={this.state.filterChecked}
onCheck={this.toggleFilter}
style={styles.checkbox}
labelStyle={styles.checkboxLabel}
iconStyle={styles.checkboxIcon}
/>
</ToolbarGroup>
)
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/ui/src/components/xterm/XTerm.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import className from 'classnames'

import './XTerm.css'

xterm.loadAddon('search', function(addon) {
console.info('loaded search addon')
})

export default class XTerm extends React.Component {

static propTypes = {
Expand Down
50 changes: 27 additions & 23 deletions pkg/ui/src/utils/LogFollower.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,39 @@ import { addError } from '../state/actions/errors'
const colorCodes = [153,215,230,147,14,10,11,159,255].map(val => `\x1B[38;5;${val}m`)
const logSuffix = `\x1B[0m`

export default class LogFollower {

// Buffer allows for appending received log lines before
// the on-screen view component is ready to receive them;
// until the onPush method is defined, 'push' works normally
static Buffer = function(maxSize=2500) {
let buffer = []
buffer._maxSize = maxSize
buffer.push = function(e) {
// if an 'onPush' is defined, and it returns false, don't
// add the element to the array
if (typeof buffer.onPush === 'function') {
if (!buffer.onPush(e)) {
return
}
}
let size = Array.prototype.push.call(buffer, e)
if (size >= buffer._maxSize) {
buffer.splice(0, size - buffer._maxSize)
class LogBuffer extends Array {
constructor(maxSize=2500) {
super(0)
this._maxSize = maxSize
this._colorIndex = 0
}

push = (e) => {
// if an 'onPush' is defined, and it returns false, don't
// add the element to the array
if (typeof this.onPush === 'function') {
if (!this.onPush(e)) {
return
}
}
buffer._colorIndex = 0
buffer.nextColor = function() {
buffer._colorIndex = (buffer._colorIndex + 1) % colorCodes.length
return colorCodes[buffer._colorIndex]
let size = Array.prototype.push.call(this, e)
if (size >= this._maxSize) {
this.splice(0, size - this._maxSize)
}
return buffer
}

nextColor = () => {
this._colorIndex = (this._colorIndex + 1) % colorCodes.length
return colorCodes[this._colorIndex]
}
}


export default class LogFollower {

static Buffer = LogBuffer

constructor(props) {

this.props = props
Expand Down

0 comments on commit 546c07b

Please sign in to comment.