Files
xen-orchestra/src/logs-cli.js
2016-06-24 18:33:43 +02:00

203 lines
4.6 KiB
JavaScript

import appConf from 'app-conf'
import get from 'lodash/get'
import highland from 'highland'
import levelup from 'level-party'
import ndjson from 'ndjson'
import parseArgs from 'minimist'
import sublevel from 'level-sublevel'
import util from 'util'
import { repair as repairDb } from 'leveldown'
import {forEach} from './utils'
import globMatcher from './glob-matcher'
// ===================================================================
async function printLogs (db, args) {
let stream = highland(db.createReadStream({reverse: true}))
if (args.since) {
stream = stream.filter(({value}) => (value.time >= args.since))
}
if (args.until) {
stream = stream.filter(({value}) => (value.time <= args.until))
}
const fields = Object.keys(args.matchers)
if (fields.length > 0) {
stream = stream.filter(({value}) => {
for (const field of fields) {
const fieldValue = get(value, field)
if (fieldValue === undefined || !args.matchers[field](fieldValue)) {
return false
}
}
return true
})
}
stream = stream.take(args.limit)
if (args.json) {
stream = highland(stream.pipe(ndjson.serialize()))
.each(value => {
process.stdout.write(value)
})
} else {
stream = stream.each(value => {
console.log(util.inspect(value, { depth: null }))
})
}
return new Promise(resolve => {
stream.done(resolve)
})
}
// ===================================================================
function helper () {
console.error(`
xo-server-logs --help, -h
Display this help message.
xo-server-logs [--json] [--limit=<limit>] [--since=<date>] [--until=<date>] [<pattern>...]
Prints the logs.
--json
Display the results as new line delimited JSON for consumption
by another program.
--limit=<limit>, -n <limit>
Limit the number of results to be displayed (default 100)
--since=<date>, --until=<date>
Start showing entries on or newer than the specified date, or on
or older than the specified date.
<date> should use the format \`YYYY-MM-DD\`.
<pattern>
Patterns can be used to filter the entries.
Patterns have the following format \`<field>=<value>\`/\`<field>\`.
xo-server-logs --repair
Repair/compact the database.
This is an advanced operation and should be used only when necessary and offline (xo-server should be stopped).
`)
}
// ===================================================================
function getArgs () {
const stringArgs = ['since', 'until', 'limit']
const args = parseArgs(process.argv.slice(2), {
string: stringArgs,
boolean: ['help', 'json', 'repair'],
default: {
limit: 100,
json: false,
help: false
},
alias: {
limit: 'n',
help: 'h'
}
})
const patterns = {}
for (let value of args._) {
value = String(value)
const i = value.indexOf('=')
if (i !== -1) {
const field = value.slice(0, i)
const pattern = value.slice(i + 1)
patterns[pattern]
? patterns[field].push(pattern)
: patterns[field] = [ pattern ]
} else if (!patterns[value]) {
patterns[value] = null
}
}
const trueFunction = () => true
args.matchers = {}
for (const field in patterns) {
const values = patterns[field]
args.matchers[field] = (values === null) ? trueFunction : globMatcher(values)
}
// Warning: minimist makes one array of values if the same option is used many times.
// (But only for strings args, not boolean)
forEach(stringArgs, arg => {
if (args[arg] instanceof Array) {
throw new Error(`error: too many values for ${arg} argument`)
}
})
;['since', 'until'].forEach(arg => {
if (args[arg] !== undefined) {
args[arg] = Date.parse(args[arg])
if (isNaN(args[arg])) {
throw new Error(`error: bad ${arg} timestamp format`)
}
}
})
if (isNaN(args.limit = +args.limit)) {
throw new Error('error: limit is not a valid number')
}
return args
}
// ===================================================================
export default async function main () {
const args = getArgs()
if (args.help) {
helper()
return
}
const config = await appConf.load('xo-server', {
ignoreUnknownFormats: true
})
if (args.repair) {
await new Promise((resolve, reject) => {
repairDb(`${config.datadir}/leveldb`, error => {
if (error) {
reject(error)
} else {
resolve()
}
})
})
return
}
const db = sublevel(levelup(
`${config.datadir}/leveldb`,
{ valueEncoding: 'json' }
)).sublevel('logs')
return printLogs(db, args)
}