@nodefony/monitoring-bundle
Version:
895 lines (863 loc) • 28.5 kB
JavaScript
let useragent = null;
try {
useragent = require("useragent");
} catch (e) {}
module.exports = class Monitor extends nodefony.Service {
constructor (container, kernel) {
super("MONITOR", container, kernel.notificationsCenter);
this.infoKernel = this.bundle.infoKernel;
this.infoBundles = {};
this.httpKernel = this.container.get("httpKernel");
this.webpackService = this.get("webpack");
this.templating = this.get("templating");
this.mailer = null;
this.infoMailer = null;
this.orm = null;
this.infoOrm = null;
this.storageProfiling = null;
this.requestEntity = null;
this.kernelSetting = null;
this.bundles = null;
if (this.kernel.ready) {
this.mailer = this.get("mailer");
this.orm = this.get(this.kernel.settings.orm);
if (this.orm) {
this.eventOrmReady();
}
this.eventboot();
this.eventReady();
this.eventPostReady();
} else {
this.once("onBoot", async () => this.eventboot());
this.once("onReady", async () => this.eventReady());
this.once("onPostReady", async (kernel) => this.eventPostReady());
}
this.gitInfo = this.initGit();
}
initialize () {
this.on("onServerRequest", (request /* , response, logString, d*/) => {
request.nodefony_time = new Date().getTime();
});
this.on("onRequest", (context) => {
this.onRequest(context);
});
}
async eventboot () {
this.log("Monitoring Event onBoot", "DEBUG");
this.mailer = this.get("mailer");
this.orm = this.get(this.kernel.settings.orm);
this.settings = this.bundle.settings;
this.orm.once("onOrmReady", () => {
if (this.orm) {
this.eventOrmReady();
}
});
if (this.settings.profiler.active) {
this.storageProfiling = this.settings.profiler.storage;
}
}
async eventReady () {
this.log("Monitoring Event onReady", "DEBUG");
if (this.mailer && this.mailer.config.transporters) {
this.infoMailer = {
transporters: this.mailer.config.transporters,
default: this.mailer.config.default
};
}
this.initialize();
}
async eventPostReady () {
this.log("Monitoring Event onPostReady", "DEBUG");
this.kernelSetting = nodefony.extend(true, {}, this.kernel.settings, {
templating: `${this.kernel.settings.templating} ${this.templating.version}`,
orm: this.orm ? `${this.kernel.settings.orm} ${this.orm.engine.version}` : "",
CDN: Boolean(this.kernel.settings.CDN),
node_start: this.kernel.node_start
});
this.cdn = this.kernel.settings.CDN;
delete this.kernelSetting.system.PM2;
delete this.kernelSetting.system.bundles;
this.kernelSetting.servers = {
http: this.kernelSetting.system.servers.http,
https: this.kernelSetting.system.servers.https,
ws: this.kernelSetting.system.servers.ws,
wss: this.kernelSetting.system.servers.wss
};
delete this.kernelSetting.system.servers;
for (const bund in kernel.bundles) {
this.infoBundles[bund] = {};
this.infoBundles[bund].waitBundleReady = this.kernel.bundles[bund].waitBundleReady;
this.infoBundles[bund].version = this.kernel.bundles[bund].version;
}
for (const event in this.kernel.notificationsCenter._events) {
switch (event) {
case "onReady":
this.infoKernel.events[event] = {
fire: kernel.ready,
nb: 0,
listeners: this.kernel.notificationsCenter._events[event].length
};
break;
default:
this.infoKernel.events[event] = nodefony.extend(true, this.infoKernel.events[event], {
listeners: this.kernel.notificationsCenter._events[event].length
});
}
}
if (this.settings.debugBar) {
this.translation = this.container.get("translation");
this.sessionService = this.container.get("sessions");
this.env = this.kernel.environment;
this.app = this.getParameters("bundles.app").App;
this.node = process.versions;
this.upload = this.container.get("upload");
this.domain = this.translation.defaultDomain;
this.nbServices = Object.keys(nodefony.services).length;
this.syslogContext = new nodefony.Syslog({
moduleName: "CONTEXT",
maxStack: 50,
defaultSeverity: "INFO"
});
this.bundles = this.getBundles();
this.security = this.getSecurity();
this.infoTemplate = this.getTemplating();
this.services = this.getServices();
}
// console.log(this.infoKernel, this.infoOrm, this.infoBundles, this.kernelSetting)
}
async eventOrmReady () {
this.log("Monitoring Event onOrmReady", "DEBUG");
this.requestEntity = this.orm.getEntity("requests");
this.infoOrm = {
name: this.orm.name,
version: this.orm.engine.version,
connections: {}
};
switch (this.infoOrm.name) {
case "sequelize":
for (const connection in this.orm.connections) {
this.infoOrm.connections[connection] = {
state: this.orm.connections[connection].state,
name: this.orm.connections[connection].name,
type: this.orm.connections[connection].type,
db: {}
};
if (this.orm.connections[connection].db) {
this.infoOrm.connections[connection].db = {
config: this.orm.connections[connection].db.config,
options: this.orm.connections[connection].db.options,
models: {}
};
for (const model in this.orm.connections[connection].db.models) {
this.infoOrm.connections[connection].db.models[model] = {
name: model
};
}
}
}
break;
case "mongoose":
for (const connection in this.orm.connections) {
this.infoOrm.connections[connection] = {
state: this.orm.connections[connection].states[this.orm.connections[connection]._readyState],
name: this.orm.connections[connection].name,
type: "mongodb",
db: {}
};
const options = {
host: `${this.orm.connections[connection].host}:${this.orm.connections[connection].port}`
};
if (this.orm.connections[connection]) {
this.infoOrm.connections[connection].db = {
config: this.orm.connections[connection].config,
options,
models: {}
};
for (const model in this.orm.connections[connection].models) {
this.infoOrm.connections[connection].db.models[model] = {
name: model
};
}
}
}
break;
}
}
initGit () {
// MANAGE GIT
const gitInfo = {
currentBranch: null
};
try {
this.kernel.git.branch((err, BranchSummary) => {
if (err) {
this.log(err, "WARNING");
return;
}
this.gitInfo.currentBranch = BranchSummary.current;
});
} catch (e) {
this.log(e, "WARNING");
}
return gitInfo;
}
getSecurity () {
const obj = {};
const firewall = this.container.get("security");
if (firewall) {
for (const area in firewall.securedAreas) {
// console.log(firewall.securedAreas[area])
const myfactory = firewall.securedAreas[area].factories.length ? firewall.securedAreas[area].factories : null;
let factory = null;
if (myfactory) {
factory = myfactory.map((fac) => fac.name).join();
} else {
factory = "none";
}
obj[area] = {};
obj[area].pattern = firewall.securedAreas[area].stringPattern;
obj[area].factory = factory;
obj[area].provider = firewall.securedAreas[area].provider ? firewall.securedAreas[area].providerName : null;
obj[area].context = firewall.securedAreas[area].sessionContext;
obj[area].state = firewall.securedAreas[area].stateLess ? "stateless" : "statefull";
}
}
return obj;
}
getTemplating () {
return {
name: this.templating.name,
version: this.templating.version
};
}
getBundles () {
const obj = {};
for (const bundle in this.kernel.bundles) {
let version = null;
let title = null;
if (this.kernel.bundles[bundle].loader === "package") {
version = `${this.kernel.bundles[bundle].package.name}@${this.kernel.bundles[bundle].package.version}`;
title = this.kernel.bundles[bundle].package.description;
} else {
version = `file: ${this.kernel.bundles[bundle].bundleName}@${this.kernel.bundles[bundle].version}`;
title = this.kernel.bundles[bundle].path;
}
obj[bundle] = {
name: this.kernel.bundles[bundle].name,
version,
title,
loader: this.kernel.bundles[bundle].loader
};
}
return obj;
}
getServices () {
return {
upload: {
tmp_dir: this.upload.config.uploadDir,
max_size: nodefony.cli.niceBytes(this.upload.config.maxFileSize)
},
translation: {
defaultLocale: this.translation.defaultLocale,
defaultDomain: this.domain
},
session: {
storage: this.sessionService.settings.handler,
path: this.sessionService.settings.save_path
},
ORM: this.infoOrm,
templating: this.infoTemplate,
mail: this.infoMailer
};
}
onRequest (context /* , resolver*/) {
context.profiler = this.canMonitoring(context);
if (this.kernel.environment === "prod" &&
!this.settings.forceDebugBarProd &&
!context.profiler) {
return;
}
let agent = null;
let tmp = null;
let myUserAgent = null;
try {
if (context.request.headers) {
agent = useragent.parse(context.request.headers["user-agent"]);
tmp = useragent.is(context.request.headers["user-agent"]);
} else {
agent = useragent.parse(context.request.httpRequest.headers["user-agent"]);
tmp = useragent.is(context.request.httpRequest.headers["user-agent"]);
}
const client = {};
for (const ele in tmp) {
if (tmp[ele] === true) {
client[ele] = tmp[ele];
}
if (ele === "version") {
client[ele] = tmp[ele];
}
}
myUserAgent = {
agent: agent.toAgent(),
toString: agent.toString(),
version: agent.toVersion(),
os: agent.os.toJSON(),
is: client
};
} catch (e) {
myUserAgent = {
agent: null,
toString: null,
version: null,
os: null,
is: null
};
}
const trans = context.get("translation");
let route = null;
let varialblesName = null;
const variables = [];
context.resolver.variables
.map((ele) => {
if (typeof ele === "string") {
variables.push(ele);
}
});
if (context.resolver.route) {
route = {
name: context.resolver.route.name,
uri: context.resolver.route.path,
variables,
/* util.inspect(context.resolver.variables, {
depth: 2
}),*/
pattern: context.resolver.route.pattern.toString(),
defaultView: context.resolver.defaultView
};
varialblesName = context.resolver.route.variables;
} else {
route = {
name: "undefined",
uri: "undefined",
variables: util.inspect(context.resolver.variables, {
depth: 2
}),
pattern: "undefined",
defaultView: context.resolver.defaultView
};
}
context.profiling = {
bundle: context.resolver.bundle ? context.resolver.bundle.name : "undefined",
bundles: this.bundles,
cdn: this.cdn,
pwd: process.env.PWD,
env: process.env,
node: this.node,
services: this.services,
git: this.gitInfo,
nbServices: this.nbServices,
security: this.security,
route,
varialblesName,
kernelSettings: this.kernelSetting,
environment: this.env,
debug: this.kernel.debug,
appSettings: this.app,
projectName: this.kernel.projectName,
queryPost: context.request.queryPost,
queryGet: context.request.queryGet,
protocol: context.protocol,
scheme: context.scheme,
cookies: context.cookies,
events: {},
twig: [],
locale: {
default: trans.defaultLocale,
domain: trans.defaultDomain
},
userAgent: myUserAgent
};
for (const event in context.notificationsCenter._events) {
if (event === "onRequest") {
context.profiling.events[event] = {
fire: true,
nb: 1,
listeners: context.notificationsCenter._events[event].length
};
} else {
context.profiling.events[event] = {
fire: false,
nb: 0,
listeners: context.notificationsCenter._events[event].length
};
}
context.on(event, () => {
if (context.profiling) {
// var ele = arguments[0];
context.profiling.events[event].fire = true;
context.profiling.events[event].nb = ++context.profiling.events[event].nb;
}
});
}
const secu = context.session ? context.session.getMetaBag("security") : null;
let token = null;
let tokenRoles = null;
if (context.token) {
tokenRoles = context.token.roles.map((tok) => tok.role).join(" ");
token = {
name: context.token.name,
user: context.token.user,
authenticated: context.token.authenticated,
factory: context.token.factory,
roles: tokenRoles,
provider: context.token.provider ? context.token.provider.name : false
};
}
if (context.security) {
context.profiling.context_secure = {
name: context.security.name,
provider: context.security.providerName,
token,
user: context.user,
firewall: context.security.name,
state: context.security.stateLess ? "stateless" : "statefull",
context: context.security.sessionContext,
tokenRoles
};
} else if (secu) {
context.profiling.context_secure = {
name: "OFF",
token: secu.token,
user: context.user,
firewall: secu.firewall,
tokenRoles
};
} else if (token) {
context.profiling.context_secure = {
name: "OFF",
token,
user: context.user,
tokenRoles
};
} else {
context.profiling.context_secure = null;
}
if (context.resolver.route && context.resolver.route.defaults) {
const tab = context.resolver.route.defaults.controller.split(":");
const contr = tab[1] ? tab[1] : "default";
let filePath = "dynamic";
if (context.resolver.route.filePath) {
filePath = path.basename(path.resolve(context.resolver.route.filePath));
}
context.profiling.routeur = {
bundle: context.resolver.bundle.name,
action: `${tab[2]}Action`,
pattern: context.resolver.route.defaults.controller,
Controller: `${contr}Controller`,
file: filePath
};
} else {
context.profiling.routeur = {
bundle: context.resolver.bundle ? context.resolver.bundle.name : null,
action: context.resolver.actionName,
Controller: context.resolver.controller ? context.resolver.controller.name : null
};
}
if (context.proxy) {
context.profiling.proxy = context.proxy;
} else {
context.profiling.proxy = null;
}
if (context.session) {
context.on("onSaveSession", () => {
context.profiling.session = {
name: context.session.name,
id: context.session.id,
metas: context.session.metaBag(),
attributes: context.session.attributes(),
flashes: context.session.flashBags(),
context: context.session.contextSession,
created: context.session.created,
updated: context.session.updated
};
});
}
if (context.request.queryFile) {
context.profiling.queryFile = {};
for (const ele in context.request.queryFile) {
context.profiling.queryFile[ele] = {
path: context.request.queryFile[ele].path,
mimetype: context.request.queryFile[ele].mimeType,
length: context.request.queryFile[ele].lenght,
fileName: context.request.queryFile[ele].fileName
};
}
}
const settings2 = this.get("httpsServer").defaultSetting2;
const accessControlList = [];
let accessControl = null;
if (context.accessControl) {
accessControl = {
pattern: context.accessControl.pattern,
roles: context.accessControl.roles,
allowRoles: context.accessControl.allowRoles,
ip: context.accessControl.ip,
allowIp: context.accessControl.allowIp,
hosts: context.accessControl.hosts,
requires_channel: context.accessControl.requires_channel,
methods: context.accessControl.methods,
actived: context.accessControl.actived
};
accessControlList.push(context.accessControl.map((access) => {
const obj = [];
for (let i = 0; i < access.roles.length; i++) {
obj.push(access.roles[i].role);
}
return obj;
}));
}
context.profiling.context = {
type: context.type,
pushAllowed: context.pushAllowed,
pushAllow: settings2 ? settings2.enablePush : false,
isAjax: context.isAjax,
secureArea: context.secureArea,
domain: context.domain,
url: context.url,
remoteAddress: context.remoteAddress,
crossDomain: context.crossDomain,
protocol: context.protocol,
scheme: context.scheme,
isControlledAccess: context.isControlledAccess,
accessControl,
accessControlList: tokenRoles
};
switch (context.type) {
case "HTTP":
case "HTTPS":
case "HTTP2":
this.httpRequest(context);
break;
case "WEBSOCKET":
case "WEBSOCKET SECURE":
this.websocketRequest(context);
break;
}
context.on("onView", this.onView.bind(this));
}
httpRequest (context) {
if (context.request.request) {
context.profiling.timeStamp = context.request.request.nodefony_time;
} else {
context.profiling.timeStamp = 0;
}
let content = null;
switch (context.request.contentType) {
case "multipart/form-data":
try {
content = JSON.stringfy(context.request.queryFile);
} catch (e) {
content = null;
}
break;
case "application/xml":
case "text/xml":
case "application/json":
case "text/json":
case "application/x-www-form-urlencoded":
content = context.request.data.toString(context.request.charset);
// content = context.request.query.toString();
break;
default:
content = null;
}
context.profiling.request = {
url: context.url,
method: context.request.method,
protocol: context.protocol,
scheme: context.scheme,
remoteAddress: context.request.remoteAddress,
queryPost: context.request.queryPost,
queryGet: context.request.queryGet,
headers: context.request.headers,
crossDomain: context.crossDomain,
dataSize: context.request.dataSize,
content,
"content-type": context.request.contentType
};
context.on("onSendMonitoring", async (response, context) => await this.onSendMonitoring(response, context));
}
async websocketRequest (context) {
context.profiling.timeStamp = context.request.nodefony_time;
let conf = null;
const configServer = {};
for (conf in context.request.serverConfig) {
if (conf === "httpServer") {
continue;
}
configServer[conf] = context.request.serverConfig[conf];
}
// console.log(context.request.remoteAddress)
// console.log(context.profiling["context"].remoteAddress)
if (context.request.httpRequest) {
context.profiling.request = {
url: context.url,
headers: context.request.httpRequest.headers,
method: context.method,
protocol: context.protocol,
scheme: context.scheme,
remoteAddress: context.request.remoteAddress,
serverConfig: configServer
};
} else {
context.profiling.request = {
url: context.url,
headers: "",
method: context.method,
protocol: context.protocol,
scheme: context.scheme,
remoteAddress: context.request.remoteAddress,
serverConfig: null
};
}
const config = {};
for (conf in context.response.config) {
if (conf === "httpServer") {
continue;
}
config[conf] = context.response.config[conf];
}
context.profiling.response = {
statusCode: context.response.statusCode,
connection: "WEBSOCKET",
config,
webSocketVersion: context.response.webSocketVersion,
message: []
};
if (context.profiler) {
await this.saveProfile(context)
.catch((e) => {
this.log(e, "ERROR");
});
if (context.connection.state === "open") {
context.on("onMessage", (message, Context, direction) => {
const ele = {
date: new Date().toTimeString(),
data: message,
direction
};
try {
// console.log(context.profiling)
if (JSON.stringify(context.profiling.response).length < 60000) {
if (message && context.profiling) {
context.profiling.response.message.push(ele);
}
} else {
context.profiling.response.message.length = 0;
context.profiling.response.message.push(ele);
}
} catch (e) {
this.log(e, "WARNING");
}
this.updateProfile(context, (error /* , result*/) => {
if (error) {
this.kernel.log(error);
}
});
});
}
}
if (context.connection.state === "open") {
context.on("onFinish", (/* Context, reasonCode, description*/) => {
if (context.profiling) {
context.profiling.response.statusCode = context.connection.state;
}
if (context.profiler) {
this.updateProfile(context, (error /* , result*/) => {
if (error) {
this.kernel.log(error);
}
if (context) {
delete context.profiling;
}
});
}
});
}
}
onSendMonitoring (response, context) {
context.profiling.timeRequest = parseInt(new Date().getTime() - context.request.request.nodefony_time, 10);
const headers = response.getHeaders();
context.profiling.response = {
statusCode: response.statusCode,
message: response.response.statusMessage,
size: response.body ? nodefony.cli.niceBytes(response.body.length) : null,
encoding: response.encoding,
"content-type": headers["content-type"] || headers["Content-Type"],
headers
};
if (context.profiler) {
return this.saveProfile(context)
.then((ctx) => {
if (ctx && ctx.response) {
ctx.sended = true;
ctx.response.send();
// END REQUEST
return ctx.close();
}
})
.catch((error) => {
if (error) {
this.kernel.log(error, "ERROR");
}
throw error;
});
}
return Promise.resolve(context);
}
canMonitoring (context) {
if (context.resolver && context.resolver.route && context.resolver.route.name.match(/^monitoring-/)) {
return false;
}
return this.settings.profiler.active;
}
onView (result, context, view, viewParam) {
try {
JSON.stringify(viewParam);
} catch (e) {
viewParam = "view param can't be parse";
}
if (context.profiling && context.profiling.twig) {
context.profiling.twig.push({
file: view
// param:viewParam
});
}
}
updateProfile (context, callback) {
if (context.profiling) {
context.profiling.timeRequest = parseInt(new Date().getTime() - context.profiling.timeStamp, 10);
const {id} = context.profiling;
switch (this.storageProfiling) {
case "syslog":
context.profilingObject.payload = context.profiling;
return;
case "orm":
try {
this.requestEntity.update({
data: JSON.stringify(context.profiling),
state: context.profiling.response.statusCode,
time: context.profiling.timeRequest
}, {
where: {
id
}
}).then((result) => {
this.kernel.log(`ORM REQUEST UPDATE ID : ${id}`, "DEBUG");
callback(null, result);
})
.catch((error) => {
this.kernel.log(error);
callback(error, null);
});
} catch (e) {
throw e;
}
break;
default:
callback(new Error("No PROFILING"), null);
}
}
}
async saveProfile (context) {
// console.log('SAVE ', this.settings, context.profiler, this.storageProfiling)
if (context.profiling) {
context.profiling.timeRequest = parseInt(new Date().getTime() - context.profiling.timeStamp, 10);
switch (this.storageProfiling) {
case "syslog":
return new Promise((resolve, reject) => {
try {
this.syslogContext.log(context.profiling);
const logProfile = this.syslogContext.getLogStack();
context.profiling.id = logProfile.uid;
} catch (e) {
return reject(e);
}
return resolve(context);
});
case "orm":
const user = context.user ? context.user.username : null;
let data = null;
// DATABASE ENTITY
try {
/* console.log(require('util').inspect(context.profiling, {
depth: 100
}));*/
data = JSON.stringify(context.profiling);
} catch (e) {
throw e;
}
switch (this.kernel.getOrm()) {
case "sequelize":
return this.requestEntity.create({
remoteAddress: context.profiling.context.remoteAddress,
userAgent: context.profiling.userAgent.toString,
url: context.profiling.request.url,
route: context.profiling.route.name,
method: context.profiling.request.method,
state: context.profiling.response.statusCode,
protocol: context.protocol,
time: context.profiling.timeRequest,
scheme: context.scheme,
username: user,
data
}, {
isNewRecord: true
})
.then((request) => {
this.kernel.log(`ORM REQUEST SAVE ID :${request.id}`, "DEBUG");
if (context && context.profiling) {
context.profiling.id = request.id;
}
return context;
})
.catch((error) => {
throw error;
});
case "mongoose":
const myuser = await this.orm.getEntity("user").findOne({
username: user
});
return this.requestEntity.create({
id: null,
remoteAddress: context.profiling.context.remoteAddress,
userAgent: context.profiling.userAgent.toString,
url: context.profiling.request.url,
route: context.profiling.route.name,
method: context.profiling.request.method,
state: context.profiling.response.statusCode,
protocol: context.protocol,
time: context.profiling.timeRequest,
scheme: context.scheme,
user: myuser ? myuser._id : null,
data
})
.then((request) => {
this.kernel.log(`ORM REQUEST SAVE ID :${request._id}`, "DEBUG");
if (context && context.profiling) {
context.profiling.id = request.id;
}
return context;
})
.catch((error) => {
throw error;
});
}
break;
default:
throw new Error("No PROFILING driver");
}
}
return Promise.resolve(context);
}
};