UNPKG

@nodefony/monitoring-bundle

Version:

Nodefony Framework Bundle Monitoring

914 lines (878 loc) 29.3 kB
let useragent = null; try { useragent = require('useragent'); } catch (e) {} /** * The class is a **`monitoring` BUNDLE** . * @module NODEFONY * @main NODEFONY * @class monitoring * @constructor * @param {class} kernel * @param {class} container * */ module.exports = class monitoringBundle extends nodefony.Bundle { constructor(name, kernel, container) { super(name, kernel, container); if (this.kernel.type === "CONSOLE") { return; } this.infoKernel = {}; this.infoBundles = {}; this.httpKernel = this.container.get("httpKernel"); this.webpackService = this.get("webpack"); let ORM = null; let mail = null; let ormName = this.kernel.settings.orm; // MANAGE GIT this.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"); } this.kernel.once("onPreBoot", async (kernel) => { this.templating = this.get("templating"); this.infoKernel.events = {}; for (let event in kernel.notificationsCenter._events) { switch (event) { case "onPreBoot": this.infoKernel.events[event] = { fire: kernel.preboot, nb: 1, listeners: kernel.notificationsCenter._events[event].length }; break; default: this.infoKernel.events[event] = { fire: false, nb: 0, listeners: kernel.notificationsCenter._events[event].length }; kernel.on(event, () => { this.infoKernel.events[event].fire = true; this.infoKernel.events[event].nb = ++this.infoKernel.events[event].nb; }); } } }); this.kernel.once("onBoot", async () => { this.mailer = this.get("mailer"); this.orm = this.get(ormName); if (this.orm) { ORM = { name: this.orm.name, version: this.orm.engine.version, connections: {} }; } this.orm.once("onOrmReady", () => { switch (ORM.name) { case "sequelize": for (let connection in this.orm.connections) { ORM.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) { ORM.connections[connection].db = { config: this.orm.connections[connection].db.config, options: this.orm.connections[connection].db.options, models: {} }; for (let model in this.orm.connections[connection].db.models) { ORM.connections[connection].db.models[model] = { name: model }; } } } break; case "mongoose": for (let connection in this.orm.connections) { ORM.connections[connection] = { state: this.orm.connections[connection].states[this.orm.connections[connection]._readyState], name: this.orm.connections[connection].name, type: "mongodb", db: {} }; let options = { host: this.orm.connections[connection].host + ":" + this.orm.connections[connection].port }; if (this.orm.connections[connection]) { ORM.connections[connection].db = { config: this.orm.connections[connection].config, options: options, models: {} }; for (let model in this.orm.connections[connection].models) { ORM.connections[connection].db.models[model] = { name: model }; } } } break; } }); }); this.kernel.once("onReady", async () => { if (this.mailer && this.mailer.config.transporters) { mail = { transporters: this.mailer.config.transporters, default: this.mailer.config.default }; } }); this.kernel.once("onPostReady", async (kernel) => { //this.debugView = this.httpKernel.getTemplate("monitoringBundle::debugBar.html.twig"); if (this.settings.profiler.active) { this.storageProfiling = this.settings.profiler.storage; } else { this.storageProfiling = null; } this.requestEntity = this.orm.getEntity("requests"); 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: this.kernel.settings.CDN ? true : false, 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 (let bund in kernel.bundles) { this.infoBundles[bund] = {}; this.infoBundles[bund].waitBundleReady = kernel.bundles[bund].waitBundleReady; this.infoBundles[bund].version = kernel.bundles[bund].version; } //console.log(this.infoBundles); for (let 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.log("ADD DEBUG BAR MONITORING", "INFO"); this.bundles = function () { let obj = {}; for (let 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: version, title: title, loader: this.kernel.bundles[bundle].loader }; } return obj; }.call(this); this.syslogContext = new nodefony.Syslog({ moduleName: "CONTEXT", maxStack: 50, defaultSeverity: "INFO" }); this.env = this.kernel.environment; this.app = this.getParameters("bundles.app").App; this.node = process.versions; this.upload = this.container.get("upload"); this.translation = this.container.get("translation"); this.sessionService = this.container.get("sessions"); this.domain = this.translation.defaultDomain; this.nbServices = Object.keys(nodefony.services).length; // TEMPLATING let templating = {}; if (this.templating) { templating = { name: this.templating.name, version: this.templating.version }; } this.service = { 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: ORM, templating: templating, mail: mail }; this.security = function () { let obj = {}; let firewall = this.container.get("security"); if (firewall) { for (let area in firewall.securedAreas) { //console.log(firewall.securedAreas[area]) let myfactory = firewall.securedAreas[area].factories.length ? firewall.securedAreas[area].factories : null; let factory = null; if (myfactory) { factory = myfactory.map((fac) => { return 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; }.call(this); } }); this.kernel.on("onServerRequest", (request /*, response, logString, d*/ ) => { request.nodefony_time = new Date().getTime(); }); this.kernel.on("onRequest", this.onRequest.bind(this)); } onRequest(context /*, resolver*/ ) { if (this.kernel.environment === "prod" && !this.settings.forceDebugBarProd && !context.profiler) { return; } let agent = null; let tmp = null; let myUserAgent = null; context.profiler = this.canMonitoring(context); 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']); } let client = {}; for (let 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 }; } let trans = context.get("translation"); let route = null; let varialblesName = null; let 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: 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 = { id: null, 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.service, git: this.gitInfo, nbServices: this.nbServices, security: this.security, route: route, varialblesName: 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, protocole: context.scheme, cookies: context.cookies, events: {}, twig: [], locale: { default: trans.defaultLocale, domain: trans.defaultDomain }, userAgent: myUserAgent }; for (let 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; } }); } let secu = context.session ? context.session.getMetaBag("security") : null; let token = null; let tokenRoles = null; if (context.token) { tokenRoles = context.token.roles.map((tok) => { return 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: token, user: context.user, firewall: context.security.name, state: context.security.stateLess ? "stateless" : "statefull", context: context.security.sessionContext, tokenRoles: tokenRoles }; } else { if (secu) { context.profiling.context_secure = { name: "OFF", token: secu.token, user: context.user, firewall: secu.firewall, tokenRoles: tokenRoles }; } else { if (token) { context.profiling.context_secure = { name: "OFF", token: token, user: context.user, tokenRoles: tokenRoles }; } else { context.profiling.context_secure = null; } } } if (context.resolver.route && context.resolver.route.defaults) { let tab = context.resolver.route.defaults.controller.split(":"); let 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 }; }); } if (context.request.queryFile) { context.profiling.queryFile = {}; for (let 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 }; } } let settings2 = this.get("httpsServer").defaultSetting2; let 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) => { let 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, isControlledAccess: context.isControlledAccess, accessControl: 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.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, "content-type": context.request.contentType }; context.on("onSendMonitoring", this.onSendMonitoring.bind(this)); } websocketRequest(context) { context.profiling.timeStamp = context.request.nodefony_time; let conf = null; let 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.request.httpRequest.method, protocol: context.scheme, remoteAddress: context.request.remoteAddress, serverConfig: configServer, }; } else { context.profiling.request = { url: context.url, headers: "", method: "", protocol: context.scheme, remoteAddress: context.request.remoteAddress, serverConfig: null, }; } let 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: config, webSocketVersion: context.response.webSocketVersion, message: [], }; if (context.profiler) { context.on("onMessage", (message, Context, direction) => { let ele = { date: new Date().toTimeString(), data: message, direction: 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); } }); }); } 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; } }); } }); if (context.profiler) { this.saveProfile(context) .catch(e => { this.log(e, "ERROR"); }); } } onSendMonitoring(response, context) { context.profiling.timeRequest = (new Date().getTime()) - (context.request.request.nodefony_time) + " ms"; let 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: headers }; if (context.profiler) { /*return this.saveProfile(context, (error , res ) => { if (error) { this.kernel.logger(error, "ERROR"); } if (context && context.response) { context.sended = true; context.response.send(); // END REQUEST return context.close(); } if (error) { throw new Error("MONITORING CAN SAVE REQUEST"); } if ((!context) || (!context.response)) { throw new Error("MONITORING REQUEST ALREADY SENDED !!! "); } });*/ 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; }); } } 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 }); } } canMonitoring(context) { /*if (!context.resolver.route) { return false; }*/ if (context.resolver && context.resolver.route && context.resolver.route.name.match(/^monitoring-/)) { return false; } /*if (!context.resolver.resolve) { return false; }*/ return this.settings.profiler.active; } updateProfile(context, callback) { if (context.profiling) { let id = context.profiling.id; 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 }, { where: { id: 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); } } } saveProfile(context) { if (context.profiling) { switch (this.storageProfiling) { case "syslog": return new Promise((resolve, reject) => { try { this.syslogContext.log(context.profiling); let logProfile = this.syslogContext.getLogStack(); context.profiling.id = logProfile.uid; } catch (e) { return reject(e); } return resolve(context); }); case "orm": let user = null; let data = null; // DATABASE ENTITY if (context.profiling.context_secure) { user = context.profiling.context_secure.user ? context.profiling.context_secure.user.username : ""; } else { user = ""; } 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({ 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, protocole: context.profiling.context.scheme, username: user, data: 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": 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, protocole: context.profiling.context.scheme, username: user, data: 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); } };