feat(xo-web/host): format logs (#5943)

See xoa-support#4100
This commit is contained in:
Mathieu 2021-10-27 15:41:29 +02:00 committed by GitHub
parent d2c5b52bf1
commit 9fe1069df0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 76 additions and 26 deletions

View File

@ -19,6 +19,7 @@
- [Jobs] Fix `job.runSequence` method (PR [#5944](https://github.com/vatesfr/xen-orchestra/pull/5944))
- [Netbox] Fix error when testing plugin on versions older than 2.10 (PR [#5963](https://github.com/vatesfr/xen-orchestra/pull/5963))
- [Snapshot] Fix "Create VM from snapshot" creating a template instead of a VM (PR [#5955](https://github.com/vatesfr/xen-orchestra/pull/5955))
- [Host/Logs] Improve the display of log content (PR [#5943](https://github.com/vatesfr/xen-orchestra/pull/5943))
### Packages to release

View File

@ -1,7 +1,9 @@
import fromCallback from 'promise-toolbox/fromCallback'
import getStream from 'get-stream'
import humanFormat from 'human-format'
import React from 'react'
import ReadableStream from 'readable-stream'
import xml2js from 'xml2js'
import { connect } from 'react-redux'
import { createPredicate } from 'value-matcher'
import { FormattedDate } from 'react-intl'
@ -9,6 +11,7 @@ import {
clone,
every,
forEach,
get,
isEmpty,
isFunction,
isPlainObject,
@ -215,6 +218,28 @@ function safeHumanFormat(value, opts) {
}
}
export const formatLogs = logs =>
Promise.all(
map(logs, ({ body }, id) => {
const matches = /^value:\s*([0-9.]+)\s+config:\s*([^]*)$/.exec(body)
if (matches === null) {
return
}
const [, value, xml] = matches
return fromCallback(xml2js.parseString, xml).then((result = {}) => {
const object = mapValues(result.variable, value => get(value, '[0].$.value'))
if (object.name === undefined) {
return
}
const { name, ...alarmAttributes } = object
return { name, value, alarmAttributes, id }
}, noop)
})
)
export const formatSize = bytes => (bytes != null ? safeHumanFormat(bytes, { scale: 'binary', unit: 'B' }) : 'N/D')
export const formatSizeShort = bytes => safeHumanFormat(bytes, { scale: 'binary', unit: 'B', decimals: 0 })

View File

@ -1,7 +1,6 @@
import _ from 'intl'
import Component from 'base-component'
import decorate from 'apply-decorators'
import fromCallback from 'promise-toolbox/fromCallback'
import { get as getDefined } from '@xen-orchestra/defined'
import Icon from 'icon'
import Link from 'link'
@ -9,14 +8,13 @@ import NoObjects from 'no-objects'
import React from 'react'
import SortedTable from 'sorted-table'
import Tooltip from 'tooltip'
import xml2js from 'xml2js'
import { Network, Sr, Vm } from 'render-xo-item'
import { SelectPool } from 'select-objects'
import { Container, Row, Col } from 'grid'
import { Card, CardHeader, CardBlock } from 'card'
import { FormattedRelative, FormattedTime } from 'react-intl'
import { flatten, forEach, get, includes, isEmpty, map, mapValues } from 'lodash'
import { connectStore, formatSize, noop, resolveIds } from 'utils'
import { flatten, forEach, includes, isEmpty, map } from 'lodash'
import { connectStore, formatLogs, formatSize, noop, resolveIds } from 'utils'
import {
deleteMessage,
deleteMessages,
@ -555,26 +553,7 @@ export default class Health extends Component {
}
_updateAlarms = props => {
Promise.all(
map(props.alertMessages, ({ body }, id) => {
const matches = /^value:\s*([0-9.]+)\s+config:\s*([^]*)$/.exec(body)
if (!matches) {
return
}
const [, value, xml] = matches
return fromCallback(xml2js.parseString, xml).then(result => {
const object = mapValues(result && result.variable, value => get(value, '[0].$.value'))
if (!object || !object.name) {
return
}
const { name, ...alarmAttributes } = object
return { name, value, alarmAttributes, id }
}, noop)
})
).then(formattedMessages => {
formatLogs(props.alertMessages).then(formattedMessages => {
this.setState({
messages: map(formattedMessages, ({ id, ...formattedMessage }) => ({
formatted: formattedMessage,

View File

@ -6,7 +6,13 @@ import SortedTable from 'sorted-table'
import { createPager } from 'selectors'
import { Row, Col } from 'grid'
import { deleteMessage, deleteMessages } from 'xo'
import { formatLogs } from 'utils'
import { FormattedRelative, FormattedTime } from 'react-intl'
import { map } from 'lodash'
const LOG_BODY_STYLE = {
whiteSpace: 'pre-wrap',
}
const LOG_COLUMNS = [
{
@ -34,7 +40,26 @@ const LOG_COLUMNS = [
},
{
name: _('logContent'),
itemRenderer: log => log.body,
itemRenderer: ({ formatted, body }) =>
formatted !== undefined ? (
<div>
<Row>
<Col mediumSize={6}>
<strong>{formatted.name}</strong>
</Col>
<Col mediumSize={6}>{formatted.value}</Col>
</Row>
<br />
{map(formatted.alarmAttributes, (value, label) => (
<Row key={label}>
<Col mediumSize={6}>{label}</Col>
<Col mediumSize={6}>{value}</Col>
</Row>
))}
</div>
) : (
<pre style={LOG_BODY_STYLE}>{body}</pre>
),
sortCriteria: log => log.body,
},
]
@ -55,7 +80,7 @@ export default class TabLogs extends Component {
super()
this.getLogs = createPager(
() => this.props.logs,
() => this.state.logs,
() => this.state.page,
10
)
@ -65,6 +90,26 @@ export default class TabLogs extends Component {
}
}
componentDidMount() {
this._formatLogs(this.props.logs)
}
componentDidUpdate(props) {
if (props.logs !== this.props.logs) {
this._formatLogs(this.props.logs)
}
}
_formatLogs = logs =>
formatLogs(logs).then(formattedLogs => {
this.setState({
logs: map(formattedLogs, ({ id, ...formattedLogs }) => ({
formatted: formattedLogs,
...logs[id],
})),
})
})
_nextPage = () => this.setState({ page: this.state.page + 1 })
_previousPage = () => this.setState({ page: this.state.page - 1 })