Build script
A build process that generates a package with single executable for each component.
This commit is contained in:
1 parent
819e8a52a3
commit
0b59811924
23 files changed
+734
-688
No files matched your search
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"node": true
|
||||
}
|
||||
}
|
||||
+7
-83
@@ -20,44 +20,10 @@
|
||||
|
||||
const cluster = require("cluster");
|
||||
const logger = require("./logger");
|
||||
const config = require("./config");
|
||||
|
||||
const VERSION = require("../package.json").version;
|
||||
|
||||
let respawnTimestamp = 0;
|
||||
let tooManyCrashesTimestamp = 0;
|
||||
|
||||
function getDependencyVersions() {
|
||||
const dependencies = [`node@${process.versions.node}`];
|
||||
try {
|
||||
const npmls = JSON.parse(
|
||||
require("child_process").execSync("npm ls --prod --json --depth 0")
|
||||
);
|
||||
|
||||
for (const k in npmls.dependencies)
|
||||
dependencies.push(`${k}@${npmls.dependencies[k].version}`);
|
||||
} catch (err) {
|
||||
// Continue regardless of error
|
||||
}
|
||||
|
||||
return dependencies.join(",");
|
||||
}
|
||||
|
||||
function getConfig() {
|
||||
const conf = [];
|
||||
const keys = Object.keys(process.env);
|
||||
for (const k of keys) {
|
||||
if (k.startsWith("GENIEACS_")) {
|
||||
const name = k.slice(9);
|
||||
const value = process.env[k];
|
||||
const def = config.getDefault(name);
|
||||
if (def == null || value !== `${def}`) conf.push(`${name}=${value}`);
|
||||
}
|
||||
}
|
||||
|
||||
return conf.join(",");
|
||||
}
|
||||
|
||||
function restartWorker(worker, code, signal) {
|
||||
const msg = {
|
||||
message: "Worker died",
|
||||
@@ -96,24 +62,12 @@ function restartWorker(worker, code, signal) {
|
||||
}, respawnTimestamp - now);
|
||||
}
|
||||
|
||||
function start(service) {
|
||||
logger.init(service, VERSION);
|
||||
const SERVICE_ADDRESS = config.get(`${service.toUpperCase()}_INTERFACE`);
|
||||
const SERVICE_PORT = config.get(`${service.toUpperCase()}_PORT`);
|
||||
|
||||
logger.info({
|
||||
message: `GenieACS (genieacs-${service}) starting`,
|
||||
pid: process.pid,
|
||||
version: VERSION,
|
||||
dependencies: getDependencyVersions(),
|
||||
config: getConfig()
|
||||
});
|
||||
|
||||
function start(workerCount, servicePort, serviceAddress) {
|
||||
cluster.on("listening", (worker, address) => {
|
||||
if (
|
||||
(address.addressType === 4 || address.addressType === 6) &&
|
||||
address.address === SERVICE_ADDRESS &&
|
||||
address.port === SERVICE_PORT
|
||||
address.address === serviceAddress &&
|
||||
address.port === servicePort
|
||||
) {
|
||||
logger.info({
|
||||
message: "Worker listening",
|
||||
@@ -126,46 +80,16 @@ function start(service) {
|
||||
|
||||
cluster.on("exit", restartWorker);
|
||||
|
||||
process.on("SIGINT", () => {
|
||||
logger.info({
|
||||
message: "Received signal SIGINT, exiting",
|
||||
pid: process.pid
|
||||
});
|
||||
|
||||
cluster.removeListener("exit", restartWorker);
|
||||
});
|
||||
|
||||
process.on("SIGTERM", () => {
|
||||
logger.info({
|
||||
message: "Received signal SIGTERM, exiting",
|
||||
pid: process.pid
|
||||
});
|
||||
|
||||
cluster.removeListener("exit", restartWorker);
|
||||
for (const pid in cluster.workers) cluster.workers[pid].kill();
|
||||
});
|
||||
|
||||
cluster.setupMaster({
|
||||
exec: "lib/server",
|
||||
args: [service]
|
||||
});
|
||||
|
||||
let workerCount = config.get(`${service.toUpperCase()}_WORKER_PROCESSES`);
|
||||
|
||||
if (!workerCount) workerCount = Math.max(2, require("os").cpus().length);
|
||||
|
||||
for (let i = 0; i < workerCount; ++i) cluster.fork();
|
||||
}
|
||||
|
||||
process.on("uncaughtException", err => {
|
||||
logger.error({
|
||||
message: "Uncaught exception in master process, exiting",
|
||||
exception: err,
|
||||
pid: process.pid
|
||||
});
|
||||
process.exitCode = 1;
|
||||
function stop() {
|
||||
cluster.removeListener("exit", restartWorker);
|
||||
for (const pid in cluster.workers) cluster.workers[pid].kill();
|
||||
});
|
||||
}
|
||||
|
||||
exports.start = start;
|
||||
exports.stop = stop;
|
||||
exports.worker = cluster.worker;
|
||||
+18
-2
@@ -19,6 +19,7 @@
|
||||
"use strict";
|
||||
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
|
||||
const options = {
|
||||
CONFIG_DIR: { type: "path", default: "config" },
|
||||
@@ -52,6 +53,13 @@ const options = {
|
||||
FS_LOG_FILE: { type: "path", default: "" },
|
||||
FS_ACCESS_LOG_FILE: { type: "path", default: "" },
|
||||
|
||||
UI_WORKER_PROCESSES: { type: "int", default: 0 },
|
||||
UI_PORT: { type: "int", default: 3000 },
|
||||
UI_INTERFACE: { type: "string", default: "0.0.0.0" },
|
||||
UI_SSL: { type: "bool", default: false },
|
||||
UI_LOG_FILE: { type: "path", default: "" },
|
||||
UI_ACCESS_LOG_FILE: { type: "path", default: "" },
|
||||
|
||||
UDP_CONNECTION_REQUEST_PORT: { type: "int", default: 0 },
|
||||
|
||||
DOWNLOAD_TIMEOUT: { type: "int", default: 3600 },
|
||||
@@ -175,7 +183,9 @@ for (const [k, v] of Object.entries(process.env))
|
||||
setConfig("CONFIG_DIR", options["CONFIG_DIR"]["default"]);
|
||||
|
||||
// Configuration file
|
||||
const configFile = require(path.resolve(allConfig.CONFIG_DIR, "config"));
|
||||
const configFile = JSON.parse(
|
||||
fs.readFileSync(path.resolve(allConfig.CONFIG_DIR, "config.json"))
|
||||
);
|
||||
for (const [k, v] of Object.entries(configFile)) {
|
||||
if (!setConfig(k, v))
|
||||
// Pass as environment variable to be accessable by extensions
|
||||
@@ -226,7 +236,13 @@ function getDefault(optionName) {
|
||||
|
||||
// Load authentication scripts
|
||||
try {
|
||||
exports.auth = require(path.resolve(allConfig.CONFIG_DIR, "auth"));
|
||||
const authScript = fs.readFileSync(
|
||||
path.resolve(allConfig.CONFIG_DIR, "auth.js")
|
||||
);
|
||||
exports.auth = (function(exports) {
|
||||
eval(authScript.toString());
|
||||
return exports;
|
||||
})({});
|
||||
} catch (error) {
|
||||
// No auth.js exists
|
||||
}
|
||||
|
||||
@@ -184,6 +184,17 @@ function flatten(details) {
|
||||
delete details.fault;
|
||||
}
|
||||
|
||||
// For genieacs-ui
|
||||
if (details.context) {
|
||||
details.remoteAddress = details.context.request.ip;
|
||||
if (details.context.state.user)
|
||||
details.user = details.context.state.user.username;
|
||||
delete details.context;
|
||||
}
|
||||
|
||||
for (const [k, v] of Object.entries(details))
|
||||
if (v == null) delete details[k];
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
@@ -207,12 +218,15 @@ function formatSimple(details, systemd) {
|
||||
if (details.remoteAddress) {
|
||||
if (details.deviceId)
|
||||
remote = `${details.remoteAddress} ${details.deviceId}: `;
|
||||
else if (details.user)
|
||||
remote = `${details.user}@${details.remoteAddress}: `;
|
||||
else remote = `${details.remoteAddress}: `;
|
||||
}
|
||||
|
||||
const keys = Object.keys(details);
|
||||
const skip = {
|
||||
deviceId: 1,
|
||||
user: 1,
|
||||
remoteAddress: 1,
|
||||
severity: 1,
|
||||
timestamp: 1,
|
||||
|
||||
+33
-76
@@ -18,29 +18,11 @@
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const service = process.argv[2];
|
||||
|
||||
if (service == null) throw new Error("Missing argument cwmp, fs, or nbi");
|
||||
|
||||
const cluster = require("cluster");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const http = require("http");
|
||||
const https = require("https");
|
||||
const logger = require("./logger");
|
||||
logger.init(service, require("../package.json").version);
|
||||
const config = require("./config");
|
||||
const db = require("./db");
|
||||
const cache = require("./cache");
|
||||
const extensions = require("./extensions");
|
||||
|
||||
const networkInterface = config.get(`${service.toUpperCase()}_INTERFACE`);
|
||||
const port = config.get(`${service.toUpperCase()}_PORT`);
|
||||
const useHttps = config.get(`${service.toUpperCase()}_SSL`);
|
||||
|
||||
const { listener, onConnection } = require(`./${service}`);
|
||||
|
||||
let server;
|
||||
let server, listener;
|
||||
|
||||
function closeServer(timeout, callback) {
|
||||
if (!server) return void callback();
|
||||
@@ -66,67 +48,42 @@ function closeServer(timeout, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function exit() {
|
||||
setTimeout(() => {
|
||||
extensions.killAll(() => {
|
||||
process.exit(1);
|
||||
});
|
||||
}, 30000).unref();
|
||||
function start(port, networkInterface, ssl, _listener, onConnection) {
|
||||
listener = _listener;
|
||||
|
||||
closeServer(20000, () => {
|
||||
db.disconnect();
|
||||
cache.disconnect();
|
||||
extensions.killAll();
|
||||
if (cluster.worker) cluster.worker.disconnect();
|
||||
});
|
||||
}
|
||||
if (ssl) {
|
||||
const options = {
|
||||
key: fs.readFileSync(ssl.key),
|
||||
cert: fs.readFileSync(ssl.cert)
|
||||
};
|
||||
|
||||
process.on("uncaughtException", err => {
|
||||
logger.error({
|
||||
message: "Uncaught exception",
|
||||
exception: err,
|
||||
pid: process.pid
|
||||
});
|
||||
exit();
|
||||
});
|
||||
|
||||
if (useHttps) {
|
||||
const httpsKey = path.resolve(config.get("CONFIG_DIR"), `${service}.key`);
|
||||
const httpsCert = path.resolve(config.get("CONFIG_DIR"), `${service}.crt`);
|
||||
const httpsCa = path.resolve(
|
||||
config.get("CONFIG_DIR"),
|
||||
`${service}.ca-bundle`
|
||||
);
|
||||
const options = {
|
||||
key: fs.readFileSync(httpsKey),
|
||||
cert: fs.readFileSync(httpsCert)
|
||||
};
|
||||
|
||||
try {
|
||||
// Use intermediate certificates if available
|
||||
options.ca = fs
|
||||
.readFileSync(httpsCa)
|
||||
.toString()
|
||||
.match(/-+BEGIN CERTIFICATE-+[0-9a-zA-Z+\-/=\s]+?-+END CERTIFICATE-+/g);
|
||||
} catch (error) {
|
||||
// No intermediate certificate
|
||||
try {
|
||||
// Use intermediate certificates if available
|
||||
options.ca = fs
|
||||
.readFileSync(ssl.ca)
|
||||
.toString()
|
||||
.match(/-+BEGIN CERTIFICATE-+[0-9a-zA-Z+\-/=\s]+?-+END CERTIFICATE-+/g);
|
||||
} catch (error) {
|
||||
// No intermediate certificate
|
||||
}
|
||||
server = https.createServer(options, listener);
|
||||
if (onConnection != null) server.on("secureConnection", onConnection);
|
||||
} else {
|
||||
server = http.createServer(listener);
|
||||
if (onConnection != null) server.on("connection", onConnection);
|
||||
}
|
||||
server = https.createServer(options, listener);
|
||||
if (onConnection != null) server.on("secureConnection", onConnection);
|
||||
} else {
|
||||
server = http.createServer(listener);
|
||||
if (onConnection != null) server.on("connection", onConnection);
|
||||
|
||||
server.listen(port, networkInterface);
|
||||
}
|
||||
|
||||
db.connect(err => {
|
||||
if (err) throw err;
|
||||
|
||||
cache.connect(err => {
|
||||
if (err) throw err;
|
||||
|
||||
server.listen(port, networkInterface);
|
||||
function stop() {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
reject(new Error("Could not close server in a timely manner"));
|
||||
}, 30000).unref();
|
||||
closeServer(20000, resolve);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
process.on("SIGINT", exit);
|
||||
process.on("SIGTERM", exit);
|
||||
exports.start = start;
|
||||
exports.stop = stop;
|
||||
@@ -1,7 +1,5 @@
|
||||
"use strict";
|
||||
|
||||
const http = require("http");
|
||||
const cluster = require("cluster");
|
||||
const Koa = require("koa");
|
||||
const Router = require("koa-router");
|
||||
const koaStatic = require("koa-static");
|
||||
@@ -10,15 +8,11 @@ const koaBodyParser = require("koa-bodyparser");
|
||||
const koaJwt = require("koa-jwt");
|
||||
const jwt = require("jsonwebtoken");
|
||||
|
||||
const config = require("./config");
|
||||
const api = require("./api");
|
||||
const Authorizer = require("../common/authorizer");
|
||||
const expression = require("../common/expression");
|
||||
const config = require("./ui/config");
|
||||
const api = require("./ui/api");
|
||||
const Authorizer = require("./common/authorizer");
|
||||
const expression = require("./common/expression");
|
||||
const logger = require("./logger");
|
||||
const db = require("./db");
|
||||
|
||||
const VERSION = require("../../package.json")["version"];
|
||||
logger.init("server", VERSION);
|
||||
|
||||
const koa = new Koa();
|
||||
const router = new Router();
|
||||
@@ -170,51 +164,7 @@ koa.use(
|
||||
})
|
||||
);
|
||||
|
||||
const server = http
|
||||
.createServer(koa.callback())
|
||||
.listen(config.server.port, config.server.interface);
|
||||
|
||||
koa.use(async (ctx, next) => {
|
||||
await next();
|
||||
if (!server.listening) ctx.set("Connection", "close");
|
||||
});
|
||||
|
||||
koa.use(router.routes());
|
||||
koa.use(koaStatic("./public"));
|
||||
|
||||
function closeServer(timeout) {
|
||||
return new Promise(resolve => {
|
||||
// prevent new sockets to connect and close server eventually.
|
||||
server.close(resolve);
|
||||
setTimeout(resolve, timeout).unref();
|
||||
});
|
||||
}
|
||||
|
||||
function exit() {
|
||||
setTimeout(() => {
|
||||
process.exit(1);
|
||||
}, 30000).unref();
|
||||
|
||||
closeServer(20000).then(() => {
|
||||
db.disconnect();
|
||||
if (cluster.worker) cluster.worker.disconnect();
|
||||
logger.close();
|
||||
});
|
||||
}
|
||||
|
||||
process.on("unhandledRejection", err => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
process.on("uncaughtException", err => {
|
||||
logger.error({
|
||||
message: "Uncaught exception",
|
||||
exception: err,
|
||||
pid: process.pid
|
||||
});
|
||||
process.exitCode = 1;
|
||||
exit();
|
||||
});
|
||||
|
||||
process.on("SIGINT", exit);
|
||||
process.on("SIGTERM", exit);
|
||||
exports.listener = koa.callback();
|
||||
+1
-1
@@ -5,7 +5,7 @@ const Router = require("koa-router");
|
||||
const db = require("./db");
|
||||
const apiFunctions = require("./api-functions");
|
||||
const expression = require("../common/expression");
|
||||
const logger = require("./logger");
|
||||
const logger = require("../logger");
|
||||
|
||||
const router = new Router();
|
||||
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const cluster = require("cluster");
|
||||
const config = require("./config");
|
||||
const logger = require("./logger");
|
||||
|
||||
const VERSION = require("../../package.json")["version"];
|
||||
|
||||
let respawnTimestamp = 0;
|
||||
let tooManyCrashesTimestamp = 0;
|
||||
|
||||
function restartWorker(worker, code, signal) {
|
||||
const msg = {
|
||||
message: "Worker died",
|
||||
pid: worker.process.pid
|
||||
};
|
||||
|
||||
if (code != null) msg.exitCode = code;
|
||||
|
||||
if (signal != null) msg.signal = signal;
|
||||
|
||||
logger.error(msg);
|
||||
|
||||
const now = Date.now();
|
||||
respawnTimestamp = Math.max(now, respawnTimestamp + 2000);
|
||||
if (respawnTimestamp === now) {
|
||||
tooManyCrashesTimestamp = now;
|
||||
cluster.fork();
|
||||
return;
|
||||
}
|
||||
|
||||
if (now - tooManyCrashesTimestamp > 60000) {
|
||||
process.exitCode = 1;
|
||||
cluster.removeListener("exit", restartWorker);
|
||||
for (const pid in cluster.workers) cluster.workers[pid].kill();
|
||||
|
||||
logger.error({
|
||||
message: "Too many crashes, exiting",
|
||||
pid: process.pid
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (process.exitCode) return;
|
||||
cluster.fork();
|
||||
}, respawnTimestamp - now);
|
||||
}
|
||||
|
||||
function start() {
|
||||
logger.init(VERSION);
|
||||
|
||||
logger.info({
|
||||
message: "GenieACS UI starting",
|
||||
pid: process.pid,
|
||||
version: VERSION
|
||||
});
|
||||
|
||||
cluster.on("listening", (worker, address) => {
|
||||
logger.info({
|
||||
message: "Worker listening",
|
||||
pid: worker.process.pid,
|
||||
address: address.address,
|
||||
port: address.port
|
||||
});
|
||||
});
|
||||
|
||||
cluster.on("exit", restartWorker);
|
||||
|
||||
process.on("SIGINT", () => {
|
||||
logger.info({
|
||||
message: "Received signal SIGINT, exiting",
|
||||
pid: process.pid
|
||||
});
|
||||
|
||||
cluster.removeListener("exit", restartWorker);
|
||||
});
|
||||
|
||||
process.on("SIGTERM", () => {
|
||||
logger.info({
|
||||
message: "Received signal SIGTERM, exiting",
|
||||
pid: process.pid
|
||||
});
|
||||
|
||||
cluster.removeListener("exit", restartWorker);
|
||||
for (const pid in cluster.workers) cluster.workers[pid].kill();
|
||||
});
|
||||
|
||||
cluster.setupMaster({ exec: "./server" });
|
||||
|
||||
let workerCount = config.server.workers;
|
||||
|
||||
if (!workerCount) workerCount = Math.max(2, require("os").cpus().length);
|
||||
|
||||
for (let i = 0; i < workerCount; ++i) cluster.fork();
|
||||
}
|
||||
|
||||
process.on("uncaughtException", err => {
|
||||
logger.error({
|
||||
message: "Uncaught exception in master process, exiting",
|
||||
exception: err,
|
||||
pid: process.pid
|
||||
});
|
||||
process.exitCode = 1;
|
||||
cluster.removeListener("exit", restartWorker);
|
||||
for (const pid in cluster.workers) cluster.workers[pid].kill();
|
||||
});
|
||||
|
||||
// Starting GenieACS UI
|
||||
start();
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
const fs = require("fs");
|
||||
const configFile = JSON.parse(fs.readFileSync("./config-ui.json"));
|
||||
const configFile = JSON.parse(fs.readFileSync("./config/config-ui.json"));
|
||||
|
||||
const config = loadConfig(configFile);
|
||||
|
||||
|
||||
@@ -1,249 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const fs = require("fs");
|
||||
const os = require("os");
|
||||
|
||||
const config = require("./config");
|
||||
|
||||
const REOPEN_EVERY = 60000;
|
||||
|
||||
const LOG_FORMAT = config.logger.logFormat;
|
||||
const ACCESS_LOG_FORMAT = config.logger.accessLogFormat || LOG_FORMAT;
|
||||
|
||||
const defaultMeta = {};
|
||||
|
||||
let LOG_SYSTEMD = false;
|
||||
let ACCESS_LOG_SYSTEMD = false;
|
||||
|
||||
let LOG_FILE, ACCESS_LOG_FILE;
|
||||
|
||||
let logStream = fs.createWriteStream(null, { fd: process.stderr.fd });
|
||||
let logStat = fs.fstatSync(logStream.fd);
|
||||
let accessLogStream = fs.createWriteStream(null, { fd: process.stdout.fd });
|
||||
let accessLogStat = fs.fstatSync(accessLogStream.fd);
|
||||
|
||||
// Reopen if original files have been moved (e.g. logrotate)
|
||||
function reopen() {
|
||||
let counter = 1;
|
||||
|
||||
if (LOG_FILE) {
|
||||
++counter;
|
||||
fs.stat(LOG_FILE, (err, stat) => {
|
||||
if (err && !err.message.startsWith("ENOENT:")) throw err;
|
||||
|
||||
if (!(stat && stat.dev === logStat.dev && stat.ino === logStat.ino)) {
|
||||
logStream.end();
|
||||
logStream = fs.createWriteStream(null, {
|
||||
fd: fs.openSync(LOG_FILE, "a")
|
||||
});
|
||||
logStat = fs.fstatSync(logStream.fd);
|
||||
}
|
||||
|
||||
if (--counter === 0)
|
||||
setTimeout(reopen, REOPEN_EVERY - (Date.now() % REOPEN_EVERY)).unref();
|
||||
});
|
||||
}
|
||||
|
||||
if (ACCESS_LOG_FILE) {
|
||||
++counter;
|
||||
fs.stat(ACCESS_LOG_FILE, (err, stat) => {
|
||||
if (err && !err.message.startsWith("ENOENT:")) throw err;
|
||||
|
||||
if (
|
||||
!(
|
||||
stat &&
|
||||
stat.dev === accessLogStat.dev &&
|
||||
stat.ino === accessLogStat.ino
|
||||
)
|
||||
) {
|
||||
accessLogStream.end();
|
||||
accessLogStream = fs.createWriteStream(null, {
|
||||
fd: fs.openSync(ACCESS_LOG_FILE, "a")
|
||||
});
|
||||
accessLogStat = fs.fstatSync(accessLogStream.fd);
|
||||
}
|
||||
|
||||
if (--counter === 0)
|
||||
setTimeout(reopen, REOPEN_EVERY - (Date.now() % REOPEN_EVERY)).unref();
|
||||
});
|
||||
}
|
||||
|
||||
if (--counter === 0)
|
||||
setTimeout(reopen, REOPEN_EVERY - (Date.now() % REOPEN_EVERY)).unref();
|
||||
}
|
||||
|
||||
function init(version) {
|
||||
defaultMeta.hostname = os.hostname();
|
||||
defaultMeta.pid = process.pid;
|
||||
defaultMeta.name = "genieacs-ui";
|
||||
defaultMeta.version = version;
|
||||
|
||||
LOG_FILE = config.logger.logFile;
|
||||
ACCESS_LOG_FILE = config.logger.accessLogFile;
|
||||
|
||||
if (LOG_FILE) {
|
||||
logStream = fs.createWriteStream(null, { fd: fs.openSync(LOG_FILE, "a") });
|
||||
logStat = fs.fstatSync(logStream.fd);
|
||||
}
|
||||
|
||||
if (ACCESS_LOG_FILE) {
|
||||
accessLogStream = fs.createWriteStream(null, {
|
||||
fd: fs.openSync(ACCESS_LOG_FILE, "a")
|
||||
});
|
||||
accessLogStat = fs.fstatSync(accessLogStream.fd);
|
||||
}
|
||||
|
||||
// Determine if logs are going to journald
|
||||
const JOURNAL_STREAM = process.env["JOURNAL_STREAM"];
|
||||
|
||||
if (JOURNAL_STREAM) {
|
||||
let [dev, inode] = JOURNAL_STREAM.split(":");
|
||||
dev = parseInt(dev);
|
||||
inode = parseInt(inode);
|
||||
|
||||
LOG_SYSTEMD = logStat.dev === dev && logStat.ino === inode;
|
||||
ACCESS_LOG_SYSTEMD =
|
||||
accessLogStat.dev === dev && accessLogStat.ino === inode;
|
||||
}
|
||||
|
||||
if (LOG_FILE || ACCESS_LOG_FILE)
|
||||
setTimeout(reopen, REOPEN_EVERY - (Date.now() % REOPEN_EVERY)).unref();
|
||||
}
|
||||
|
||||
function close() {
|
||||
accessLogStream.end();
|
||||
logStream.end();
|
||||
}
|
||||
|
||||
function flatten(details) {
|
||||
if (details.exception) {
|
||||
details.exceptionName = details.exception.name;
|
||||
details.exceptionMessage = details.exception.message;
|
||||
details.exceptionStack = details.exception.stack;
|
||||
delete details.exception;
|
||||
}
|
||||
|
||||
if (details.context) {
|
||||
details.remoteAddress = details.context.request.ip;
|
||||
if (details.context.state.user)
|
||||
details.user = details.context.state.user.username;
|
||||
delete details.context;
|
||||
}
|
||||
|
||||
for (const [k, v] of Object.entries(details))
|
||||
if (v == null) delete details[k];
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
function formatJson(details, systemd) {
|
||||
if (systemd) {
|
||||
let severity = "";
|
||||
if (details.severity === "info") severity = "<6>";
|
||||
else if (details.severity === "warn") severity = "<4>";
|
||||
else if (details.severity === "error") severity = "<3>";
|
||||
|
||||
return `${severity}${JSON.stringify(flatten(details))}${os.EOL}`;
|
||||
}
|
||||
|
||||
return `${JSON.stringify(flatten(details))}${os.EOL}`;
|
||||
}
|
||||
|
||||
function formatSimple(details, systemd) {
|
||||
flatten(details);
|
||||
|
||||
let remote = "";
|
||||
if (details.remoteAddress) {
|
||||
if (details.user) remote = `${details.user}@${details.remoteAddress}: `;
|
||||
else remote = `${details.remoteAddress}: `;
|
||||
}
|
||||
|
||||
const keys = Object.keys(details);
|
||||
const skip = {
|
||||
user: 1,
|
||||
remoteAddress: 1,
|
||||
severity: 1,
|
||||
timestamp: 1,
|
||||
message: 1
|
||||
};
|
||||
|
||||
let meta = "";
|
||||
|
||||
const kv = [];
|
||||
for (const k of keys)
|
||||
if (!skip[k]) kv.push(`${k}=${JSON.stringify(details[k])}`);
|
||||
|
||||
if (kv.length) meta = `; ${kv.join(" ")}`;
|
||||
|
||||
if (systemd) {
|
||||
let severity = "";
|
||||
if (details.severity === "info") severity = "<6>";
|
||||
else if (details.severity === "warn") severity = "<4>";
|
||||
else if (details.severity === "error") severity = "<3>";
|
||||
|
||||
return `${severity}${remote}${details.message}${meta}${os.EOL}`;
|
||||
}
|
||||
|
||||
return `${details.timestamp} [${details.severity.toUpperCase()}] ${remote}${
|
||||
details.message
|
||||
}${meta}${os.EOL}`;
|
||||
}
|
||||
|
||||
function log(details) {
|
||||
details.timestamp = new Date().toISOString();
|
||||
if (LOG_FORMAT === "json") {
|
||||
details = Object.assign({}, defaultMeta, details);
|
||||
logStream.write(formatJson(details, LOG_SYSTEMD));
|
||||
} else {
|
||||
logStream.write(formatSimple(details, LOG_SYSTEMD));
|
||||
}
|
||||
}
|
||||
|
||||
function info(details) {
|
||||
details.severity = "info";
|
||||
log(details);
|
||||
}
|
||||
|
||||
function warn(details) {
|
||||
details.severity = "warn";
|
||||
log(details);
|
||||
}
|
||||
|
||||
function error(details) {
|
||||
details.severity = "error";
|
||||
log(details);
|
||||
}
|
||||
|
||||
function accessLog(details) {
|
||||
details.timestamp = new Date().toISOString();
|
||||
if (ACCESS_LOG_FORMAT === "json") {
|
||||
Object.assign(details, defaultMeta);
|
||||
accessLogStream.write(formatJson(details, ACCESS_LOG_SYSTEMD));
|
||||
} else {
|
||||
accessLogStream.write(formatSimple(details, ACCESS_LOG_SYSTEMD));
|
||||
}
|
||||
}
|
||||
|
||||
function accessInfo(details) {
|
||||
details.severity = "info";
|
||||
accessLog(details);
|
||||
}
|
||||
|
||||
function accessWarn(details) {
|
||||
details.severity = "warn";
|
||||
accessLog(details);
|
||||
}
|
||||
|
||||
function accessError(details) {
|
||||
details.severity = "error";
|
||||
accessLog(details);
|
||||
}
|
||||
|
||||
exports.init = init;
|
||||
exports.close = close;
|
||||
exports.info = info;
|
||||
exports.warn = warn;
|
||||
exports.error = error;
|
||||
exports.accessInfo = accessInfo;
|
||||
exports.accessWarn = accessWarn;
|
||||
exports.accessError = accessError;
|
||||
Reference in new issue
Block a user