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:
parent
3388f699a0
commit
a2fd705153
@ -1,27 +1,23 @@
|
|||||||
var apiUtils = require("@node-red/editor-api/lib/util");
|
let runtimeAPI;
|
||||||
/** @type {runtime.RuntimeModule} */var runtimeAPI;
|
let settings;
|
||||||
|
const apiUtil = require("../util");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
init: function(/** @type {runtime.RuntimeModule} */_runtimeAPI) {
|
init: function(_settings, _runtimeAPI) {
|
||||||
|
settings = _settings;
|
||||||
runtimeAPI = _runtimeAPI;
|
runtimeAPI = _runtimeAPI;
|
||||||
},
|
},
|
||||||
getBasicReport: function(req, res) {
|
getReport: function(req, res) {
|
||||||
var opts = {
|
const diagnosticsOptions = settings.diagnosticsOptions || {};
|
||||||
|
const opts = {
|
||||||
user: req.user,
|
user: req.user,
|
||||||
scope: "basic"
|
scope: diagnosticsOptions.level || "basic"
|
||||||
}
|
}
|
||||||
runtimeAPI.diagnostics.get(opts).then(function(result) {
|
if(diagnosticsOptions.enabled === false || diagnosticsOptions.enabled === "false") {
|
||||||
res.json(result);
|
apiUtil.rejectHandler(req, res, {message: "disabled", status: 403, code: "diagnosticsOptions.enabled" })
|
||||||
});
|
} else {
|
||||||
},
|
runtimeAPI.diagnostics.get(opts)
|
||||||
getAdminReport: function(req, res) {
|
.then(function(result) { res.json(result); })
|
||||||
var opts = {
|
.catch(err => apiUtil.rejectHandler(req, res, err))
|
||||||
user: req.user,
|
|
||||||
scope: "admin"
|
|
||||||
}
|
}
|
||||||
runtimeAPI.diagnostics.get(opts).then(function(result) {
|
|
||||||
res.json(result);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ module.exports = {
|
|||||||
context.init(runtimeAPI);
|
context.init(runtimeAPI);
|
||||||
info.init(settings,runtimeAPI);
|
info.init(settings,runtimeAPI);
|
||||||
plugins.init(runtimeAPI);
|
plugins.init(runtimeAPI);
|
||||||
diagnostics.init(runtimeAPI);
|
diagnostics.init(settings, runtimeAPI);
|
||||||
|
|
||||||
var needsPermission = auth.needsPermission;
|
var needsPermission = auth.needsPermission;
|
||||||
|
|
||||||
@ -97,8 +97,7 @@ module.exports = {
|
|||||||
adminApp.get("/plugins", needsPermission("plugins.read"), plugins.getAll, apiUtil.errorHandler);
|
adminApp.get("/plugins", needsPermission("plugins.read"), plugins.getAll, apiUtil.errorHandler);
|
||||||
adminApp.get("/plugins/messages", needsPermission("plugins.read"), plugins.getCatalogs, 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", needsPermission("diagnostics.read"), diagnostics.getReport, apiUtil.errorHandler);
|
||||||
adminApp.get("/diagnostics/admin", needsPermission("flows.write"), diagnostics.getAdminReport, apiUtil.errorHandler);
|
|
||||||
|
|
||||||
return adminApp;
|
return adminApp;
|
||||||
}
|
}
|
||||||
|
@ -1,73 +1,176 @@
|
|||||||
|
|
||||||
/**
|
const os = require('os');
|
||||||
* @mixin @node-red/diagnostics
|
const fs = require('fs');
|
||||||
* @namespace RED.runtime.diagnostics
|
|
||||||
*/
|
|
||||||
|
|
||||||
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) {
|
function buildDiagnosticReport(scope, callback) {
|
||||||
var basic = {
|
const modules = {};
|
||||||
"report": "diagnostics",
|
const nl = runtime.nodes.getNodeList();
|
||||||
"scope": scope,
|
for (let i = 0; i < nl.length; i++) {
|
||||||
"runtime": {
|
if (modules[nl[i].module]) {
|
||||||
version: runtime.settings.version,
|
continue;
|
||||||
isStarted: runtime.isStarted()
|
}
|
||||||
|
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: {
|
intl: Intl.DateTimeFormat().resolvedOptions(),
|
||||||
available: runtime.settings.available(),
|
nodejs: {
|
||||||
apiMaxLength: runtime.settings.apiMaxLength || "NO SETTING",
|
version: process.version,
|
||||||
coreNodesDir: runtime.settings.coreNodesDir,
|
arch: process.arch,
|
||||||
contextStorage: listContextModules(),
|
platform: process.platform,
|
||||||
debugMaxLength: runtime.settings.debugMaxLength,
|
memoryUsage: process.memoryUsage(),
|
||||||
editorTheme: runtime.settings.editorTheme,
|
},
|
||||||
flowFile: runtime.settings.flowFile,
|
os: {
|
||||||
disableEditor:runtime.settings.disableEditor,
|
totalmem: os.totalmem(),
|
||||||
debugMaxLength:runtime.settings.debugMaxLength,
|
freemem: os.freemem(),
|
||||||
|
arch: os.arch(),
|
||||||
httpAdminRoot: runtime.settings.httpAdminRoot,
|
loadavg: os.loadavg(),
|
||||||
httpAdminCors: runtime.settings.httpAdminCors ? "HAS SETTING": "NOT SET",
|
platform: os.platform(),
|
||||||
httpNodeAuth: runtime.settings.httpNodeAuth ? "HAS SETTING": "NOT SET",
|
release: os.release(),
|
||||||
|
type: os.type(),
|
||||||
httpNodeRoot: runtime.settings.httpNodeRoot,
|
uptime: os.uptime(),
|
||||||
httpNodeCors: runtime.settings.httpNodeCors ? "HAS SETTING": "NOT SET",
|
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,
|
adminAuth: runtime.settings.adminAuth ? "HAS SETTING" : "NO SETTING",
|
||||||
httpStaticCors: runtime.settings.httpStaticCors,
|
|
||||||
|
|
||||||
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",
|
httpNodeRoot: runtime.settings.httpNodeRoot || "NO SETTING",
|
||||||
uiPort: runtime.settings.uiPort ? "HAS SETTING": "NOT SET",
|
httpNodeCors: runtime.settings.httpNodeCors ? "HAS SETTING" : "NO SETTING",
|
||||||
userDir: runtime.settings.userDir ? "HAS SETTING": "NOT SET",
|
|
||||||
|
|
||||||
version: runtime.settings.version
|
httpStatic: runtime.settings.httpStatic ? "HAS SETTING" : "NO SETTING",
|
||||||
}
|
httpStatic: runtime.settings.httpStaticRoot || "NO SETTING",
|
||||||
}
|
httpStaticCors: runtime.settings.httpStaticCors ? "HAS SETTING" : "NO SETTING",
|
||||||
var admin = {};
|
|
||||||
if(scope == "admin") {
|
uiHost: runtime.settings.uiHost ? "HAS SETTING" : "NO SETTING",
|
||||||
admin = {
|
uiPort: runtime.settings.uiPort ? "HAS SETTING" : "NO SETTING",
|
||||||
httpAdminCors: runtime.settings.httpAdminCors ? runtime.settings.httpAdminCors : "NOT SET",
|
userDir: runtime.settings.userDir ? "HAS SETTING" : "NO SETTING",
|
||||||
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",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
callback(report);
|
||||||
|
|
||||||
|
/** gets a sanitised list containing only the module name */
|
||||||
function listContextModules() {
|
function listContextModules() {
|
||||||
var keys = Object.keys(runtime.settings.contextStorage);
|
const keys = Object.keys(runtime.settings.contextStorage);
|
||||||
var result = {};
|
const result = {};
|
||||||
keys.forEach(e => {
|
keys.forEach(e => {
|
||||||
result[e] = {
|
result[e] = {
|
||||||
module: runtime.settings.contextStorage[e].module
|
module: String(runtime.settings.contextStorage[e].module)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return result;
|
return result;
|
||||||
@ -75,40 +178,24 @@ function buildDiagnosticReport(scope, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var api = module.exports = {
|
module.exports = {
|
||||||
init: function (_runtime) {
|
init: function (_runtime) {
|
||||||
runtime = _runtime;
|
runtime = _runtime;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Gets the node-red diagnostics report
|
* Gets the node-red diagnostics report
|
||||||
* @param {{scope: string}} - settings
|
* @param {{scope: string}} opts - settings
|
||||||
* @return {Promise} - the diagnostics information
|
* @return {Promise} the diagnostics information
|
||||||
* @memberof @node-red/diagnostics
|
* @memberof @node-red/diagnostics
|
||||||
*/
|
*/
|
||||||
get: async function (opts) {
|
get: async function (opts) {
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
opts = opts || {}
|
opts = opts || {}
|
||||||
var scope = opts.scope;
|
|
||||||
try {
|
try {
|
||||||
if (scope === 'admin') {
|
runtime.log.audit({ event: "diagnostics.get", scope: opts.scope }, opts.req);
|
||||||
//admin level info
|
buildDiagnosticReport(opts.scope, (report) => resolve(report));
|
||||||
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({});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
runtime.log.audit({ event: "diagnostics.get", scope: scope }, opts.req);
|
error.status = 500;
|
||||||
reject(error);
|
reject(error);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user