mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-16 22:50:10 +03:00
parent
d3db340c91
commit
c88ee63e86
@ -35,11 +35,13 @@ export default makeStyles(theme => ({
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
'& img': {
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'cover',
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)'
|
||||
userSelect: 'none'
|
||||
},
|
||||
transition: theme.transitions.create('filter', { duration: '0.2s' }),
|
||||
filter: ({ isSelected, disableFilterImage }) => {
|
||||
|
108
src/fireedge/src/client/components/DebugLog/filters.js
Normal file
108
src/fireedge/src/client/components/DebugLog/filters.js
Normal file
@ -0,0 +1,108 @@
|
||||
import React, { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { makeStyles, Paper, Divider } from '@material-ui/core'
|
||||
import { ToggleButton, ToggleButtonGroup } from '@material-ui/lab'
|
||||
|
||||
import { DEBUG_LEVEL } from 'client/constants'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
display: 'flex',
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
flexWrap: 'wrap',
|
||||
marginBottom: '0.8em'
|
||||
},
|
||||
grouped: {
|
||||
margin: theme.spacing(0.5),
|
||||
border: 'none',
|
||||
'&:not(:first-child)': {
|
||||
borderRadius: theme.shape.borderRadius
|
||||
},
|
||||
'&:first-child': {
|
||||
borderRadius: theme.shape.borderRadius
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
const Filters = memo(({ log, filters, setFilters }) => {
|
||||
const classes = useStyles()
|
||||
|
||||
const commands = Object.keys(log)
|
||||
|
||||
const handleFilterCommands = (_, filterCommand) => {
|
||||
setFilters(prev => ({ ...prev, command: filterCommand }))
|
||||
}
|
||||
|
||||
const handleFilterSeverity = (_, filterCommand) => {
|
||||
setFilters(prev => ({ ...prev, severity: filterCommand }))
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper elevation={0} className={classes.root}>
|
||||
{/* SEVERITY FILTER */}
|
||||
<ToggleButtonGroup
|
||||
classes={{
|
||||
grouped: classes.grouped
|
||||
}}
|
||||
value={filters.severity}
|
||||
exclusive
|
||||
size='small'
|
||||
onChange={handleFilterSeverity}
|
||||
>
|
||||
{Object.values(DEBUG_LEVEL).map(severity => (
|
||||
<ToggleButton key={severity} value={severity}>
|
||||
{severity}
|
||||
</ToggleButton>
|
||||
))}
|
||||
</ToggleButtonGroup>
|
||||
|
||||
<Divider flexItem orientation="vertical" className={classes.divider} />
|
||||
|
||||
{/* COMMANDS FILTER */}
|
||||
{commands.length > 1 && (
|
||||
<ToggleButtonGroup
|
||||
classes={{
|
||||
grouped: classes.grouped
|
||||
}}
|
||||
value={filters.command}
|
||||
exclusive
|
||||
size='small'
|
||||
onChange={handleFilterCommands}
|
||||
>
|
||||
{commands?.map(command => (
|
||||
<ToggleButton key={command} value={command}>
|
||||
{command}
|
||||
</ToggleButton>
|
||||
))}
|
||||
</ToggleButtonGroup>
|
||||
)}
|
||||
</Paper>
|
||||
)
|
||||
}, (prev, next) =>
|
||||
Object.keys(prev.log).length === Object.keys(next.log).length &&
|
||||
prev.filters.command === next.filters.command &&
|
||||
prev.filters.severity === next.filters.severity
|
||||
)
|
||||
|
||||
Filters.propTypes = {
|
||||
filters: PropTypes.shape({
|
||||
command: PropTypes.string,
|
||||
severity: PropTypes.string
|
||||
}),
|
||||
log: PropTypes.object.isRequired,
|
||||
setFilters: PropTypes.func
|
||||
}
|
||||
|
||||
Filters.defaultProps = {
|
||||
filters: {
|
||||
command: undefined,
|
||||
severity: undefined
|
||||
},
|
||||
log: {},
|
||||
setFilters: () => undefined
|
||||
}
|
||||
|
||||
Filters.displayName = 'Filters'
|
||||
|
||||
export default Filters
|
@ -1,13 +1,24 @@
|
||||
import React, { useEffect, useState, memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { makeStyles, Box } from '@material-ui/core'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
import AutoScrollBox from 'client/components/AutoScrollBox'
|
||||
import Message from 'client/components/DebugLog/message'
|
||||
import { DEBUG_LEVEL } from 'client/constants'
|
||||
import MessageList from 'client/components/DebugLog/messagelist'
|
||||
import Filters from 'client/components/DebugLog/filters'
|
||||
import * as LogUtils from 'client/components/DebugLog/utils'
|
||||
|
||||
const debugLogStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
display: 'flex',
|
||||
flexFlow: 'column',
|
||||
height: '100%'
|
||||
},
|
||||
containerScroll: {
|
||||
width: '100%',
|
||||
flexGrow: 1,
|
||||
overflow: 'auto',
|
||||
borderRadius: 5,
|
||||
backgroundColor: '#1d1f21',
|
||||
fontSize: '1.1em',
|
||||
wordBreak: 'break-word',
|
||||
'&::-webkit-scrollbar': {
|
||||
@ -23,62 +34,36 @@ const debugLogStyles = makeStyles(theme => ({
|
||||
}
|
||||
}))
|
||||
|
||||
const DebugLog = memo(({ uuid, socket, logDefault }) => {
|
||||
const DebugLog = memo(({ uuid, socket, logDefault, title }) => {
|
||||
const classes = debugLogStyles()
|
||||
|
||||
const [log, setLog] = useState(logDefault)
|
||||
|
||||
useEffect(() => {
|
||||
uuid && socket.on((socketData = {}) => {
|
||||
const { id, data, command, commandId } = socketData
|
||||
const [filters, setFilters] = useState(() => ({
|
||||
command: undefined,
|
||||
severity: undefined
|
||||
}))
|
||||
|
||||
id === uuid && setLog(prev => ({
|
||||
...prev,
|
||||
[command]: {
|
||||
[commandId]: [...(prev?.[command]?.[commandId] ?? []), data]
|
||||
}
|
||||
}))
|
||||
useEffect(() => {
|
||||
uuid && socket?.on((socketData = {}) => {
|
||||
socketData.id === uuid &&
|
||||
setLog(prevLog => LogUtils.concatNewMessageToLog(prevLog, socketData))
|
||||
})
|
||||
return () => uuid && socket.off()
|
||||
return () => uuid && socket?.off()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Box borderRadius={5} bgcolor={'#1d1f21'} width={1} height={1} className={classes.root}>
|
||||
<AutoScrollBox scrollBehavior="auto">
|
||||
{Object.entries(log)?.map(([command, entries]) =>
|
||||
Object.entries(entries)?.map(([commandId, messages]) =>
|
||||
messages?.map((data, index) => {
|
||||
const key = `${index}-${command}-${commandId}`
|
||||
<div className={classes.root}>
|
||||
{title}
|
||||
|
||||
try {
|
||||
const { timestamp, severity, message } = JSON.parse(data)
|
||||
const decryptMessage = atob(message)
|
||||
<Filters log={log} filters={filters} setFilters={setFilters} />
|
||||
|
||||
return (
|
||||
<Message
|
||||
key={key}
|
||||
timestamp={timestamp}
|
||||
severity={severity}
|
||||
message={decryptMessage}
|
||||
/>
|
||||
)
|
||||
} catch {
|
||||
const severity = data.includes(DEBUG_LEVEL.ERROR)
|
||||
? DEBUG_LEVEL.ERROR
|
||||
: data.includes(DEBUG_LEVEL.INFO)
|
||||
? DEBUG_LEVEL.INFO
|
||||
: data.includes(DEBUG_LEVEL.WARN)
|
||||
? DEBUG_LEVEL.WARN
|
||||
: DEBUG_LEVEL.DEBUG
|
||||
|
||||
return (
|
||||
<Message key={key} severity={severity} message={data} />
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
)}
|
||||
</AutoScrollBox>
|
||||
</Box>
|
||||
<div className={classes.containerScroll}>
|
||||
<AutoScrollBox scrollBehavior="auto">
|
||||
<MessageList log={log} filters={filters} />
|
||||
</AutoScrollBox>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}, (prev, next) => prev.uuid === next.uuid)
|
||||
|
||||
@ -88,7 +73,11 @@ DebugLog.propTypes = {
|
||||
on: PropTypes.func.isRequired,
|
||||
off: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
logDefault: PropTypes.object
|
||||
logDefault: PropTypes.object,
|
||||
title: PropTypes.oneOfType([
|
||||
PropTypes.element,
|
||||
PropTypes.string
|
||||
])
|
||||
}
|
||||
|
||||
DebugLog.defaultProps = {
|
||||
@ -97,9 +86,12 @@ DebugLog.defaultProps = {
|
||||
on: () => undefined,
|
||||
off: () => undefined
|
||||
},
|
||||
logDefault: {}
|
||||
logDefault: {},
|
||||
title: null
|
||||
}
|
||||
|
||||
DebugLog.displayName = 'DebugLog'
|
||||
|
||||
export default DebugLog
|
||||
|
||||
export { LogUtils }
|
||||
|
46
src/fireedge/src/client/components/DebugLog/messagelist.js
Normal file
46
src/fireedge/src/client/components/DebugLog/messagelist.js
Normal file
@ -0,0 +1,46 @@
|
||||
import * as React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import Message from 'client/components/DebugLog/message'
|
||||
import { getMessageInfo } from 'client/components/DebugLog/utils'
|
||||
|
||||
const MessageList = ({ log = {}, filters = {} }) =>
|
||||
Object.entries(log)?.map(([command, entries]) => (
|
||||
// filter by command
|
||||
(!filters.command || filters.command.includes(command)) && (
|
||||
Object.entries(entries)?.map(([commandId, messages]) =>
|
||||
Array.isArray(messages) && messages?.map((data, index) => {
|
||||
const { severity, ...messageInfo } = getMessageInfo(data)
|
||||
|
||||
// filter by severity
|
||||
if (filters.severity && filters.severity !== severity) return null
|
||||
|
||||
const key = `${index}-${command}-${commandId}`
|
||||
|
||||
return (
|
||||
<Message key={key} severity={severity} {...messageInfo} />
|
||||
)
|
||||
})
|
||||
)
|
||||
)
|
||||
))
|
||||
|
||||
MessageList.propTypes = {
|
||||
filters: PropTypes.shape({
|
||||
command: PropTypes.string,
|
||||
severity: PropTypes.string
|
||||
}).isRequired,
|
||||
log: PropTypes.object
|
||||
}
|
||||
|
||||
MessageList.defaultProps = {
|
||||
filters: {
|
||||
command: undefined,
|
||||
severity: undefined
|
||||
},
|
||||
log: undefined
|
||||
}
|
||||
|
||||
MessageList.displayName = 'MessageList'
|
||||
|
||||
export default MessageList
|
55
src/fireedge/src/client/components/DebugLog/utils.js
Normal file
55
src/fireedge/src/client/components/DebugLog/utils.js
Normal file
@ -0,0 +1,55 @@
|
||||
import { DEBUG_LEVEL } from 'client/constants'
|
||||
|
||||
/**
|
||||
* Returns severity type if message text includes debug level
|
||||
* @param {string} data - Message text
|
||||
* @returns {string} Severity type (debug level)
|
||||
*/
|
||||
export const getSeverityFromData = data =>
|
||||
data.includes(DEBUG_LEVEL.ERROR)
|
||||
? DEBUG_LEVEL.ERROR
|
||||
: data.includes(DEBUG_LEVEL.INFO)
|
||||
? DEBUG_LEVEL.INFO
|
||||
: data.includes(DEBUG_LEVEL.WARN)
|
||||
? DEBUG_LEVEL.WARN
|
||||
: DEBUG_LEVEL.DEBUG
|
||||
|
||||
/**
|
||||
* Returns the message information as json
|
||||
* @param {string} data - Message information data as string
|
||||
* @returns {object} Message data
|
||||
*/
|
||||
export const getMessageInfo = (data = '') => {
|
||||
try {
|
||||
const { message, timestamp, severity } = JSON.parse(data)
|
||||
const decryptMessage = atob(message)
|
||||
|
||||
return { timestamp, severity, message: decryptMessage }
|
||||
} catch {
|
||||
const severity = getSeverityFromData(data)
|
||||
|
||||
return { severity, message: data }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new log with a new message concatenated
|
||||
* @param {array} log - Current log data
|
||||
* @param {object} message - New message to concat
|
||||
* @param {string} message.command - Message's command: create, configure, etc
|
||||
* @param {string} message.commandId - Message's command id
|
||||
* @param {string} message.data - Message's information data
|
||||
* @returns {array} New log
|
||||
*/
|
||||
export const concatNewMessageToLog = (log, message = {}) => {
|
||||
if (typeof message !== 'object') return log
|
||||
|
||||
const { data, command, commandId } = message
|
||||
|
||||
return {
|
||||
...log,
|
||||
[command]: {
|
||||
[commandId]: [...(log?.[command]?.[commandId] ?? []), data]
|
||||
}
|
||||
}
|
||||
}
|
@ -4,44 +4,39 @@ import PropTypes from 'prop-types'
|
||||
import { LinearProgress } from '@material-ui/core'
|
||||
|
||||
import { useProvision, useFetch, useSocket } from 'client/hooks'
|
||||
import DebugLog from 'client/components/DebugLog'
|
||||
import DebugLog, { LogUtils } from 'client/components/DebugLog'
|
||||
import * as Types from 'client/types/provision'
|
||||
|
||||
const Log = React.memo(({ hidden, data: { ID } }) => {
|
||||
const Log = React.memo(({ hidden, data: { ID: id } }) => {
|
||||
const { getProvision } = useSocket()
|
||||
const { getProvisionLog } = useProvision()
|
||||
const { data: provisionLog, fetchRequest, loading } = useFetch(getProvisionLog)
|
||||
|
||||
const {
|
||||
data: { uuid = id, log } = {},
|
||||
fetchRequest,
|
||||
loading
|
||||
} = useFetch(getProvisionLog)
|
||||
|
||||
React.useEffect(() => {
|
||||
!hidden && fetchRequest({ id: ID })
|
||||
}, [ID])
|
||||
!hidden && fetchRequest({ id })
|
||||
}, [id])
|
||||
|
||||
React.useEffect(() => {
|
||||
(!provisionLog && !hidden) && fetchRequest({ id: ID })
|
||||
(!log && !hidden) && fetchRequest({ id })
|
||||
}, [hidden])
|
||||
|
||||
const log = provisionLog?.log?.reduce((res, dataLog) => {
|
||||
try {
|
||||
const json = JSON.parse(dataLog)
|
||||
const { data, command, commandId } = json
|
||||
|
||||
return {
|
||||
...res,
|
||||
[command]: {
|
||||
[commandId]: [...(res?.[command]?.[commandId] ?? []), data]
|
||||
}
|
||||
}
|
||||
} catch { return res }
|
||||
}, {})
|
||||
const parsedLog = React.useMemo(() =>
|
||||
log
|
||||
?.map(entry => {
|
||||
try { return JSON.parse(entry) } catch { return entry }
|
||||
})
|
||||
?.reduce(LogUtils.concatNewMessageToLog, {})
|
||||
, [loading])
|
||||
|
||||
return loading ? (
|
||||
<LinearProgress color='secondary' style={{ width: '100%' }} />
|
||||
) : (
|
||||
<DebugLog
|
||||
uuid={provisionLog?.uuid ?? ID}
|
||||
socket={getProvision}
|
||||
logDefault={log}
|
||||
/>
|
||||
<DebugLog uuid={uuid} socket={getProvision} logDefault={parsedLog} />
|
||||
)
|
||||
}, (prev, next) =>
|
||||
prev.hidden === next.hidden && prev.data === next.data)
|
||||
|
@ -8,18 +8,18 @@ import { yupResolver } from '@hookform/resolvers'
|
||||
|
||||
import FormStepper from 'client/components/FormStepper'
|
||||
import Steps from 'client/containers/Provisions/Form/Create/Steps'
|
||||
import FormCreateStyles from 'client/containers/Provisions/Form/Create/styles'
|
||||
import formCreateStyles from 'client/containers/Provisions/Form/Create/styles'
|
||||
import DebugLog from 'client/components/DebugLog'
|
||||
|
||||
import { useProvision, useSocket, useFetch } from 'client/hooks'
|
||||
import { PATH } from 'client/router/provision'
|
||||
import { set, mapUserInputs } from 'client/utils'
|
||||
|
||||
import { Tr, Translate } from 'client/components/HOC'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
function ProvisionCreateForm () {
|
||||
const classes = FormCreateStyles()
|
||||
const classes = formCreateStyles()
|
||||
const history = useHistory()
|
||||
|
||||
const [uuid, setUuid] = useState(undefined)
|
||||
@ -71,19 +71,22 @@ function ProvisionCreateForm () {
|
||||
|
||||
if (uuid) {
|
||||
return (
|
||||
<div className={classes.rootLog}>
|
||||
<div className={classes.titleWrapper}>
|
||||
<IconButton aria-label='back-to-list' size='medium'
|
||||
onClick={() => history.push(PATH.PROVISIONS.LIST)}
|
||||
>
|
||||
<ArrowBackIcon fontSize='large' />
|
||||
</IconButton>
|
||||
<span className={classes.titleText}>
|
||||
<Translate word={T.BackToList} values={T.Provisions} />
|
||||
</span>
|
||||
</div>
|
||||
<DebugLog uuid={uuid} socket={getProvision} />
|
||||
</div>
|
||||
<DebugLog
|
||||
uuid={uuid}
|
||||
socket={getProvision}
|
||||
title={(
|
||||
<div className={classes.titleWrapper}>
|
||||
<IconButton aria-label='back-to-list' size='medium'
|
||||
onClick={() => history.push(PATH.PROVISIONS.LIST)}
|
||||
>
|
||||
<ArrowBackIcon fontSize='large' />
|
||||
</IconButton>
|
||||
<span className={classes.titleText}>
|
||||
<Translate word={T.BackToList} values={T.Provisions} />
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user