refactor tail invokation into logtail.sh
This commit is contained in:
+3
-13
@@ -164,7 +164,7 @@ const appstore = require('./appstore.js'),
|
||||
domains = require('./domains.js'),
|
||||
eventlog = require('./eventlog.js'),
|
||||
fs = require('fs'),
|
||||
LogStream = require('./log-stream.js'),
|
||||
logs = require('./logs.js'),
|
||||
mail = require('./mail.js'),
|
||||
manifestFormat = require('cloudron-manifestformat'),
|
||||
mounts = require('./mounts.js'),
|
||||
@@ -179,7 +179,6 @@ const appstore = require('./appstore.js'),
|
||||
services = require('./services.js'),
|
||||
settings = require('./settings.js'),
|
||||
shell = require('./shell.js'),
|
||||
spawn = require('child_process').spawn,
|
||||
storage = require('./storage.js'),
|
||||
superagent = require('superagent'),
|
||||
system = require('./system.js'),
|
||||
@@ -2025,19 +2024,10 @@ async function getLogs(app, options) {
|
||||
|
||||
const appId = app.id;
|
||||
|
||||
const lines = options.lines === -1 ? '+1' : options.lines,
|
||||
format = options.format || 'json',
|
||||
follow = options.follow;
|
||||
|
||||
assert.strictEqual(typeof format, 'string');
|
||||
|
||||
const args = [ '--lines=' + lines ];
|
||||
if (follow) args.push('--follow', '--retry', '--quiet'); // same as -F. to make it work if file doesn't exist, --quiet to not output file headers, which are no logs
|
||||
|
||||
const logPaths = await getLogPaths(app);
|
||||
const cp = spawn('/usr/bin/tail', args.concat(logPaths));
|
||||
const cp = logs.tail(logPaths, { lines: options.lines, follow: options.follow });
|
||||
|
||||
const logStream = new LogStream({ format, source: appId });
|
||||
const logStream = new logs.LogStream({ format: options.format || 'json', source: appId });
|
||||
logStream.close = cp.kill.bind(cp, 'SIGKILL'); // hook for caller. closing stream kills the child process
|
||||
|
||||
cp.stdout.pipe(logStream);
|
||||
|
||||
+7
-19
@@ -41,7 +41,7 @@ const apps = require('./apps.js'),
|
||||
eventlog = require('./eventlog.js'),
|
||||
execSync = require('child_process').execSync,
|
||||
fs = require('fs'),
|
||||
LogStream = require('./log-stream.js'),
|
||||
logs = require('./logs.js'),
|
||||
mail = require('./mail.js'),
|
||||
notifications = require('./notifications.js'),
|
||||
oidc = require('./oidc.js'),
|
||||
@@ -53,7 +53,6 @@ const apps = require('./apps.js'),
|
||||
services = require('./services.js'),
|
||||
settings = require('./settings.js'),
|
||||
shell = require('./shell.js'),
|
||||
spawn = require('child_process').spawn,
|
||||
sysinfo = require('./sysinfo.js'),
|
||||
tasks = require('./tasks.js'),
|
||||
users = require('./users.js');
|
||||
@@ -217,27 +216,16 @@ async function getLogs(unit, options) {
|
||||
assert.strictEqual(typeof unit, 'string');
|
||||
assert(options && typeof options === 'object');
|
||||
|
||||
assert.strictEqual(typeof options.lines, 'number');
|
||||
assert.strictEqual(typeof options.format, 'string');
|
||||
assert.strictEqual(typeof options.follow, 'boolean');
|
||||
debug(`Getting logs for ${unit}`);
|
||||
|
||||
const lines = options.lines === -1 ? '+1' : options.lines,
|
||||
format = options.format || 'json',
|
||||
follow = options.follow;
|
||||
|
||||
debug('Getting logs for %s as %s', unit, format);
|
||||
|
||||
let args = [ '--lines=' + lines ];
|
||||
if (follow) args.push('--follow');
|
||||
|
||||
// need to handle box.log without subdir
|
||||
if (unit === 'box') args.push(path.join(paths.LOG_DIR, 'box.log'));
|
||||
else if (unit.startsWith('crash-')) args.push(path.join(paths.CRASH_LOG_DIR, unit.slice(6) + '.log'));
|
||||
let logFile = '';
|
||||
if (unit === 'box') logFile = path.join(paths.LOG_DIR, 'box.log'); // box.log is at the top
|
||||
else if (unit.startsWith('crash-')) logFile = path.join(paths.CRASH_LOG_DIR, unit.slice(6) + '.log');
|
||||
else throw new BoxError(BoxError.BAD_FIELD, `No such unit '${unit}'`);
|
||||
|
||||
const cp = spawn('/usr/bin/tail', args);
|
||||
const cp = logs.tail([logFile], { lines: options.lines, follow: options.follow });
|
||||
|
||||
const logStream = new LogStream({ format, source: unit });
|
||||
const logStream = new logs.LogStream({ format: options.format || 'json', source: unit });
|
||||
logStream.close = cp.kill.bind(cp, 'SIGKILL'); // hook for caller. closing stream kills the child process
|
||||
|
||||
cp.stdout.pipe(logStream);
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
const stream = require('stream'),
|
||||
const assert = require('assert'),
|
||||
path = require('path'),
|
||||
spawn = require('child_process').spawn,
|
||||
stream = require('stream'),
|
||||
{ StringDecoder } = require('string_decoder'),
|
||||
TransformStream = stream.Transform;
|
||||
|
||||
const LOGTAIL_CMD = path.join(__dirname, 'scripts/logtail.sh');
|
||||
|
||||
class LogStream extends TransformStream {
|
||||
constructor(options) {
|
||||
super();
|
||||
@@ -49,4 +54,36 @@ class LogStream extends TransformStream {
|
||||
}
|
||||
}
|
||||
|
||||
exports = module.exports = LogStream;
|
||||
function tail(filePaths, options) {
|
||||
assert(Array.isArray(filePaths));
|
||||
assert.strictEqual(typeof options, 'object');
|
||||
|
||||
const lines = options.lines === -1 ? '+1' : options.lines,
|
||||
follow = options.follow;
|
||||
|
||||
const args = [ '--lines=' + lines ];
|
||||
if (follow) args.push('--follow');
|
||||
|
||||
return spawn(LOGTAIL_CMD, args.concat(filePaths));
|
||||
}
|
||||
|
||||
function journalctl(unit, options) {
|
||||
assert.strictEqual(typeof unit, 'string');
|
||||
assert.strictEqual(typeof options, 'object');
|
||||
|
||||
const args = [];
|
||||
args.push('--lines=' + (options.lines === -1 ? 'all' : options.lines));
|
||||
args.push(`--unit=${unit}`);
|
||||
args.push('--no-pager');
|
||||
args.push('--output=short-iso');
|
||||
|
||||
if (options.follow) args.push('--follow');
|
||||
|
||||
return spawn('journalctl', args);
|
||||
}
|
||||
|
||||
exports = module.exports = {
|
||||
tail,
|
||||
journalctl,
|
||||
LogStream
|
||||
};
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
|
||||
args=$(getopt -o "" -l "follow,lines:" -n "$0" -- "$@")
|
||||
eval set -- "${args}"
|
||||
|
||||
follow=""
|
||||
lines=""
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
--follow) follow="--follow --retry --quiet"; shift;; # same as -F. to make it work if file doesn't exist, --quiet to not output file headers, which are no logs
|
||||
--lines) lines="$2"; shift 2;;
|
||||
--) break;;
|
||||
*) echo "Unknown option $1"; exit 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
exec tail ${follow} --lines=${lines} "$@"
|
||||
+6
-34
@@ -45,7 +45,7 @@ const addonConfigs = require('./addonconfigs.js'),
|
||||
hat = require('./hat.js'),
|
||||
http = require('http'),
|
||||
infra = require('./infra_version.js'),
|
||||
LogStream = require('./log-stream.js'),
|
||||
logs = require('./logs.js'),
|
||||
mail = require('./mail.js'),
|
||||
os = require('os'),
|
||||
path = require('path'),
|
||||
@@ -57,7 +57,6 @@ const addonConfigs = require('./addonconfigs.js'),
|
||||
settings = require('./settings.js'),
|
||||
sftp = require('./sftp.js'),
|
||||
shell = require('./shell.js'),
|
||||
spawn = require('child_process').spawn,
|
||||
superagent = require('superagent'),
|
||||
system = require('./system.js');
|
||||
|
||||
@@ -425,10 +424,6 @@ async function getServiceLogs(id, options) {
|
||||
assert.strictEqual(typeof id, 'string');
|
||||
assert(options && typeof options === 'object');
|
||||
|
||||
assert.strictEqual(typeof options.lines, 'number');
|
||||
assert.strictEqual(typeof options.format, 'string');
|
||||
assert.strictEqual(typeof options.follow, 'boolean');
|
||||
|
||||
const [name, instance ] = id.split(':');
|
||||
|
||||
if (instance) {
|
||||
@@ -439,41 +434,18 @@ async function getServiceLogs(id, options) {
|
||||
|
||||
debug(`Getting logs for ${name}`);
|
||||
|
||||
const lines = options.lines,
|
||||
format = options.format || 'json',
|
||||
follow = options.follow;
|
||||
let cp;
|
||||
|
||||
let cmd, args = [];
|
||||
|
||||
// docker and unbound use journald
|
||||
if (name === 'docker' || name === 'unbound') {
|
||||
cmd = 'journalctl';
|
||||
|
||||
args.push('--lines=' + (lines === -1 ? 'all' : lines));
|
||||
args.push(`--unit=${name}`);
|
||||
args.push('--no-pager');
|
||||
args.push('--output=short-iso');
|
||||
|
||||
if (follow) args.push('--follow');
|
||||
cp = logs.journalctl(name, options);
|
||||
} else if (name === 'nginx') {
|
||||
cmd = '/usr/bin/tail';
|
||||
|
||||
args.push('--lines=' + (lines === -1 ? '+1' : lines));
|
||||
if (follow) args.push('--follow', '--retry', '--quiet'); // same as -F. to make it work if file doesn't exist, --quiet to not output file headers, which are no logs
|
||||
args.push('/var/log/nginx/access.log');
|
||||
args.push('/var/log/nginx/error.log');
|
||||
cp = logs.tail(['/var/log/nginx/access.log', '/var/log/nginx/error.log'], { lines: options.lines, follow: options.follow });
|
||||
} else {
|
||||
cmd = '/usr/bin/tail';
|
||||
|
||||
args.push('--lines=' + (lines === -1 ? '+1' : lines));
|
||||
if (follow) args.push('--follow', '--retry', '--quiet'); // same as -F. to make it work if file doesn't exist, --quiet to not output file headers, which are no logs
|
||||
const containerName = APP_SERVICES[name] ? `${name}-${instance}` : name;
|
||||
args.push(path.join(paths.LOG_DIR, containerName, 'app.log'));
|
||||
cp = logs.tail([path.join(paths.LOG_DIR, containerName, 'app.log')], { lines: options.lines, follow: options.follow });
|
||||
}
|
||||
|
||||
const cp = spawn(cmd, args);
|
||||
|
||||
const logStream = new LogStream({ format, source: name });
|
||||
const logStream = new logs.LogStream({ format: options.format || 'json', source: name });
|
||||
logStream.close = cp.kill.bind(cp, 'SIGKILL'); // closing stream kills the child process
|
||||
|
||||
cp.stdout.pipe(logStream);
|
||||
|
||||
+3
-15
@@ -46,12 +46,11 @@ const assert = require('assert'),
|
||||
BoxError = require('./boxerror.js'),
|
||||
database = require('./database.js'),
|
||||
debug = require('debug')('box:tasks'),
|
||||
LogStream = require('./log-stream.js'),
|
||||
logs = require('./logs.js'),
|
||||
path = require('path'),
|
||||
paths = require('./paths.js'),
|
||||
safe = require('safetydance'),
|
||||
shell = require('./shell.js'),
|
||||
spawn = require('child_process').spawn,
|
||||
_ = require('underscore');
|
||||
|
||||
let gTasks = {}; // indexed by task id
|
||||
@@ -269,19 +268,8 @@ function getLogs(taskId, options) {
|
||||
|
||||
debug(`Getting logs for ${taskId}`);
|
||||
|
||||
const lines = options.lines === -1 ? '+1' : options.lines,
|
||||
format = options.format || 'json',
|
||||
follow = options.follow;
|
||||
|
||||
const cmd = '/usr/bin/tail';
|
||||
let args = [ '--lines=' + lines ];
|
||||
|
||||
if (follow) args.push('--follow', '--retry', '--quiet'); // same as -F. to make it work if file doesn't exist, --quiet to not output file headers, which are no logs
|
||||
args.push(`${paths.TASKS_LOG_DIR}/${taskId}.log`);
|
||||
|
||||
const cp = spawn(cmd, args);
|
||||
|
||||
const logStream = new LogStream({ format, source: taskId });
|
||||
const cp = logs.tail([`${paths.TASKS_LOG_DIR}/${taskId}.log`], { lines: options.lines, follow: options.follow });
|
||||
const logStream = new logs.LogStream({ format: options.format || 'json', source: taskId });
|
||||
logStream.close = cp.kill.bind(cp, 'SIGKILL'); // hook for caller. closing stream kills the child process
|
||||
|
||||
cp.stdout.pipe(logStream);
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
|
||||
const expect = require('expect.js'),
|
||||
fs = require('fs'),
|
||||
LogStream = require('../log-stream.js'),
|
||||
logs = require('../logs.js'),
|
||||
stream = require('stream');
|
||||
|
||||
describe('log stream', function () {
|
||||
it('can create stream', function (done) {
|
||||
fs.writeFileSync('/tmp/test-input.log', '2022-10-09T15:19:48.740Z message', 'utf8');
|
||||
const input = fs.createReadStream('/tmp/test-input.log');
|
||||
const log = new LogStream({ format: 'json', source: 'test' });
|
||||
const log = new logs.LogStream({ format: 'json', source: 'test' });
|
||||
const output = fs.createWriteStream('/tmp/test-output.log');
|
||||
|
||||
stream.pipeline(input, log, output, function (error) {
|
||||
Reference in New Issue
Block a user