UNPKG

backtrace-morgue

Version:

command line interface to the Backtrace object store

220 lines (191 loc) 5.49 kB
const client = require('./client'); function validateOne(option, value) { if (!value) { console.error(`--${ option } is required`); return false; } if (Array.isArray(value)) { console.error(`--${ option } must have exactly one value`); return false; } return true; } function validateZeroOrOne(option, value) { if (Array.isArray(value)) { console.error(`--${ option } must have at most one value`); return false; } return true; } class MetricsImporterCli { constructor(client) { this.client = client; } async routeMethod(args) { const routes = { 'importer': { create: (args) => this.importerCreate(args), }, source: { 'check-query': (args) => this.sourceCheckQuery(args), }, 'logs': (args) => this.logs(args), }; let route = routes; let next; while (typeof route === 'object') { next = args._[0]; args._.shift(); if (!next) { route = null; break; } route = route[next]; } if (!route) { console.error("Unrecognized command. See metrics-importer help for usage"); return; } await route(args); } help() { console.warn(` usage: morgue metrics-import [<entity>] <command> <options> Command may be one of the following: source check-query: Run a test query against a given source. importer create: create an importer. logs: Get logs by source id or importer id. For full documentation, please see: https://github.com/backtrace-labs/backtrace-morgue `); } async logs(args) { const project = args.project; const sourceId = args['source']; const importerId = args['importer']; let limit = 100; if (!project) { console.log("Project is required"); return; } if (!validateZeroOrOne('source', sourceId) || !validateZeroOrOne('importer', importerId)) { return; } if (!sourceId && !importerId) { console.error("Specify either --source or --importer, not both"); return; } if ('limit' in args) { limit = Number.parseInt(args.limit); if (Number.isNaN(limit) || limit <= 0) { console.error("--limit must be a positive integer"); return; } } const response = await this.client.logs({ project, sourceId, importerId, limit }); if (response.messages.length === 0) { console.log("No logs"); return; } /* * The service gives us messages most recent first, but we want to display * most recent last. */ const sorted = response.messages.reverse(); for (let m of sorted) { const time = (new Date(m.time * 1000)).toISOString(); let msg; if (importerId) { msg = `${ time } ${ m.message }`; } else { msg = `${ time } importer=${ m.sourceId } ${ m.message }`; } msg = `${ m.level.padEnd(9) }${ msg }`; console.log(msg); } } async importerCreate(args) { const project = args.project; const sourceId = args["source"]; const query = args.query; const startAtUnparsed = args["start-at"]; const delay = args.delay; const metric = args.metric; const metricGroup = args['metric-group']; const name = args.name; if (!project) { console.error("Project is required"); return; } if (!validateOne("source", sourceId) || !validateOne("name", name) || !validateOne("metric", metric) || !validateOne("metric-group", metricGroup) || !validateZeroOrOne("delay", delay) || !validateZeroOrOne("start-at", startAtUnparsed)) { return; } /* By default, scrape the last day. */ let startAt = new Date(Date.now() - 24 * 60 * 1000); if (startAtUnparsed) { startAt = new Date(startAtUnparsed); if (Number.isNaN(startAt.getTime())) { console.error("Unable to parse --start-at"); return; } } const params = { project, sourceId, name, metric, metricGroup, query, startAt: Math.floor(startAt.getTime() / 1000), delay: delay !== undefined ? delay : 60, }; const resp = await this.client.createImporter(params); console.log(`Importer id ${ resp.id }`); } async sourceCheckQuery(args) { const project = args.project; const sourceId = args['source']; const query = args.query; if (!project) { console.error("Project is required"); return; } if (!validateOne("source", sourceId) || !validateOne("query", query)) { return; } const resp = await this.client.checkSource({ project, sourceId, query }); if (resp.errors.length) { console.log("Errors:"); resp.errors.map(i => console.log(i)); console.log(""); } if (resp.warnings.length) { console.log("Warnings:"); resp.warnings.map(i => console.log(i)); console.log(""); } if (resp.success) { console.log( "This query can be used as a valid importer query for this source"); } else { console.log(`If used to create an importer, this query will be unable to complete scrapes successfully.`); } } } async function metricsImporterCliFromCoroner(coroner) { const c = await client.metricsImporterClientFromCoroner(coroner); return new MetricsImporterCli(c); } module.exports = { MetricsImporterCli, metricsImporterCliFromCoroner };