code optimization

This commit is contained in:
Daniel Sommer 2022-03-15 01:36:47 +01:00
parent 2b37fc9cdd
commit fe1a21f222
5 changed files with 264 additions and 97 deletions

View file

@ -1,22 +1,21 @@
{ {
"server": { "server": {
"listen": "0.0.0.0", "listen": "0.0.0.0",
"port": 3000, "port": 3000
"timestamp": "DD.MM.YYYY HH:mm:ss:SS"
}, },
"log": { "log": {
"level": "debug" "level": "debug",
"timestamp": "DD.MM.YYYY HH:mm:ss:SS"
}, },
"api": [ "api": [
{ {
"url": "/uptime", "url": "/uptime",
"type": "get", "type": "get",
"command": "uptime123", "command": "uptime1",
"args": [ "args": [
"-V",
"-p"
], ],
"passargs": true "passargs": true,
"detach": false
} }
] ]
} }

View file

@ -1,27 +1,85 @@
const logger = require('../libs/logger.js'); const logger = require('./logger.js');
const { spawn } = require('child_process') const { spawn } = require('child_process')
function execute(endpoint) { const cmds = new Map();
if (!endpoint || !endpoint.command) {
logger.warn('no command defined'); async function execute(endpoint) {
if (endpoint === undefined) {
return; return;
} }
logger.debug('executing command \'' + endpoint.command + '\' with args \'' + endpoint.args + '\'...'); return new Promise((resolve, reject) => {
var cmd = spawn(endpoint.command, endpoint.args); logger.info('executing command \'' + endpoint.command + '\' (args: \'' + endpoint.args + '\')...');
cmd.stdout.on('data', function(data) { var cmd = spawn(endpoint.command, endpoint.args);
logger.debug(data); cmd.timestamp = new Date().getTime();
}); let result = '';
cmd.stderr.on('data', function(data) { let error = '';
logger.error(data); cmd.stdout.on('data', (data) => {
}); result += data;
cmd.on('close', function(code) { });
logger.debug('command \'' + endpoint.command + '\' with args \'' + endpoint.args + '\' finished with exit code ' + code); cmd.stderr.on('data', (data) => {
}); error += data;
cmd.on('error', function(err) { });
logger.error('command \'' + endpoint.command + '\' with args \'' + endpoint.args + '\' encountered an error >>> ' + err); cmd.on('spawn', () => {
logger.info('spawned command \'' + endpoint.command + '\' (args: \'' + endpoint.args + '\')');
addCommand(cmd, endpoint);
if (endpoint.detach !== false) {
resolve();
}
});
cmd.on('error', (err) => {
error += err;
removeCommand(endpoint);
if (endpoint.detach !== false) {
reject();
}
});
cmd.on('close', (code) => {
removeCommand(endpoint);
let fn = logger.info;
let msg = 'command \'' + endpoint.command + '\' (args: \'' + endpoint.args + '\') finished with exit code ' + code + ' after ' + (new Date().getTime() - cmd.timestamp) + 'ms';
if (error !== undefined && error.length > 0) {
msg += ' >>> ' + error;
fn = logger.error;
reject(error);
}
if (result !== undefined && result.length > 0) {
msg += ' > ' + result;
}
fn(msg);
resolve(result);
});
}); });
} }
function addCommand(command, endpoint) {
if (command === undefined || endpoint === undefined) {
return;
}
cmds.set(JSON.stringify(endpoint), command);
}
function removeCommand(endpoint) {
if (endpoint === undefined) {
return;
}
cmds.delete(JSON.stringify(endpoint));
}
function isCommandActive(endpoint) {
return endpoint !== undefined && cmds.has(JSON.stringify(endpoint));
}
function killCommand(endpoint) {
if (endpoint === undefined) {
return;
}
const command = cmds.get(JSON.stringify(endpoint));
if (command === undefined) {
return;
}
process.kill(command.pid);
}
module.exports = { module.exports = {
execute execute
} }

View file

@ -1,48 +1,80 @@
const config = require("../config.json"); const moment = require('moment');
const moment = require("moment");
// constants // constants
const LOG_PREFIX_DEBUG = "debug"; const LOG_PREFIX_DEBUG = 'debug';
const LOG_PREFIX_INFO = "info"; const LOG_PREFIX_INFO = 'info';
const LOG_PREFIX_WARNING = "warning"; const LOG_PREFIX_WARNING = 'warning';
const LOG_PREFIX_ERROR = "error"; const LOG_PREFIX_ERROR = 'error';
const LOGLEVEL_DEBUG = 0; const LOGLEVEL_DEBUG = 0;
const LOGLEVEL_INFO = 1; const LOGLEVEL_INFO = 1;
const LOGLEVEL_WARNING = 2; const LOGLEVEL_WARNING = 2;
const LOGLEVEL_ERROR = 3; const LOGLEVEL_ERROR = 3;
// set loglevel on "require" let loglevel;
const loglevel = function() { let timestamp;
switch (config.log.level) {
initialize();
function initialize(loglevel, timestamp) {
setLogLevel(loglevel || global.config?.log?.level);
setTimestamp(timestamp || global.config?.log?.timestamp);
}
// set the loglevel
function setLogLevel(value) {
switch (value) {
case LOG_PREFIX_DEBUG: case LOG_PREFIX_DEBUG:
case LOGLEVEL_DEBUG: case LOGLEVEL_DEBUG:
return LOGLEVEL_DEBUG; loglevel = LOGLEVEL_DEBUG;
break;
case LOG_PREFIX_INFO: case LOG_PREFIX_INFO:
case LOGLEVEL_INFO: case LOGLEVEL_INFO:
return LOGLEVEL_INFO; loglevel = LOGLEVEL_INFO;
break;
case LOG_PREFIX_WARNING: case LOG_PREFIX_WARNING:
case LOGLEVEL_WARNING: case LOGLEVEL_WARNING:
return LOGLEVEL_WARNING; loglevel = LOGLEVEL_WARNING;
break;
case LOG_PREFIX_ERROR: case LOG_PREFIX_ERROR:
case LOGLEVEL_ERROR: case LOGLEVEL_ERROR:
return LOGLEVEL_ERROR; loglevel = LOGLEVEL_ERROR;
break;
default: default:
return LOGLEVEL_INFO; loglevel = LOGLEVEL_INFO;
break;
} }
}(); }
// log a http request // get the timestamp format
function request(request) { function setTimestamp(value) {
let message = "[" + request.method + "] url: \"" + request.url + "\""; timestamp = value || 'DD.MM.YYYY HH:mm:ss:SS';
}
// log a http request - response object
function http(object) {
if (object === undefined) {
return;
}
let message = '[' + object.request.method + ':' + object.code + '] url: \'' + object.request.url + '\'';
let counter = 1; let counter = 1;
for (let param in request.body) { for (let param in object.request.body) {
message += ", parameter " + counter + ": \"" + param + "=" + request.body[param] + "\""; message += ', parameter ' + counter + ': \'' + param + '=' + object.request.body[param] + '\'';
counter++; counter++;
} }
if (object.request.timestamp) {
message += ' > ' + (new Date().getTime() - object.request.timestamp) + 'ms';
}
if (object.data) {
message += ' > data: ' + object.data;
}
if (object.code != 200) {
error(message.trim());
return;
}
debug(message.trim()); debug(message.trim());
} }
// prefix log with "info" // prefix log with 'info'
function info(message) { function info(message) {
if (loglevel > LOGLEVEL_INFO) { if (loglevel > LOGLEVEL_INFO) {
return; return;
@ -50,28 +82,42 @@ function info(message) {
trace(message); trace(message);
} }
// prefix log with "info" // prefix log with 'info'
function warn(message) { function warn(message) {
if (loglevel > LOGLEVEL_WARNING) { if (loglevel > LOGLEVEL_WARNING) {
return; return;
} }
trace(message, "warning"); trace(message, 'warning');
} }
// prefix log with "debug" // prefix log with 'debug'
function debug(message) { function debug(message) {
if (loglevel > LOGLEVEL_DEBUG) { if (loglevel > LOGLEVEL_DEBUG) {
return; return;
} }
trace(message, "debug"); trace(message, 'debug');
} }
// prefix log with "error" // prefix log with 'error'
function error(message) { function error(message) {
if (loglevel > LOGLEVEL_ERROR) { if (loglevel > LOGLEVEL_ERROR) {
return; return;
} }
trace(message, "error"); if (message.stack) {
trace(message.stack, 'error');
return;
}
if (message.errors !== undefined) {
for (let index = 0; index < message.errors.length; index++) {
trace(message.errors[index], 'error');
}
return;
}
if (message.message) {
trace(message.message, 'error');
return;
}
trace(message, 'error');
} }
// default logging function // default logging function
@ -80,31 +126,32 @@ function trace(message, prefix) {
return; return;
} }
if (prefix === undefined || prefix === null || prefix.length === 0) { if (prefix === undefined || prefix === null || prefix.length === 0) {
prefix = "info"; prefix = 'info';
} }
let print; let print;
switch (prefix) { switch (prefix) {
case "error": case 'error':
print = console.error; print = console.error;
break; break;
case "debug": case 'debug':
print = console.debug; print = console.debug;
break; break;
case "warning": case 'warning':
print = console.warn; print = console.warn;
break; break;
default: default:
print = console.log; print = console.log;
} }
message = moment().format(config.server.timestamp) + " | " + prefix + " > " + message; message = moment().format(timestamp) + ' | ' + prefix + ' > ' + message;
print(message); print(message);
} }
// exports // exports
module.exports = { module.exports = {
initialize,
info, info,
warn, warn,
debug, debug,
error, error,
request http
}; }

