1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Improve diagnostics content

This commit is contained in:
Steve-Mcl 2022-03-28 18:49:56 +01:00
parent 3388f699a0
commit a2fd705153
3 changed files with 176 additions and 94 deletions

View File

@ -1,27 +1,23 @@
var apiUtils = require("@node-red/editor-api/lib/util");
/** @type {runtime.RuntimeModule} */var runtimeAPI;
let runtimeAPI;
let settings;
const apiUtil = require("../util");
module.exports = {
init: function(/** @type {runtime.RuntimeModule} */_runtimeAPI) {
init: function(_settings, _runtimeAPI) {
settings = _settings;
runtimeAPI = _runtimeAPI;
},
getBasicReport: function(req, res) {
var opts = {
getReport: function(req, res) {
const diagnosticsOptions = settings.diagnosticsOptions || {};
const opts = {
user: req.user,
scope: "basic"
scope: diagnosticsOptions.level || "basic"
}
runtimeAPI.diagnostics.get(opts).then(function(result) {
res.json(result);
});
},
getAdminReport: function(req, res) {
var opts = {
user: req.user,
scope: "admin"
if(diagnosticsOptions.enabled === false || diagnosticsOptions.enabled === "false") {
apiUtil.rejectHandler(req, res, {message: "disabled", status: 403, code: "diagnosticsOptions.enabled" })
} else {
runtimeAPI.diagnostics.get(opts)
.then(function(result) { res.json(result); })
.catch(err => apiUtil.rejectHandler(req, res, err))
}
runtimeAPI.diagnostics.get(opts).then(function(result) {
res.json(result);
});
}
}

View File

@ -35,7 +35,7 @@ module.exports = {
context.init(runtimeAPI);
info.init(settings,runtimeAPI);
plugins.init(runtimeAPI);
diagnostics.init(runtimeAPI);
diagnostics.init(settings, runtimeAPI);
var needsPermission = auth.needsPermission;
@ -97,8 +97,7 @@ module.exports = {
adminApp.get("/plugins", needsPermission("plugins.read"), plugins.getAll, apiUtil.errorHandler);
adminApp.get("/plugins/messages", needsPermission("plugins.read"), plugins.getCatalogs, apiUtil.errorHandler);
adminApp.get("/diagnostics/basic", needsPermission("settings.read"), diagnostics.getBasicReport, apiUtil.errorHandler);
adminApp.get("/diagnostics/admin", needsPermission("flows.write"), diagnostics.getAdminReport, apiUtil.errorHandler);
adminApp.get("/diagnostics", needsPermission("diagnostics.read"), diagnostics.getReport, apiUtil.errorHandler);
return adminApp;
}

View File

@ -1,73 +1,176 @@
/**
* @mixin @node-red/diagnostics
* @namespace RED.runtime.diagnostics
*/
const os = require('os');
const fs = require('fs');
var runtime;
let runtime;
let isContainerCached;
let isWSLCached;
var util = require("@node-red/util").util;
const isInWsl = () => {
if (isWSLCached === undefined) {
isWSLCached = getIsInWSL();
}
return isWSLCached;
function getIsInWSL() {
if (process.platform !== 'linux') {
return false;
}
try {
if (os.release().toLowerCase().includes('microsoft')) {
if (isInContainer()) {
return false;
}
return true;
}
return fs.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft') ? !isInContainer() : false;
} catch (_) {
return false;
}
}
};
const isInContainer = () => {
if (isContainerCached === undefined) {
isContainerCached = hasDockerEnv() || hasDockerCGroup();
}
return isContainerCached;
function hasDockerEnv() {
try {
fs.statSync('/.dockerenv');
return true;
} catch {
return false;
}
}
function hasDockerCGroup() {
try {
const s = fs.readFileSync('/proc/self/cgroup', 'utf8');
if (s.includes('docker')) {
return "docker"
} else if (s.includes('kubepod')) {
return "kubepod"
} else if (s.includes('lxc')) {
return "lxc"
}
} catch {
return false;
}
}
}
function buildDiagnosticReport(scope, callback) {
var basic = {
"report": "diagnostics",
"scope": scope,
"runtime": {
version: runtime.settings.version,
isStarted: runtime.isStarted()
const modules = {};
const nl = runtime.nodes.getNodeList();
for (let i = 0; i < nl.length; i++) {
if (modules[nl[i].module]) {
continue;
}
modules[nl[i].module] = nl[i].version
}
const now = new Date();
const report = {
report: "diagnostics",
scope: scope,
version: runtime.settings.version,
isStarted: runtime.isStarted(),
containerised: isInContainer(),
wsl: isInWsl(),
time: {
timestamp: now.valueOf(),
utc: "" + now,
locale: now.toLocaleString(),
},
settings: {
available: runtime.settings.available(),
apiMaxLength: runtime.settings.apiMaxLength || "NO SETTING",
coreNodesDir: runtime.settings.coreNodesDir,
contextStorage: listContextModules(),
debugMaxLength: runtime.settings.debugMaxLength,
editorTheme: runtime.settings.editorTheme,
flowFile: runtime.settings.flowFile,
disableEditor:runtime.settings.disableEditor,
debugMaxLength:runtime.settings.debugMaxLength,
httpAdminRoot: runtime.settings.httpAdminRoot,
httpAdminCors: runtime.settings.httpAdminCors ? "HAS SETTING": "NOT SET",
httpNodeAuth: runtime.settings.httpNodeAuth ? "HAS SETTING": "NOT SET",
httpNodeRoot: runtime.settings.httpNodeRoot,
httpNodeCors: runtime.settings.httpNodeCors ? "HAS SETTING": "NOT SET",
intl: Intl.DateTimeFormat().resolvedOptions(),
nodejs: {
version: process.version,
arch: process.arch,
platform: process.platform,
memoryUsage: process.memoryUsage(),
},
os: {
totalmem: os.totalmem(),
freemem: os.freemem(),
arch: os.arch(),
loadavg: os.loadavg(),
platform: os.platform(),
release: os.release(),
type: os.type(),
uptime: os.uptime(),
version: os.version(),
},
runtime: {
modules: modules,
settings: {
available: runtime.settings.available(),
apiMaxLength: runtime.settings.apiMaxLength || "NO SETTING",
//coreNodesDir: runtime.settings.coreNodesDir,
disableEditor: runtime.settings.disableEditor,
contextStorage: listContextModules(),
debugMaxLength: runtime.settings.debugMaxLength || "NO SETTING",
editorTheme: runtime.settings.editorTheme || "NO SETTING",
flowFile: runtime.settings.flowFile || "NO SETTING",
mqttReconnectTime: runtime.settings.mqttReconnectTime || "NO SETTING",
serialReconnectTime: runtime.settings.serialReconnectTime || "NO SETTING",
httpStatic: runtime.settings.httpStatic,
httpStaticCors: runtime.settings.httpStaticCors,
adminAuth: runtime.settings.adminAuth ? "HAS SETTING" : "NO SETTING",
mqttReconnectTime: runtime.settings.mqttReconnectTime,
httpAdminRoot: runtime.settings.adminAuth ? "HAS SETTING" : "NO SETTING",
httpAdminCors: runtime.settings.httpAdminCors ? "HAS SETTING" : "NO SETTING",
httpNodeAuth: runtime.settings.httpNodeAuth ? "HAS SETTING" : "NO SETTING",
httpAdminRoot: runtime.settings.httpAdminRoot || "NO SETTING",
httpAdminCors: runtime.settings.httpAdminCors ? "HAS SETTING" : "NO SETTING",
uiHost: runtime.settings.uiHost ? "HAS SETTING": "NOT SET",
uiPort: runtime.settings.uiPort ? "HAS SETTING": "NOT SET",
userDir: runtime.settings.userDir ? "HAS SETTING": "NOT SET",
httpNodeRoot: runtime.settings.httpNodeRoot || "NO SETTING",
httpNodeCors: runtime.settings.httpNodeCors ? "HAS SETTING" : "NO SETTING",
version: runtime.settings.version
}
}
var admin = {};
if(scope == "admin") {
admin = {
httpAdminCors: runtime.settings.httpAdminCors ? runtime.settings.httpAdminCors : "NOT SET",
httpNodeCors: runtime.settings.httpNodeCors ? runtime.settings.httpNodeCors : "NOT SET",
uiHost: runtime.settings.uiHost ? runtime.settings.uiHost : "NOT SET",
uiPort: runtime.settings.uiPort ? runtime.settings.uiPort : "NOT SET",
userDir: runtime.settings.userDir ? runtime.settings.userDir : "NOT SET",
httpStatic: runtime.settings.httpStatic ? "HAS SETTING" : "NO SETTING",
httpStatic: runtime.settings.httpStaticRoot || "NO SETTING",
httpStaticCors: runtime.settings.httpStaticCors ? "HAS SETTING" : "NO SETTING",
uiHost: runtime.settings.uiHost ? "HAS SETTING" : "NO SETTING",
uiPort: runtime.settings.uiPort ? "HAS SETTING" : "NO SETTING",
userDir: runtime.settings.userDir ? "HAS SETTING" : "NO SETTING",
}
}
}
var report = Object.assign({}, admin, basic);
if (scope == "admin") {
const moreSettings = {
adminAuth_type: (runtime.settings.adminAuth && runtime.settings.adminAuth.type) ? runtime.settings.adminAuth.type : "NO SETTING",
httpAdminCors: runtime.settings.httpAdminCors ? runtime.settings.httpAdminCors : "NO SETTING",
httpNodeCors: runtime.settings.httpNodeCors ? runtime.settings.httpNodeCors : "NO SETTING",
httpStaticCors: runtime.settings.httpStaticCors ? "HAS SETTING" : "NO SETTING",
settingsFile: runtime.settings.settingsFile ? runtime.settings.settingsFile : "NO SETTING",
uiHost: runtime.settings.uiHost ? runtime.settings.uiHost : "NO SETTING",
uiPort: runtime.settings.uiPort ? runtime.settings.uiPort : "NO SETTING",
userDir: runtime.settings.userDir ? runtime.settings.userDir : "NO SETTING",
}
const moreNodejs = {
execPath: process.execPath,
pid: process.pid,
}
const moreOs = {
cpus: os.cpus(),
homedir: os.homedir(),
hostname: os.hostname(),
networkInterfaces: os.networkInterfaces(),
}
report.runtime.settings = Object.assign({}, report.runtime.settings, moreSettings);
report.nodejs = Object.assign({}, report.nodejs, moreNodejs);
report.os = Object.assign({}, report.os, moreOs);
}
callback(report);
/** gets a sanitised list containing only the module name */
function listContextModules() {
var keys = Object.keys(runtime.settings.contextStorage);
var result = {};
const keys = Object.keys(runtime.settings.contextStorage);
const result = {};
keys.forEach(e => {
result[e] = {
module: runtime.settings.contextStorage[e].module
module: String(runtime.settings.contextStorage[e].module)
}
})
return result;
@ -75,40 +178,24 @@ function buildDiagnosticReport(scope, callback) {
}
var api = module.exports = {
module.exports = {
init: function (_runtime) {
runtime = _runtime;
},
/**
* Gets the node-red diagnostics report
* @param {{scope: string}} - settings
* @return {Promise} - the diagnostics information
* @param {{scope: string}} opts - settings
* @return {Promise} the diagnostics information
* @memberof @node-red/diagnostics
*/
get: async function (opts) {
return new Promise(function (resolve, reject) {
opts = opts || {}
var scope = opts.scope;
try {
if (scope === 'admin') {
//admin level info
runtime.log.audit({ event: "diagnostics.get", scope: "admin" }, opts.req);
buildDiagnosticReport(scope, (report) => resolve(report));
} else if (scope === 'detail') {
//detail!
runtime.log.audit({ event: "diagnostics.get", scope: "detail" }, opts.req);
buildDiagnosticReport(scope, (report) => resolve(report));
} else if (scope === 'basic') {
//basic!
runtime.log.audit({ event: "diagnostics.get", scope: "basic" }, opts.req);
buildDiagnosticReport(scope, (report) => resolve(report));
} else {
runtime.log.audit({ event: "diagnostics.get", scope: scope }, opts.req);
resolve({});
}
runtime.log.audit({ event: "diagnostics.get", scope: opts.scope }, opts.req);
buildDiagnosticReport(opts.scope, (report) => resolve(report));
} catch (error) {
runtime.log.audit({ event: "diagnostics.get", scope: scope }, opts.req);
error.status = 500;
reject(error);
}
})