diff --git a/src/apps.js b/src/apps.js index ee7d4cbba..f6bbb8713 100644 --- a/src/apps.js +++ b/src/apps.js @@ -822,6 +822,9 @@ function getLogs(appId, options, callback) { format = options.format || 'json', follow = !!options.follow; + assert.strictEqual(typeof lines, 'number'); + assert.strictEqual(typeof format, 'string'); + var args = [ '--lines=' + lines ]; if (follow) args.push('--follow'); args.push(path.join(paths.LOG_DIR, appId, 'app.log')); diff --git a/src/cloudron.js b/src/cloudron.js index ec780b307..200a4189d 100644 --- a/src/cloudron.js +++ b/src/cloudron.js @@ -327,42 +327,40 @@ function checkDiskSpace(callback) { }); } -function getLogs(options, callback) { +function getLogs(unit, options, callback) { + assert.strictEqual(typeof unit, 'string'); assert(options && typeof options === 'object'); assert.strictEqual(typeof callback, 'function'); - var units = options.units || [], - lines = options.lines || 100, + var lines = options.lines || 100, format = options.format || 'json', follow = !!options.follow; - assert(Array.isArray(units)); assert.strictEqual(typeof lines, 'number'); assert.strictEqual(typeof format, 'string'); - debug('Getting logs for %j', units); + assert.strictEqual(typeof lines, 'number'); + assert.strictEqual(typeof format, 'string'); - var args = [ '--no-pager', '--lines=' + lines ]; - units.forEach(function (u) { - if (u === 'box') args.push('--unit=box'); - else if (u === 'mail') args.push('CONTAINER_NAME=mail'); - }); - if (format === 'short') args.push('--output=short', '-a'); else args.push('--output=json'); + debug('Getting logs for %s as %s', unit, format); + + var args = [ '--lines=' + lines ]; if (follow) args.push('--follow'); + args.push(path.join(paths.LOG_DIR, unit, 'app.log')); - var cp = spawn('/bin/journalctl', args); + var cp = spawn('/usr/bin/tail', args); var transformStream = split(function mapper(line) { if (format !== 'json') return line + '\n'; - var obj = safe.JSON.parse(line); - if (!obj) return undefined; + var data = line.split(' '); // logs are + var timestamp = (new Date(data[0])).getTime(); + if (isNaN(timestamp)) timestamp = 0; return JSON.stringify({ - realtimeTimestamp: obj.__REALTIME_TIMESTAMP, - monotonicTimestamp: obj.__MONOTONIC_TIMESTAMP, - message: obj.MESSAGE, - source: obj.SYSLOG_IDENTIFIER || '' + realtimeTimestamp: timestamp * 1000, + message: line.slice(data[0].length+1), + source: unit }) + '\n'; }); diff --git a/src/routes/cloudron.js b/src/routes/cloudron.js index 1e12859ab..fa2428bc6 100644 --- a/src/routes/cloudron.js +++ b/src/routes/cloudron.js @@ -97,19 +97,18 @@ function feedback(req, res, next) { } function getLogs(req, res, next) { + assert.strictEqual(typeof req.params.unit, 'string'); + var lines = req.query.lines ? parseInt(req.query.lines, 10) : 100; if (isNaN(lines)) return next(new HttpError(400, 'lines must be a number')); - var units = req.query.units || 'all'; - var options = { lines: lines, follow: false, - units: units.split(','), format: req.query.format }; - cloudron.getLogs(options, function (error, logStream) { + cloudron.getLogs(req.params.unit, options, function (error, logStream) { if (error && error.reason === CloudronError.BAD_FIELD) return next(new HttpError(404, 'Invalid type')); if (error) return next(new HttpError(500, error)); @@ -124,11 +123,11 @@ function getLogs(req, res, next) { } function getLogStream(req, res, next) { + assert.strictEqual(typeof req.params.unit, 'string'); + var lines = req.query.lines ? parseInt(req.query.lines, 10) : -10; // we ignore last-event-id if (isNaN(lines)) return next(new HttpError(400, 'lines must be a valid number')); - var units = req.query.units || 'all'; - function sse(id, data) { return 'id: ' + id + '\ndata: ' + data + '\n\n'; } if (req.headers.accept !== 'text/event-stream') return next(new HttpError(400, 'This API call requires EventStream')); @@ -136,11 +135,10 @@ function getLogStream(req, res, next) { var options = { lines: lines, follow: true, - units: units.split(','), format: req.query.format }; - cloudron.getLogs(options, function (error, logStream) { + cloudron.getLogs(req.params.unit, options, function (error, logStream) { if (error && error.reason === CloudronError.BAD_FIELD) return next(new HttpError(404, 'Invalid type')); if (error) return next(new HttpError(500, error)); diff --git a/src/server.js b/src/server.js index 606c5f278..bb506853b 100644 --- a/src/server.js +++ b/src/server.js @@ -120,8 +120,8 @@ function initializeExpressSync() { router.post('/api/v1/cloudron/reboot', cloudronScope, routes.cloudron.reboot); router.get ('/api/v1/cloudron/graphs', cloudronScope, routes.graphs.getGraphs); router.get ('/api/v1/cloudron/disks', cloudronScope, routes.cloudron.getDisks); - router.get ('/api/v1/cloudron/logs', cloudronScope, routes.cloudron.getLogs); - router.get ('/api/v1/cloudron/logstream', cloudronScope, routes.cloudron.getLogStream); + router.get ('/api/v1/cloudron/logs/:unit', cloudronScope, routes.cloudron.getLogs); + router.get ('/api/v1/cloudron/logstream/:unit', cloudronScope, routes.cloudron.getLogStream); router.get ('/api/v1/cloudron/ssh/authorized_keys', cloudronScope, routes.ssh.getAuthorizedKeys); router.put ('/api/v1/cloudron/ssh/authorized_keys', cloudronScope, routes.ssh.addAuthorizedKey); router.get ('/api/v1/cloudron/ssh/authorized_keys/:identifier', cloudronScope, routes.ssh.getAuthorizedKey);