View file

@ -1,50 +1,75 @@
const config = require('../config.json');
const logger = require('../libs/logger.js'); const logger = require('../libs/logger.js');
const commands = require('../libs/commands.js'); const commands = require('../libs/commands.js');
const http = require('http'); const http = require('http');
const { time } = require('console');
var server; let server;
var api; let api;
function start(callback) { async function start() {
const listen = global.config?.server?.listen || '0.0.0.0';
const port = global.config?.server?.port;
buildAPI(); buildAPI();
if (!server) { if (server === undefined) {
server = http.createServer(); server = http.createServer();
} }
server.listen(config.server.port, config.server.listen) return new Promise((resolve, reject) => {
.on('listening', function () { server.listen(port, listen)
logger.debug('server listening on ' + config.server.listen + ':' + config.server.port + '...'); .on('listening', function () {
handleRequests(); logger.debug('server listening on ' + config.server.listen + ':' + config.server.port + '...');
}); handleRequests();
} resolve();
});
function handleRequests() {
server.on('request', function (request, response) {
logger.request(request);
var endpoint = api.get(request.url);
if (!endpoint) {
response.writeHead(501);
response.end('endpoint not defined\n');
return;
}
if (request.method.toLowerCase() != (endpoint.type.toLowerCase())) {
response.writeHead(405);
response.end('endpoint does not support ' + request.method + ' requests\n');
return;
}
commands.execute(endpoint);
response.end();
}); });
} }
function buildAPI(callback) { function handleRequests() {
if (!config.api) { server.on('request', (request, response) => {
logger.warn('no api defined'); request.timestamp = new Date().getTime();
respond(request, response, api.get(request.url));
});
}
async function respond(request, response, endpoint) {
if (response === undefined) {
return;
}
let data = {
status: 'ok'
};
let code = 200;
if (endpoint === undefined) {
code = 501;
data.status = 'error';
data.msg = 'endpoint not defined';
} else if (request.method.toLowerCase() !== (endpoint.type.toLowerCase())) {
code = 405;
data.status = 'error';
data.msg = 'endpoint does not support ' + request.method + ' requests';
} else {
try {
data.result = await commands.execute(endpoint);
} catch (err) {
code = 501;
data.status = 'error';
data.msg = err;
}
}
const json = JSON.stringify(data);
response.writeHead(code);
response.end(json);
logger.http({ request: request, code: code, data: data });
}
function buildAPI() {
const apiConfig = global.config?.api;
if (apiConfig === undefined || apiConfig.length === 0) {
throw new Error('no api endpoints configured - aborting');
} }
api = new Map(); api = new Map();
config.api.forEach(function(endpoint) { config.api.forEach(function (endpoint) {
var url = endpoint.url; let url = endpoint.url;
var tmp = endpoint; let tmp = endpoint;
delete tmp.url; delete tmp.url;
api.set(url, tmp); api.set(url, tmp);
}); });

View file

@ -1,11 +1,49 @@
const logger = require('./libs/logger.js'); const logger = require('./libs/logger.js');
const packageJSON = require('./package.json'); const packageJSON = require('./package.json');
const server = require('./libs/server.js') const server = require('./libs/server.js');
const path = require('path');
logger.info("launching " + packageJSON.name + " " + packageJSON.version) const INTERRUPTS = ['beforeExit', 'SIGINT', 'SIGTERM'];
server.start(function(err) {
main();
async function main() {
let configPath = path.resolve('./config.json');
try {
global.config = require(configPath);
} catch (err) {
logger.warn('could not read config file at \'' + configPath + '\'');
}
logger.initialize();
handleExit();
logger.info("launching " + packageJSON.name + " " + packageJSON.version + "...");
try {
await server.start();
} catch (err) {
exit(err);
}
};
function handleExit() {
for (var index = 0; index < INTERRUPTS.length; index++) {
process.on(INTERRUPTS[index], async (code) => {
exit(undefined, code);
});
}
}
function exit(err, code) {
if (code === undefined) {
code = 0;
if (err !== undefined) {
code = 1;
}
}
if (err) { if (err) {
logger.error(err); logger.error(err);
process.exit(1); logger.error(packageJSON.name + ' ' + packageJSON.version + ' ended due to an error');
} else {
logger.info(packageJSON.name + ' ' + packageJSON.version + ' shutting down gracefully')
} }
}); process.exit(code);
}