Merge pull request #2188 from influxdata/feature/tickscript-logging
Tickscript Loggingpull/2416/head^2
commit
37d96f7cc7
|
@ -17,6 +17,7 @@
|
||||||
1. [#2423](https://github.com/influxdata/chronograf/pull/2423): Gracefully scale Template Variables Manager overlay on smaller displays
|
1. [#2423](https://github.com/influxdata/chronograf/pull/2423): Gracefully scale Template Variables Manager overlay on smaller displays
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
1. [#2188](https://github.com/influxdata/chronograf/pull/2188): Add Kapacitor logs to the TICKscript editor
|
||||||
1. [#2384](https://github.com/influxdata/chronograf/pull/2384): Add filtering by name to Dashboard index page
|
1. [#2384](https://github.com/influxdata/chronograf/pull/2384): Add filtering by name to Dashboard index page
|
||||||
1. [#2385](https://github.com/influxdata/chronograf/pull/2385): Add time shift feature to DataExplorer and Dashboards
|
1. [#2385](https://github.com/influxdata/chronograf/pull/2385): Add time shift feature to DataExplorer and Dashboards
|
||||||
1. [#2400](https://github.com/influxdata/chronograf/pull/2400): Allow override of generic oauth2 keys for email
|
1. [#2400](https://github.com/influxdata/chronograf/pull/2400): Allow override of generic oauth2 keys for email
|
||||||
|
@ -43,7 +44,7 @@
|
||||||
### UI Improvements
|
### UI Improvements
|
||||||
1. [#2111](https://github.com/influxdata/chronograf/pull/2111): Increase size of Cell Editor query tabs to reveal more of their query strings
|
1. [#2111](https://github.com/influxdata/chronograf/pull/2111): Increase size of Cell Editor query tabs to reveal more of their query strings
|
||||||
1. [#2120](https://github.com/influxdata/chronograf/pull/2120): Improve appearance of Admin Page tabs on smaller screens
|
1. [#2120](https://github.com/influxdata/chronograf/pull/2120): Improve appearance of Admin Page tabs on smaller screens
|
||||||
1. [#2119](https://github.com/influxdata/chronograf/pull/2119): Add cancel button to Tickscript editor
|
1. [#2119](https://github.com/influxdata/chronograf/pull/2119): Add cancel button to TICKscript editor
|
||||||
1. [#2104](https://github.com/influxdata/chronograf/pull/2104): Redesign dashboard naming & renaming interaction
|
1. [#2104](https://github.com/influxdata/chronograf/pull/2104): Redesign dashboard naming & renaming interaction
|
||||||
1. [#2104](https://github.com/influxdata/chronograf/pull/2104): Redesign dashboard switching dropdown
|
1. [#2104](https://github.com/influxdata/chronograf/pull/2104): Redesign dashboard switching dropdown
|
||||||
|
|
||||||
|
@ -63,7 +64,7 @@
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
1. [#1885](https://github.com/influxdata/chronograf/pull/1885): Add `fill` options to data explorer and dashboard queries
|
1. [#1885](https://github.com/influxdata/chronograf/pull/1885): Add `fill` options to data explorer and dashboard queries
|
||||||
1. [#1978](https://github.com/influxdata/chronograf/pull/1978): Support editing kapacitor TICKScript
|
1. [#1978](https://github.com/influxdata/chronograf/pull/1978): Support editing kapacitor TICKscript
|
||||||
1. [#1721](https://github.com/influxdata/chronograf/pull/1721): Introduce the TICKscript editor UI
|
1. [#1721](https://github.com/influxdata/chronograf/pull/1721): Introduce the TICKscript editor UI
|
||||||
1. [#1992](https://github.com/influxdata/chronograf/pull/1992): Add .csv download button to data explorer
|
1. [#1992](https://github.com/influxdata/chronograf/pull/1992): Add .csv download button to data explorer
|
||||||
1. [#2082](https://github.com/influxdata/chronograf/pull/2082): Add Data Explorer InfluxQL query and location query synchronization, so queries can be shared via a a URL
|
1. [#2082](https://github.com/influxdata/chronograf/pull/2082): Add Data Explorer InfluxQL query and location query synchronization, so queries can be shared via a a URL
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
'arrow-parens': 0,
|
'arrow-parens': 0,
|
||||||
'comma-dangle': [2, 'always-multiline'],
|
'comma-dangle': [2, 'always-multiline'],
|
||||||
'no-cond-assign': 2,
|
'no-cond-assign': 2,
|
||||||
'no-console': ['error', {allow: ['error']}],
|
'no-console': ['error', {allow: ['error', 'warn']}],
|
||||||
'no-constant-condition': 2,
|
'no-constant-condition': 2,
|
||||||
'no-control-regex': 2,
|
'no-control-regex': 2,
|
||||||
'no-debugger': 2,
|
'no-debugger': 2,
|
||||||
|
|
|
@ -100,3 +100,32 @@ export const updateTask = async (
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getLogStream = kapacitor =>
|
||||||
|
fetch(`${kapacitor.links.proxy}?path=/kapacitor/v1preview/logs`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const getLogStreamByRuleID = (kapacitor, ruleID) =>
|
||||||
|
fetch(
|
||||||
|
`${kapacitor.links.proxy}?path=/kapacitor/v1preview/logs?task=${ruleID}`,
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const pingKapacitorVersion = async kapacitor => {
|
||||||
|
try {
|
||||||
|
const result = await AJAX({
|
||||||
|
method: 'GET',
|
||||||
|
url: `${kapacitor.links.proxy}?path=/kapacitor/v1preview/ping`,
|
||||||
|
})
|
||||||
|
const kapVersion = result.headers['x-kapacitor-version']
|
||||||
|
return kapVersion === '' ? null : kapVersion
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
|
const LogItemHTTP = ({logItem}) =>
|
||||||
|
<div className="logs-table--row">
|
||||||
|
<div className="logs-table--divider">
|
||||||
|
<div className={`logs-table--level ${logItem.lvl}`} />
|
||||||
|
<div className="logs-table--timestamp">
|
||||||
|
{logItem.ts}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="logs-table--details">
|
||||||
|
<div className="logs-table--service">HTTP Request</div>
|
||||||
|
<div className="logs-table--http">
|
||||||
|
{logItem.method} {logItem.username}@{logItem.host} ({logItem.duration})
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
const {shape, string} = PropTypes
|
||||||
|
|
||||||
|
LogItemHTTP.propTypes = {
|
||||||
|
logItem: shape({
|
||||||
|
lvl: string.isRequired,
|
||||||
|
ts: string.isRequired,
|
||||||
|
method: string.isRequired,
|
||||||
|
username: string.isRequired,
|
||||||
|
host: string.isRequired,
|
||||||
|
duration: string.isRequired,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LogItemHTTP
|
|
@ -0,0 +1,32 @@
|
||||||
|
import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
|
const LogItemHTTPError = ({logItem}) =>
|
||||||
|
<div className="logs-table--row" key={logItem.key}>
|
||||||
|
<div className="logs-table--divider">
|
||||||
|
<div className={`logs-table--level ${logItem.lvl}`} />
|
||||||
|
<div className="logs-table--timestamp">
|
||||||
|
{logItem.ts}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="logs-table--details">
|
||||||
|
<div className="logs-table--service error">HTTP Server</div>
|
||||||
|
<div className="logs-table--blah">
|
||||||
|
<div className="logs-table--key-values error">
|
||||||
|
ERROR: {logItem.msg}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
const {shape, string} = PropTypes
|
||||||
|
|
||||||
|
LogItemHTTPError.propTypes = {
|
||||||
|
logItem: shape({
|
||||||
|
key: string.isRequired,
|
||||||
|
lvl: string.isRequired,
|
||||||
|
ts: string.isRequired,
|
||||||
|
msg: string.isRequired,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LogItemHTTPError
|
|
@ -0,0 +1,34 @@
|
||||||
|
import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
|
const LogItemInfluxDBDebug = ({logItem}) =>
|
||||||
|
<div className="logs-table--row">
|
||||||
|
<div className="logs-table--divider">
|
||||||
|
<div className={`logs-table--level ${logItem.lvl}`} />
|
||||||
|
<div className="logs-table--timestamp">
|
||||||
|
{logItem.ts}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="logs-table--details">
|
||||||
|
<div className="logs-table--service debug">InfluxDB</div>
|
||||||
|
<div className="logs-table--blah">
|
||||||
|
<div className="logs-table--key-values debug">
|
||||||
|
DEBUG: {logItem.msg}
|
||||||
|
<br />
|
||||||
|
Cluster: {logItem.cluster}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
const {shape, string} = PropTypes
|
||||||
|
|
||||||
|
LogItemInfluxDBDebug.propTypes = {
|
||||||
|
logItem: shape({
|
||||||
|
lvl: string.isRequired,
|
||||||
|
ts: string.isRequired,
|
||||||
|
msg: string.isRequired,
|
||||||
|
cluster: string.isRequired,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LogItemInfluxDBDebug
|
|
@ -0,0 +1,31 @@
|
||||||
|
import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
|
const LogItemKapacitorDebug = ({logItem}) =>
|
||||||
|
<div className="logs-table--row">
|
||||||
|
<div className="logs-table--divider">
|
||||||
|
<div className={`logs-table--level ${logItem.lvl}`} />
|
||||||
|
<div className="logs-table--timestamp">
|
||||||
|
{logItem.ts}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="logs-table--details">
|
||||||
|
<div className="logs-table--service debug">Kapacitor</div>
|
||||||
|
<div className="logs-table--blah">
|
||||||
|
<div className="logs-table--key-values debug">
|
||||||
|
DEBUG: {logItem.msg}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
const {shape, string} = PropTypes
|
||||||
|
|
||||||
|
LogItemKapacitorDebug.propTypes = {
|
||||||
|
logItem: shape({
|
||||||
|
lvl: string.isRequired,
|
||||||
|
ts: string.isRequired,
|
||||||
|
msg: string.isRequired,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LogItemKapacitorDebug
|
|
@ -0,0 +1,31 @@
|
||||||
|
import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
|
const LogItemKapacitorError = ({logItem}) =>
|
||||||
|
<div className="logs-table--row">
|
||||||
|
<div className="logs-table--divider">
|
||||||
|
<div className={`logs-table--level ${logItem.lvl}`} />
|
||||||
|
<div className="logs-table--timestamp">
|
||||||
|
{logItem.ts}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="logs-table--details">
|
||||||
|
<div className="logs-table--service error">Kapacitor</div>
|
||||||
|
<div className="logs-table--blah">
|
||||||
|
<div className="logs-table--key-values error">
|
||||||
|
ERROR: {logItem.msg}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
const {shape, string} = PropTypes
|
||||||
|
|
||||||
|
LogItemKapacitorError.propTypes = {
|
||||||
|
logItem: shape({
|
||||||
|
lvl: string.isRequired,
|
||||||
|
ts: string.isRequired,
|
||||||
|
msg: string.isRequired,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LogItemKapacitorError
|
|
@ -0,0 +1,51 @@
|
||||||
|
import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
|
const renderKeysAndValues = object => {
|
||||||
|
if (!object) {
|
||||||
|
return <span className="logs-table--empty-cell">--</span>
|
||||||
|
}
|
||||||
|
const objKeys = Object.keys(object)
|
||||||
|
const objValues = Object.values(object)
|
||||||
|
|
||||||
|
const objElements = objKeys.map((objKey, i) =>
|
||||||
|
<div key={i} className="logs-table--key-value">
|
||||||
|
{objKey}: <span>{objValues[i]}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
return objElements
|
||||||
|
}
|
||||||
|
const LogItemKapacitorPoint = ({logItem}) =>
|
||||||
|
<div className="logs-table--row">
|
||||||
|
<div className="logs-table--divider">
|
||||||
|
<div className={`logs-table--level ${logItem.lvl}`} />
|
||||||
|
<div className="logs-table--timestamp">
|
||||||
|
{logItem.ts}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="logs-table--details">
|
||||||
|
<div className="logs-table--service">Kapacitor Point</div>
|
||||||
|
<div className="logs-table--blah">
|
||||||
|
<div className="logs-table--key-values">
|
||||||
|
TAGS<br />
|
||||||
|
{renderKeysAndValues(logItem.tag)}
|
||||||
|
</div>
|
||||||
|
<div className="logs-table--key-values">
|
||||||
|
FIELDS<br />
|
||||||
|
{renderKeysAndValues(logItem.field)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
const {shape, string} = PropTypes
|
||||||
|
|
||||||
|
LogItemKapacitorPoint.propTypes = {
|
||||||
|
logItem: shape({
|
||||||
|
lvl: string.isRequired,
|
||||||
|
ts: string.isRequired,
|
||||||
|
tag: shape.isRequired,
|
||||||
|
field: shape.isRequired,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LogItemKapacitorPoint
|
|
@ -0,0 +1,28 @@
|
||||||
|
import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
|
const LogItemSession = ({logItem}) =>
|
||||||
|
<div className="logs-table--row">
|
||||||
|
<div className="logs-table--divider">
|
||||||
|
<div className={`logs-table--level ${logItem.lvl}`} />
|
||||||
|
<div className="logs-table--timestamp">
|
||||||
|
{logItem.ts}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="logs-table--details">
|
||||||
|
<div className="logs-table--session">
|
||||||
|
{logItem.msg}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
const {shape, string} = PropTypes
|
||||||
|
|
||||||
|
LogItemSession.propTypes = {
|
||||||
|
logItem: shape({
|
||||||
|
lvl: string.isRequired,
|
||||||
|
ts: string.isRequired,
|
||||||
|
msg: string.isRequired,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LogItemSession
|
|
@ -0,0 +1,38 @@
|
||||||
|
import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
|
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||||
|
import LogsTableRow from 'src/kapacitor/components/LogsTableRow'
|
||||||
|
|
||||||
|
const LogsTable = ({logs}) =>
|
||||||
|
<div className="logs-table--container">
|
||||||
|
<div className="logs-table--header">
|
||||||
|
<h2 className="panel-title">Logs</h2>
|
||||||
|
</div>
|
||||||
|
<FancyScrollbar
|
||||||
|
className="logs-table--panel fancy-scroll--kapacitor"
|
||||||
|
autoHide={false}
|
||||||
|
>
|
||||||
|
<div className="logs-table">
|
||||||
|
{logs.length
|
||||||
|
? logs.map((log, i) =>
|
||||||
|
<LogsTableRow key={log.key} logItem={log} index={i} />
|
||||||
|
)
|
||||||
|
: <div className="page-spinner" />}
|
||||||
|
</div>
|
||||||
|
</FancyScrollbar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
const {arrayOf, shape, string} = PropTypes
|
||||||
|
|
||||||
|
LogsTable.propTypes = {
|
||||||
|
logs: arrayOf(
|
||||||
|
shape({
|
||||||
|
key: string.isRequired,
|
||||||
|
ts: string.isRequired,
|
||||||
|
lvl: string.isRequired,
|
||||||
|
msg: string.isRequired,
|
||||||
|
})
|
||||||
|
).isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LogsTable
|
|
@ -0,0 +1,68 @@
|
||||||
|
import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
|
import LogItemSession from 'src/kapacitor/components/LogItemSession'
|
||||||
|
import LogItemHTTP from 'src/kapacitor/components/LogItemHTTP'
|
||||||
|
import LogItemHTTPError from 'src/kapacitor/components/LogItemHTTPError'
|
||||||
|
import LogItemKapacitorPoint from 'src/kapacitor/components/LogItemKapacitorPoint'
|
||||||
|
import LogItemKapacitorError from 'src/kapacitor/components/LogItemKapacitorError'
|
||||||
|
import LogItemKapacitorDebug from 'src/kapacitor/components/LogItemKapacitorDebug'
|
||||||
|
import LogItemInfluxDBDebug from 'src/kapacitor/components/LogItemInfluxDBDebug'
|
||||||
|
|
||||||
|
const LogsTableRow = ({logItem, index}) => {
|
||||||
|
if (logItem.service === 'sessions') {
|
||||||
|
return <LogItemSession logItem={logItem} key={index} />
|
||||||
|
}
|
||||||
|
if (logItem.service === 'http' && logItem.msg === 'http request') {
|
||||||
|
return <LogItemHTTP logItem={logItem} key={index} />
|
||||||
|
}
|
||||||
|
if (logItem.service === 'kapacitor' && logItem.msg === 'point') {
|
||||||
|
return <LogItemKapacitorPoint logItem={logItem} key={index} />
|
||||||
|
}
|
||||||
|
if (logItem.service === 'httpd_server_errors' && logItem.lvl === 'error') {
|
||||||
|
return <LogItemHTTPError logItem={logItem} key={index} />
|
||||||
|
}
|
||||||
|
if (logItem.service === 'kapacitor' && logItem.lvl === 'error') {
|
||||||
|
return <LogItemKapacitorError logItem={logItem} key={index} />
|
||||||
|
}
|
||||||
|
if (logItem.service === 'kapacitor' && logItem.lvl === 'debug') {
|
||||||
|
return <LogItemKapacitorDebug logItem={logItem} key={index} />
|
||||||
|
}
|
||||||
|
if (logItem.service === 'influxdb' && logItem.lvl === 'debug') {
|
||||||
|
return <LogItemInfluxDBDebug logItem={logItem} key={index} />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="logs-table--row" key={index}>
|
||||||
|
<div className="logs-table--divider">
|
||||||
|
<div className={`logs-table--level ${logItem.lvl}`} />
|
||||||
|
<div className="logs-table--timestamp">
|
||||||
|
{logItem.ts}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="logs-table--details">
|
||||||
|
<div className="logs-table--service">
|
||||||
|
{logItem.service || '--'}
|
||||||
|
</div>
|
||||||
|
<div className="logs-table--blah">
|
||||||
|
<div className="logs-table--key-values">
|
||||||
|
{logItem.msg || '--'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const {number, shape, string} = PropTypes
|
||||||
|
|
||||||
|
LogsTableRow.propTypes = {
|
||||||
|
logItem: shape({
|
||||||
|
key: string.isRequired,
|
||||||
|
ts: string.isRequired,
|
||||||
|
lvl: string.isRequired,
|
||||||
|
msg: string.isRequired,
|
||||||
|
}).isRequired,
|
||||||
|
index: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LogsTableRow
|
|
@ -0,0 +1,26 @@
|
||||||
|
import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
|
const LogsToggle = ({areLogsVisible, onToggleLogsVisbility}) =>
|
||||||
|
<ul className="nav nav-tablist nav-tablist-sm nav-tablist-malachite logs-toggle">
|
||||||
|
<li
|
||||||
|
className={areLogsVisible ? null : 'active'}
|
||||||
|
onClick={onToggleLogsVisbility}
|
||||||
|
>
|
||||||
|
Editor
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
className={areLogsVisible ? 'active' : null}
|
||||||
|
onClick={onToggleLogsVisbility}
|
||||||
|
>
|
||||||
|
Editor + Logs
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
const {bool, func} = PropTypes
|
||||||
|
|
||||||
|
LogsToggle.propTypes = {
|
||||||
|
areLogsVisible: bool,
|
||||||
|
onToggleLogsVisbility: func.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LogsToggle
|
|
@ -2,56 +2,63 @@ import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
import TickscriptHeader from 'src/kapacitor/components/TickscriptHeader'
|
import TickscriptHeader from 'src/kapacitor/components/TickscriptHeader'
|
||||||
import TickscriptEditor from 'src/kapacitor/components/TickscriptEditor'
|
import TickscriptEditor from 'src/kapacitor/components/TickscriptEditor'
|
||||||
|
import TickscriptEditorControls from 'src/kapacitor/components/TickscriptEditorControls'
|
||||||
|
import TickscriptEditorConsole from 'src/kapacitor/components/TickscriptEditorConsole'
|
||||||
|
import LogsTable from 'src/kapacitor/components/LogsTable'
|
||||||
|
|
||||||
const Tickscript = ({
|
const Tickscript = ({
|
||||||
source,
|
|
||||||
onSave,
|
onSave,
|
||||||
task,
|
task,
|
||||||
|
logs,
|
||||||
validation,
|
validation,
|
||||||
onSelectDbrps,
|
onSelectDbrps,
|
||||||
onChangeScript,
|
onChangeScript,
|
||||||
onChangeType,
|
onChangeType,
|
||||||
onChangeID,
|
onChangeID,
|
||||||
isNewTickscript,
|
isNewTickscript,
|
||||||
|
areLogsVisible,
|
||||||
|
areLogsEnabled,
|
||||||
|
onToggleLogsVisbility,
|
||||||
}) =>
|
}) =>
|
||||||
<div className="page">
|
<div className="page">
|
||||||
<TickscriptHeader
|
<TickscriptHeader
|
||||||
task={task}
|
task={task}
|
||||||
source={source}
|
|
||||||
onSave={onSave}
|
onSave={onSave}
|
||||||
onChangeID={onChangeID}
|
areLogsVisible={areLogsVisible}
|
||||||
onChangeType={onChangeType}
|
areLogsEnabled={areLogsEnabled}
|
||||||
onSelectDbrps={onSelectDbrps}
|
onToggleLogsVisbility={onToggleLogsVisbility}
|
||||||
isNewTickscript={isNewTickscript}
|
isNewTickscript={isNewTickscript}
|
||||||
/>
|
/>
|
||||||
<div className="page-contents">
|
<div className="page-contents--split">
|
||||||
<div className="tickscript-console">
|
<div className="tickscript">
|
||||||
<div className="tickscript-console--output">
|
<TickscriptEditorControls
|
||||||
{validation
|
isNewTickscript={isNewTickscript}
|
||||||
? <p>
|
onSelectDbrps={onSelectDbrps}
|
||||||
{validation}
|
onChangeType={onChangeType}
|
||||||
</p>
|
onChangeID={onChangeID}
|
||||||
: <p className="tickscript-console--default">
|
task={task}
|
||||||
Save your TICKscript to validate it
|
/>
|
||||||
</p>}
|
<TickscriptEditorConsole validation={validation} />
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="tickscript-editor">
|
|
||||||
<TickscriptEditor
|
<TickscriptEditor
|
||||||
script={task.tickscript}
|
script={task.tickscript}
|
||||||
onChangeScript={onChangeScript}
|
onChangeScript={onChangeScript}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{areLogsVisible ? <LogsTable logs={logs} /> : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
const {arrayOf, bool, func, shape, string} = PropTypes
|
const {arrayOf, bool, func, shape, string} = PropTypes
|
||||||
|
|
||||||
Tickscript.propTypes = {
|
Tickscript.propTypes = {
|
||||||
|
logs: arrayOf(shape()).isRequired,
|
||||||
onSave: func.isRequired,
|
onSave: func.isRequired,
|
||||||
source: shape({
|
source: shape({
|
||||||
id: string,
|
id: string,
|
||||||
}),
|
}),
|
||||||
|
areLogsVisible: bool,
|
||||||
|
areLogsEnabled: bool,
|
||||||
|
onToggleLogsVisbility: func.isRequired,
|
||||||
task: shape({
|
task: shape({
|
||||||
id: string,
|
id: string,
|
||||||
script: string,
|
script: string,
|
||||||
|
|
|
@ -21,7 +21,13 @@ class TickscriptEditor extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CodeMirror value={script} onChange={this.updateCode} options={options} />
|
<div className="tickscript-editor">
|
||||||
|
<CodeMirror
|
||||||
|
value={script}
|
||||||
|
onChange={this.updateCode}
|
||||||
|
options={options}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
|
const TickscriptEditorConsole = ({validation}) =>
|
||||||
|
<div className="tickscript-console">
|
||||||
|
<div className="tickscript-console--output">
|
||||||
|
{validation
|
||||||
|
? <p>
|
||||||
|
{validation}
|
||||||
|
</p>
|
||||||
|
: <p className="tickscript-console--default">
|
||||||
|
Save your TICKscript to validate it
|
||||||
|
</p>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
const {string} = PropTypes
|
||||||
|
|
||||||
|
TickscriptEditorConsole.propTypes = {
|
||||||
|
validation: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TickscriptEditorConsole
|
|
@ -0,0 +1,44 @@
|
||||||
|
import React, {PropTypes} from 'react'
|
||||||
|
import TickscriptType from 'src/kapacitor/components/TickscriptType'
|
||||||
|
import MultiSelectDBDropdown from 'shared/components/MultiSelectDBDropdown'
|
||||||
|
import TickscriptID, {
|
||||||
|
TickscriptStaticID,
|
||||||
|
} from 'src/kapacitor/components/TickscriptID'
|
||||||
|
|
||||||
|
const addName = list => list.map(l => ({...l, name: `${l.db}.${l.rp}`}))
|
||||||
|
|
||||||
|
const TickscriptEditorControls = ({
|
||||||
|
isNewTickscript,
|
||||||
|
onSelectDbrps,
|
||||||
|
onChangeType,
|
||||||
|
onChangeID,
|
||||||
|
task,
|
||||||
|
}) =>
|
||||||
|
<div className="tickscript-controls">
|
||||||
|
{isNewTickscript
|
||||||
|
? <TickscriptID onChangeID={onChangeID} id={task.id} />
|
||||||
|
: <TickscriptStaticID id={task.name} />}
|
||||||
|
<div className="tickscript-controls--right">
|
||||||
|
<TickscriptType type={task.type} onChangeType={onChangeType} />
|
||||||
|
<MultiSelectDBDropdown
|
||||||
|
selectedItems={addName(task.dbrps)}
|
||||||
|
onApply={onSelectDbrps}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
const {arrayOf, bool, func, shape, string} = PropTypes
|
||||||
|
|
||||||
|
TickscriptEditorControls.propTypes = {
|
||||||
|
isNewTickscript: bool.isRequired,
|
||||||
|
onSelectDbrps: func.isRequired,
|
||||||
|
onChangeType: func.isRequired,
|
||||||
|
onChangeID: func.isRequired,
|
||||||
|
task: shape({
|
||||||
|
id: string,
|
||||||
|
script: string,
|
||||||
|
dbsrps: arrayOf(shape()),
|
||||||
|
}).isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TickscriptEditorControls
|
|
@ -1,52 +1,36 @@
|
||||||
import React, {PropTypes} from 'react'
|
import React, {PropTypes} from 'react'
|
||||||
import {Link} from 'react-router'
|
|
||||||
|
|
||||||
import SourceIndicator from 'shared/components/SourceIndicator'
|
import SourceIndicator from 'shared/components/SourceIndicator'
|
||||||
import TickscriptType from 'src/kapacitor/components/TickscriptType'
|
import LogsToggle from 'src/kapacitor/components/LogsToggle'
|
||||||
import MultiSelectDBDropdown from 'shared/components/MultiSelectDBDropdown'
|
|
||||||
import TickscriptID, {
|
|
||||||
TickscriptStaticID,
|
|
||||||
} from 'src/kapacitor/components/TickscriptID'
|
|
||||||
|
|
||||||
const addName = list => list.map(l => ({...l, name: `${l.db}.${l.rp}`}))
|
|
||||||
|
|
||||||
const TickscriptHeader = ({
|
const TickscriptHeader = ({
|
||||||
task: {id, type, dbrps},
|
task: {id},
|
||||||
task,
|
|
||||||
source,
|
|
||||||
onSave,
|
onSave,
|
||||||
onChangeType,
|
areLogsVisible,
|
||||||
onChangeID,
|
areLogsEnabled,
|
||||||
onSelectDbrps,
|
|
||||||
isNewTickscript,
|
isNewTickscript,
|
||||||
|
onToggleLogsVisbility,
|
||||||
}) =>
|
}) =>
|
||||||
<div className="page-header">
|
<div className="page-header full-width">
|
||||||
<div className="page-header__container">
|
<div className="page-header__container">
|
||||||
<div className="page-header__left">
|
<div className="page-header__left">
|
||||||
{isNewTickscript
|
<h1 className="page-header__title">TICKscript Editor</h1>
|
||||||
? <TickscriptID onChangeID={onChangeID} id={id} />
|
|
||||||
: <TickscriptStaticID id={task.name} />}
|
|
||||||
</div>
|
</div>
|
||||||
|
{areLogsEnabled &&
|
||||||
|
<LogsToggle
|
||||||
|
areLogsVisible={areLogsVisible}
|
||||||
|
areLogsEnabled={areLogsEnabled}
|
||||||
|
onToggleLogsVisbility={onToggleLogsVisbility}
|
||||||
|
/>}
|
||||||
<div className="page-header__right">
|
<div className="page-header__right">
|
||||||
<SourceIndicator />
|
<SourceIndicator />
|
||||||
<TickscriptType type={type} onChangeType={onChangeType} />
|
|
||||||
<MultiSelectDBDropdown
|
|
||||||
selectedItems={addName(dbrps)}
|
|
||||||
onApply={onSelectDbrps}
|
|
||||||
/>
|
|
||||||
<Link
|
|
||||||
className="btn btn-sm btn-default"
|
|
||||||
to={`/sources/${source.id}/alert-rules`}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Link>
|
|
||||||
<button
|
<button
|
||||||
className="btn btn-success btn-sm"
|
className="btn btn-success btn-sm"
|
||||||
title={id ? '' : 'ID your TICKscript to save'}
|
title={id ? '' : 'ID your TICKscript to save'}
|
||||||
onClick={onSave}
|
onClick={onSave}
|
||||||
disabled={!id}
|
disabled={!id}
|
||||||
>
|
>
|
||||||
Save Rule
|
{isNewTickscript ? 'Save New TICKscript' : 'Save TICKscript'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -55,11 +39,11 @@ const TickscriptHeader = ({
|
||||||
const {arrayOf, bool, func, shape, string} = PropTypes
|
const {arrayOf, bool, func, shape, string} = PropTypes
|
||||||
|
|
||||||
TickscriptHeader.propTypes = {
|
TickscriptHeader.propTypes = {
|
||||||
|
isNewTickscript: bool,
|
||||||
onSave: func,
|
onSave: func,
|
||||||
source: shape({
|
areLogsVisible: bool,
|
||||||
id: string,
|
areLogsEnabled: bool,
|
||||||
}),
|
onToggleLogsVisbility: func.isRequired,
|
||||||
onSelectDbrps: func.isRequired,
|
|
||||||
task: shape({
|
task: shape({
|
||||||
dbrps: arrayOf(
|
dbrps: arrayOf(
|
||||||
shape({
|
shape({
|
||||||
|
@ -68,9 +52,6 @@ TickscriptHeader.propTypes = {
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
onChangeType: func.isRequired,
|
|
||||||
onChangeID: func.isRequired,
|
|
||||||
isNewTickscript: bool.isRequired,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TickscriptHeader
|
export default TickscriptHeader
|
||||||
|
|
|
@ -10,7 +10,7 @@ class TickscriptID extends Component {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
className="page-header--editing kapacitor-theme"
|
className="form-control input-sm form-malachite"
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
value={id}
|
value={id}
|
||||||
onChange={onChangeID}
|
onChange={onChangeID}
|
||||||
|
@ -23,10 +23,7 @@ class TickscriptID extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TickscriptStaticID = ({id}) =>
|
export const TickscriptStaticID = ({id}) =>
|
||||||
<h1
|
<h1 className="tickscript-controls--name">
|
||||||
className="page-header--editing kapacitor-theme"
|
|
||||||
style={{display: 'flex', justifyContent: 'baseline'}}
|
|
||||||
>
|
|
||||||
{id}
|
{id}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
import React, {PropTypes, Component} from 'react'
|
import React, {PropTypes, Component} from 'react'
|
||||||
import {connect} from 'react-redux'
|
import {connect} from 'react-redux'
|
||||||
import {bindActionCreators} from 'redux'
|
import {bindActionCreators} from 'redux'
|
||||||
|
import uuid from 'node-uuid'
|
||||||
|
|
||||||
import Tickscript from 'src/kapacitor/components/Tickscript'
|
import Tickscript from 'src/kapacitor/components/Tickscript'
|
||||||
import * as kapactiorActionCreators from 'src/kapacitor/actions/view'
|
import * as kapactiorActionCreators from 'src/kapacitor/actions/view'
|
||||||
import * as errorActionCreators from 'shared/actions/errors'
|
import * as errorActionCreators from 'shared/actions/errors'
|
||||||
import {getActiveKapacitor} from 'src/shared/apis'
|
import {getActiveKapacitor} from 'src/shared/apis'
|
||||||
|
import {getLogStreamByRuleID, pingKapacitorVersion} from 'src/kapacitor/apis'
|
||||||
|
import {publishNotification} from 'shared/actions/notifications'
|
||||||
|
|
||||||
class TickscriptPage extends Component {
|
class TickscriptPage extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -23,6 +26,96 @@ class TickscriptPage extends Component {
|
||||||
},
|
},
|
||||||
validation: '',
|
validation: '',
|
||||||
isEditingID: true,
|
isEditingID: true,
|
||||||
|
logs: [],
|
||||||
|
areLogsEnabled: false,
|
||||||
|
failStr: '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchChunkedLogs = async (kapacitor, ruleID) => {
|
||||||
|
const {notify} = this.props
|
||||||
|
|
||||||
|
try {
|
||||||
|
const version = await pingKapacitorVersion(kapacitor)
|
||||||
|
|
||||||
|
if (version && parseInt(version.split('.')[1], 10) < 4) {
|
||||||
|
this.setState({
|
||||||
|
areLogsEnabled: false,
|
||||||
|
})
|
||||||
|
notify(
|
||||||
|
'warning',
|
||||||
|
'Could not use logging, requires Kapacitor version 1.4'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.logs.length === 0) {
|
||||||
|
this.setState({
|
||||||
|
areLogsEnabled: true,
|
||||||
|
logs: [
|
||||||
|
{
|
||||||
|
id: uuid.v4(),
|
||||||
|
key: uuid.v4(),
|
||||||
|
lvl: 'info',
|
||||||
|
msg: 'created log session',
|
||||||
|
service: 'sessions',
|
||||||
|
tags: 'nil',
|
||||||
|
ts: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await getLogStreamByRuleID(kapacitor, ruleID)
|
||||||
|
|
||||||
|
const reader = await response.body.getReader()
|
||||||
|
const decoder = new TextDecoder()
|
||||||
|
|
||||||
|
let result
|
||||||
|
|
||||||
|
while (this.state.areLogsEnabled === true && !(result && result.done)) {
|
||||||
|
result = await reader.read()
|
||||||
|
|
||||||
|
const chunk = decoder.decode(result.value || new Uint8Array(), {
|
||||||
|
stream: !result.done,
|
||||||
|
})
|
||||||
|
|
||||||
|
const json = chunk.split('\n')
|
||||||
|
|
||||||
|
let logs = []
|
||||||
|
let failStr = this.state.failStr
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (let objStr of json) {
|
||||||
|
objStr = failStr + objStr
|
||||||
|
failStr = objStr
|
||||||
|
const jsonStr = `[${objStr.split('}{').join('},{')}]`
|
||||||
|
logs = [
|
||||||
|
...logs,
|
||||||
|
...JSON.parse(jsonStr).map(log => ({
|
||||||
|
...log,
|
||||||
|
key: uuid.v4(),
|
||||||
|
})),
|
||||||
|
]
|
||||||
|
failStr = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
logs: [...this.state.logs, ...logs],
|
||||||
|
failStr,
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(err, failStr)
|
||||||
|
this.setState({
|
||||||
|
logs: [...this.state.logs, ...logs],
|
||||||
|
failStr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
notify('error', error)
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,9 +143,17 @@ class TickscriptPage extends Component {
|
||||||
this.setState({task: {tickscript, dbrps, type, status, name, id}})
|
this.setState({task: {tickscript, dbrps, type, status, name, id}})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.fetchChunkedLogs(kapacitor, ruleID)
|
||||||
|
|
||||||
this.setState({kapacitor})
|
this.setState({kapacitor})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.setState({
|
||||||
|
areLogsEnabled: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
handleSave = async () => {
|
handleSave = async () => {
|
||||||
const {kapacitor, task} = this.state
|
const {kapacitor, task} = this.state
|
||||||
const {
|
const {
|
||||||
|
@ -96,13 +197,18 @@ class TickscriptPage extends Component {
|
||||||
this.setState({task: {...this.state.task, id: e.target.value}})
|
this.setState({task: {...this.state.task, id: e.target.value}})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleToggleLogsVisbility = () => {
|
||||||
|
this.setState({areLogsVisible: !this.state.areLogsVisible})
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {source} = this.props
|
const {source} = this.props
|
||||||
const {task, validation} = this.state
|
const {task, validation, logs, areLogsVisible, areLogsEnabled} = this.state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tickscript
|
<Tickscript
|
||||||
task={task}
|
task={task}
|
||||||
|
logs={logs}
|
||||||
source={source}
|
source={source}
|
||||||
validation={validation}
|
validation={validation}
|
||||||
onSave={this.handleSave}
|
onSave={this.handleSave}
|
||||||
|
@ -111,6 +217,9 @@ class TickscriptPage extends Component {
|
||||||
onChangeScript={this.handleChangeScript}
|
onChangeScript={this.handleChangeScript}
|
||||||
onChangeType={this.handleChangeType}
|
onChangeType={this.handleChangeType}
|
||||||
onChangeID={this.handleChangeID}
|
onChangeID={this.handleChangeID}
|
||||||
|
areLogsVisible={areLogsVisible}
|
||||||
|
areLogsEnabled={areLogsEnabled}
|
||||||
|
onToggleLogsVisbility={this.handleToggleLogsVisbility}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -142,6 +251,7 @@ TickscriptPage.propTypes = {
|
||||||
ruleID: string,
|
ruleID: string,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
rules: arrayOf(shape()),
|
rules: arrayOf(shape()),
|
||||||
|
notify: func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
|
@ -153,6 +263,7 @@ const mapStateToProps = state => {
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
kapacitorActions: bindActionCreators(kapactiorActionCreators, dispatch),
|
kapacitorActions: bindActionCreators(kapactiorActionCreators, dispatch),
|
||||||
errorActions: bindActionCreators(errorActionCreators, dispatch),
|
errorActions: bindActionCreators(errorActionCreators, dispatch),
|
||||||
|
notify: bindActionCreators(publishNotification, dispatch),
|
||||||
})
|
})
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(TickscriptPage)
|
export default connect(mapStateToProps, mapDispatchToProps)(TickscriptPage)
|
||||||
|
|
|
@ -97,7 +97,7 @@ class ResizeContainer extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {bottomHeightPixels, topHeight, bottomHeight, isDragging} = this.state
|
const {bottomHeightPixels, topHeight, bottomHeight, isDragging} = this.state
|
||||||
const {containerClass, children} = this.props
|
const {containerClass, children, theme} = this.props
|
||||||
|
|
||||||
if (React.Children.count(children) > maximumNumChildren) {
|
if (React.Children.count(children) > maximumNumChildren) {
|
||||||
console.error(
|
console.error(
|
||||||
|
@ -122,6 +122,7 @@ class ResizeContainer extends Component {
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<ResizeHandle
|
<ResizeHandle
|
||||||
|
theme={theme}
|
||||||
isDragging={isDragging}
|
isDragging={isDragging}
|
||||||
onHandleStartDrag={this.handleStartDrag}
|
onHandleStartDrag={this.handleStartDrag}
|
||||||
top={topHeight}
|
top={topHeight}
|
||||||
|
@ -149,6 +150,7 @@ ResizeContainer.propTypes = {
|
||||||
minBottomHeight: number,
|
minBottomHeight: number,
|
||||||
initialTopHeight: string,
|
initialTopHeight: string,
|
||||||
initialBottomHeight: string,
|
initialBottomHeight: string,
|
||||||
|
theme: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ResizeContainer
|
export default ResizeContainer
|
||||||
|
|
|
@ -6,15 +6,19 @@ const ResizeHandle = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onHandleStartDrag: func.isRequired,
|
onHandleStartDrag: func.isRequired,
|
||||||
isDragging: bool.isRequired,
|
isDragging: bool.isRequired,
|
||||||
|
theme: string,
|
||||||
top: string,
|
top: string,
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {isDragging, onHandleStartDrag, top} = this.props
|
const {isDragging, onHandleStartDrag, top, theme} = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classnames('resizer--handle', {dragging: isDragging})}
|
className={classnames('resizer--handle', {
|
||||||
|
dragging: isDragging,
|
||||||
|
'resizer--malachite': theme === 'kapacitor',
|
||||||
|
})}
|
||||||
onMouseDown={onHandleStartDrag}
|
onMouseDown={onHandleStartDrag}
|
||||||
style={{top}}
|
style={{top}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
@import 'components/source-indicator';
|
@import 'components/source-indicator';
|
||||||
@import 'components/source-selector';
|
@import 'components/source-selector';
|
||||||
@import 'components/tables';
|
@import 'components/tables';
|
||||||
|
@import 'components/kapacitor-logs-table';
|
||||||
|
|
||||||
// Pages
|
// Pages
|
||||||
@import 'pages/config-endpoints';
|
@import 'pages/config-endpoints';
|
||||||
|
@ -60,6 +61,7 @@
|
||||||
@import 'pages/kapacitor';
|
@import 'pages/kapacitor';
|
||||||
@import 'pages/dashboards';
|
@import 'pages/dashboards';
|
||||||
@import 'pages/admin';
|
@import 'pages/admin';
|
||||||
|
@import 'pages/tickscript-editor';
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
@import 'unsorted';
|
@import 'unsorted';
|
||||||
|
|
|
@ -7,45 +7,6 @@
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$tickscript-console-height: 120px;
|
|
||||||
|
|
||||||
.tickscript-console,
|
|
||||||
.tickscript-editor {
|
|
||||||
padding-left: $page-wrapper-padding;
|
|
||||||
padding-right: $page-wrapper-padding;
|
|
||||||
margin: 0 auto;
|
|
||||||
max-width: $page-wrapper-max-width;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.tickscript-console {
|
|
||||||
height: $tickscript-console-height;
|
|
||||||
padding-top: 30px;
|
|
||||||
}
|
|
||||||
.tickscript-console--output {
|
|
||||||
padding: 0 60px;
|
|
||||||
font-family: $code-font;
|
|
||||||
font-weight: 600;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
background-color: $g3-castle;
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
border-radius: $radius $radius 0 0;
|
|
||||||
|
|
||||||
> p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.tickscript-console--default {
|
|
||||||
color: $g10-wolf;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
.tickscript-editor {
|
|
||||||
margin: 0 auto;
|
|
||||||
padding-bottom: 30px;
|
|
||||||
height: calc(100% - #{$tickscript-console-height});
|
|
||||||
}
|
|
||||||
.ReactCodeMirror {
|
.ReactCodeMirror {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -54,8 +15,8 @@ $tickscript-console-height: 120px;
|
||||||
.cm-s-material.CodeMirror {
|
.cm-s-material.CodeMirror {
|
||||||
border-radius: 0 0 $radius $radius;
|
border-radius: 0 0 $radius $radius;
|
||||||
font-family: $code-font;
|
font-family: $code-font;
|
||||||
background-color: $g2-kevlar;
|
background-color: transparent;
|
||||||
color: $c-neutrino;
|
color: $g13-mist;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
@ -63,7 +24,7 @@ $tickscript-console-height: 120px;
|
||||||
@include custom-scrollbar-round($g2-kevlar,$g6-smoke);
|
@include custom-scrollbar-round($g2-kevlar,$g6-smoke);
|
||||||
}
|
}
|
||||||
.cm-s-material .CodeMirror-gutters {
|
.cm-s-material .CodeMirror-gutters {
|
||||||
background-color: fade-out($g4-onyx, 0.5);
|
background-color: fade-out($g4-onyx, 0.7);
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
.CodeMirror-gutter.CodeMirror-linenumbers {
|
.CodeMirror-gutter.CodeMirror-linenumbers {
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
Styles for Kapacitor Logs Table
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
$logs-table-header-height: 60px;
|
||||||
|
$logs-table-padding: 60px;
|
||||||
|
$logs-row-indent: 6px;
|
||||||
|
$logs-level-dot: 8px;
|
||||||
|
$logs-margin: 4px;
|
||||||
|
|
||||||
|
.logs-table--container {
|
||||||
|
width: 50%;
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
@include gradient-v($g3-castle,$g1-raven);
|
||||||
|
}
|
||||||
|
.logs-table--header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
height: $logs-table-header-height;
|
||||||
|
padding: 0 $logs-table-padding 0 ($logs-table-padding / 2);
|
||||||
|
background-color: $g4-onyx;
|
||||||
|
}
|
||||||
|
.logs-table--panel {
|
||||||
|
position: absolute !important;
|
||||||
|
top: $logs-table-header-height;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100% - #{$logs-table-header-height}) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logs-table,
|
||||||
|
.logs-table--row {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
@keyframes LogsFadeIn {
|
||||||
|
from {
|
||||||
|
background-color: $g6-smoke;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.logs-table {
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
}
|
||||||
|
.logs-table--row {
|
||||||
|
padding: 8px ($logs-table-padding - 16px) 8px ($logs-table-padding / 2);
|
||||||
|
border-bottom: 2px solid $g3-castle;
|
||||||
|
animation-name: LogsFadeIn;
|
||||||
|
animation-duration: 2.5s;
|
||||||
|
animation-iteration-count: 1;
|
||||||
|
animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
|
||||||
|
transition: background-color 0.25s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $g4-onyx;
|
||||||
|
}
|
||||||
|
&:first-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.logs-table--divider {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.logs-table--level {
|
||||||
|
width: $logs-level-dot;
|
||||||
|
height: $logs-level-dot;
|
||||||
|
border-radius: 50%;
|
||||||
|
position: relative;
|
||||||
|
margin-right: $logs-row-indent;
|
||||||
|
|
||||||
|
&.debug {background-color: $c-comet;}
|
||||||
|
&.info {background-color: $g6-smoke;}
|
||||||
|
&.warn {background-color: $c-pineapple;}
|
||||||
|
&.ok {background-color: $c-rainforest;}
|
||||||
|
&.error {background-color: $c-dreamsicle;}
|
||||||
|
}
|
||||||
|
.logs-table--timestamp {
|
||||||
|
font-family: $code-font;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 11px;
|
||||||
|
color: $g9-mountain;
|
||||||
|
flex: 1 0 0;
|
||||||
|
}
|
||||||
|
.logs-table--details {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
font-size: 13px;
|
||||||
|
color: $g13-mist;
|
||||||
|
font-weight: 600;
|
||||||
|
padding-left: ($logs-level-dot + $logs-row-indent);
|
||||||
|
|
||||||
|
.error {color: $c-dreamsicle;}
|
||||||
|
.debug {color: $c-comet;}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Logs Table Item Types */
|
||||||
|
.logs-table--session {
|
||||||
|
text-transform: capitalize;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.logs-table--service {
|
||||||
|
width: 140px;
|
||||||
|
}
|
||||||
|
.logs-table--blah {
|
||||||
|
display: flex;
|
||||||
|
flex: 1 0 0;
|
||||||
|
}
|
||||||
|
.logs-table--key-values {
|
||||||
|
color: $g11-sidewalk;
|
||||||
|
flex: 1 0 50%;
|
||||||
|
}
|
||||||
|
.logs-table--key-value {
|
||||||
|
}
|
||||||
|
.logs-table--key-value span {
|
||||||
|
color: $c-pool;
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ $resizer-dots: $g3-castle;
|
||||||
$resizer-color: $g5-pepper;
|
$resizer-color: $g5-pepper;
|
||||||
$resizer-color-hover: $g8-storm;
|
$resizer-color-hover: $g8-storm;
|
||||||
$resizer-color-active: $c-pool;
|
$resizer-color-active: $c-pool;
|
||||||
|
$resizer-color-kapacitor: $c-rainforest;
|
||||||
|
|
||||||
.resize--container {
|
.resize--container {
|
||||||
overflow: hidden !important;
|
overflow: hidden !important;
|
||||||
|
@ -110,3 +111,12 @@ $resizer-color-active: $c-pool;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Kapacitor Theme */
|
||||||
|
.resizer--handle.resizer--malachite.dragging {
|
||||||
|
&:before,
|
||||||
|
&:after {
|
||||||
|
background-color: $resizer-color-kapacitor;
|
||||||
|
box-shadow: 0 0 $resizer-glow $resizer-color-kapacitor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
.page {
|
.page {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
.page-contents {
|
.page-contents,
|
||||||
|
.page-contents--split {
|
||||||
position: absolute !important;
|
position: absolute !important;
|
||||||
top: $chronograf-page-header-height;
|
top: $chronograf-page-header-height;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
@ -28,6 +29,10 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.page-contents--split {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
.container-fluid {
|
.container-fluid {
|
||||||
padding: ($chronograf-page-header-height / 2) $page-wrapper-padding;
|
padding: ($chronograf-page-header-height / 2) $page-wrapper-padding;
|
||||||
max-width: $page-wrapper-max-width;
|
max-width: $page-wrapper-max-width;
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
Styles for TICKscript Editor
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
$tickscript-console-height: 60px;
|
||||||
|
|
||||||
|
.tickscript {
|
||||||
|
flex: 1 0 0;
|
||||||
|
}
|
||||||
|
.tickscript-controls,
|
||||||
|
.tickscript-console,
|
||||||
|
.tickscript-editor {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.tickscript-controls,
|
||||||
|
.tickscript-console {
|
||||||
|
height: $tickscript-console-height;
|
||||||
|
}
|
||||||
|
.tickscript-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 60px;
|
||||||
|
background-color: $g3-castle;
|
||||||
|
}
|
||||||
|
.tickscript-controls--name {
|
||||||
|
margin: 0;
|
||||||
|
letter-spacing: 0;
|
||||||
|
@include no-user-select();
|
||||||
|
font-size: 17px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: $g13-mist;
|
||||||
|
}
|
||||||
|
.tickscript-controls--right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
|
> * {margin-left: 8px;}
|
||||||
|
}
|
||||||
|
.tickscript-console--output {
|
||||||
|
padding: 0 60px;
|
||||||
|
font-family: $code-font;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: $g2-kevlar;
|
||||||
|
border-bottom: 2px solid $g3-castle;
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
border-radius: $radius $radius 0 0;
|
||||||
|
|
||||||
|
> p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tickscript-console--default {
|
||||||
|
color: $g10-wolf;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.tickscript-editor {
|
||||||
|
height: calc(100% - #{$tickscript-console-height * 2});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Toggle for displaying Logs
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
.logs-toggle {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
|
||||||
|
> li {
|
||||||
|
width: 100px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
> li:not(.active) {
|
||||||
|
background-color: $g0-obsidian;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $g3-castle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue