Last caller information / MCI

This commit is contained in:
Bryan Ashby 2020-11-26 19:51:00 -07:00
parent 3a7f7750ab
commit 9c7fb16196
No known key found for this signature in database
GPG Key ID: B49EB437951D2542
6 changed files with 120 additions and 25 deletions

View File

@ -236,6 +236,8 @@ function initialize(cb) {
// //
const User = require('./user.js'); const User = require('./user.js');
// :TODO: use User.getUserInfo() for this!
const propLoadOpts = { const propLoadOpts = {
names : [ names : [
UserProps.RealName, UserProps.Sex, UserProps.EmailAddress, UserProps.RealName, UserProps.Sex, UserProps.EmailAddress,
@ -246,7 +248,7 @@ function initialize(cb) {
async.waterfall( async.waterfall(
[ [
function getOpUserName(next) { function getOpUserName(next) {
return User.getUserName(1, next); return User.getUserName(User.RootUserID, next);
}, },
function getOpProps(opUserName, next) { function getOpProps(opUserName, next) {
User.loadProperties(User.RootUserID, propLoadOpts, (err, opProps) => { User.loadProperties(User.RootUserID, propLoadOpts, (err, opProps) => {
@ -273,8 +275,9 @@ function initialize(cb) {
} }
); );
}, },
function initCallsToday(callback) { function initSystemLogStats(callback) {
const StatLog = require('./stat_log.js'); const StatLog = require('./stat_log.js');
const filter = { const filter = {
logName : SysLogKeys.UserLoginHistory, logName : SysLogKeys.UserLoginHistory,
resultType : 'count', resultType : 'count',
@ -288,7 +291,7 @@ function initialize(cb) {
return callback(null); return callback(null);
}); });
}, },
function initUserTransferStats(callback) { function initUserLogStats(callback) {
const StatLog = require('./stat_log'); const StatLog = require('./stat_log');
const entries = [ const entries = [
@ -324,6 +327,33 @@ function initialize(cb) {
return callback(null); return callback(null);
}); });
}, },
function initLastLogin(callback) {
const StatLog = require('./stat_log');
StatLog.getSystemLogEntries(SysLogKeys.UserLoginHistory, 'timestamp_desc', 1, (err, lastLogin) => {
if (err) {
return callback(null);
}
let loginObj;
try
{
loginObj = JSON.parse(lastLogin[0].log_value);
loginObj.timestamp = moment(lastLogin[0].timestamp);
}
catch (e)
{
return callback(null);
}
// For live stats we want to resolve user ID -> name, etc.
const User = require('./user');
User.getUserInfo(loginObj.userId, (err, props) => {
const stat = Object.assign({}, props, loginObj);
StatLog.setNonPersistentSystemStat(SysProps.LastLogin, stat);
return callback(null);
});
});
},
function initMessageStats(callback) { function initMessageStats(callback) {
return require('./message_area.js').startup(callback); return require('./message_area.js').startup(callback);
}, },

View File

@ -98,7 +98,6 @@ const PREDEFINED_MCI_GENERATORS = {
SA : function opAffils() { return StatLog.getSystemStat(SysProps.SysOpAffiliations); }, SA : function opAffils() { return StatLog.getSystemStat(SysProps.SysOpAffiliations); },
SS : function opSex() { return StatLog.getSystemStat(SysProps.SysOpSex); }, SS : function opSex() { return StatLog.getSystemStat(SysProps.SysOpSex); },
SE : function opEmail() { return StatLog.getSystemStat(SysProps.SysOpEmailAddress); }, SE : function opEmail() { return StatLog.getSystemStat(SysProps.SysOpEmailAddress); },
// :TODO: op age, web, ?????
// //
// Current user / session // Current user / session
@ -243,10 +242,6 @@ const PREDEFINED_MCI_GENERATORS = {
UU : function systemUptime() { UU : function systemUptime() {
return moment.duration(process.uptime(), 'seconds').humanize(); return moment.duration(process.uptime(), 'seconds').humanize();
}, },
// :TODO: MCI for core count, e.g. os.cpus().length
// :TODO: cpu load average (over N seconds): http://stackoverflow.com/questions/9565912/convert-the-output-of-os-cpus-in-node-js-to-percentage
NV : function nodeVersion() { return process.version; }, NV : function nodeVersion() { return process.version; },
AN : function activeNodes() { return clientConnections.getActiveConnections().length.toString(); }, AN : function activeNodes() { return clientConnections.getActiveConnections().length.toString(); },
@ -265,8 +260,6 @@ const PREDEFINED_MCI_GENERATORS = {
// //
// System File Base, Up/Download Info // System File Base, Up/Download Info
// //
// :TODO: DD - Today's # of downloads (iNiQUiTY)
//
SD : function systemNumDownloads() { return StatLog.getFriendlySystemStat(SysProps.FileDlTotalCount, 0); }, SD : function systemNumDownloads() { return StatLog.getFriendlySystemStat(SysProps.FileDlTotalCount, 0); },
SO : function systemByteDownload() { SO : function systemByteDownload() {
const byteSize = StatLog.getSystemStatNum(SysProps.FileDlTotalBytes); const byteSize = StatLog.getSystemStatNum(SysProps.FileDlTotalBytes);
@ -308,10 +301,27 @@ const PREDEFINED_MCI_GENERATORS = {
}, },
// :TODO: NT - New users today (Obv/2) // :TODO: NT - New users today (Obv/2)
// :TODO: LC - name of last caller to system (Obv/2)
// :TODO: TZ - Average *system* post/call ratio (iNiQUiTY) // :TODO: TZ - Average *system* post/call ratio (iNiQUiTY)
// :TODO: ?? - Total users on system // :TODO: ?? - Total users on system
LC : function lastCallerUserName() { // Obv/2
const lastLogin = StatLog.getSystemStat(SysProps.LastLogin) || {};
return lastLogin.userName || 'N/A';
},
LD : function lastCallerDate(client) {
const lastLogin = StatLog.getSystemStat(SysProps.LastLogin) || {};
if (!lastLogin.timestamp) {
return 'N/A';
}
return lastLogin.timestamp.format(client.currentTheme.helpers.getDateFormat());
},
LT : function lastCallerTime(client) {
const lastLogin = StatLog.getSystemStat(SysProps.LastLogin) || {};
if (!lastLogin.timestamp) {
return 'N/A';
}
return lastLogin.timestamp.format(client.currentTheme.helpers.getTimeFormat());
},
// //
// Special handling for XY // Special handling for XY

View File

@ -10,6 +10,7 @@
module.exports = { module.exports = {
LoginCount : 'login_count', LoginCount : 'login_count',
LoginsToday : 'logins_today', // non-persistent LoginsToday : 'logins_today', // non-persistent
LastLogin : 'last_login', // object { userId, sessionId, userName, userRealName, timestamp }; non-persistent
FileBaseAreaStats : 'file_base_area_stats', // object - see file_base_area.js::getAreaStats FileBaseAreaStats : 'file_base_area_stats', // object - see file_base_area.js::getAreaStats
FileUlTotalCount : 'ul_total_count', FileUlTotalCount : 'ul_total_count',
@ -25,19 +26,15 @@ module.exports = {
MessageTotalCount : 'message_post_total_count', // total non-private messages on the system; non-persistent MessageTotalCount : 'message_post_total_count', // total non-private messages on the system; non-persistent
MessagesToday : 'message_post_today', // non-private messages posted/imported today; non-persistent MessagesToday : 'message_post_today', // non-private messages posted/imported today; non-persistent
// begin +op non-persistent... SysOpUsername : 'sysop_username', // non-persistent
SysOpUsername : 'sysop_username', SysOpRealName : 'sysop_real_name', // non-persistent
SysOpRealName : 'sysop_real_name', SysOpLocation : 'sysop_location', // non-persistent
SysOpLocation : 'sysop_location', SysOpAffiliations : 'sysop_affiliation', // non-persistent
SysOpAffiliations : 'sysop_affiliation', SysOpSex : 'sysop_sex', // non-persistent
SysOpSex : 'sysop_sex', SysOpEmailAddress : 'sysop_email_address', // non-persistent
SysOpEmailAddress : 'sysop_email_address',
// end +op non-persistent
NextRandomRumor : 'random_rumor', NextRandomRumor : 'random_rumor',
// begin system stat non-persistent... SystemMemoryStats : 'system_memory_stats', // object { totalBytes, freeBytes }; non-persistent
SystemMemoryStats : 'system_memory_stats', // object { totalBytes, freeBytes } SystemLoadStats : 'system_load_stats', // object { average, current }; non-persistent
SystemLoadStats : 'system_load_stats', // object { average, current }
// end system stat non persistent
}; };

View File

@ -630,6 +630,41 @@ module.exports = class User {
); );
} }
static getUserInfo(userId, propsList, cb) {
if (!cb && _.isFunction(propsList)) {
cb = propsList;
propsList = [
UserProps.RealName, UserProps.Sex, UserProps.EmailAddress,
UserProps.Location, UserProps.Affiliations,
];
}
async.waterfall(
[
(callback) => {
return User.getUserName(userId, callback);
},
(userName, callback) => {
User.loadProperties(userId, { names : propsList }, (err, props) => {
return callback(err, Object.assign({}, props, { user_name : userName }));
});
}
],
(err, userProps) => {
if (err) {
return cb(err);
}
const userInfo = {};
Object.keys(userProps).forEach(key => {
userInfo[_.camelCase(key)] = userProps[key] || 'N/A';
});
return cb(null, userInfo);
}
);
}
static isRootUserId(userId) { static isRootUserId(userId) {
return (User.RootUserID === userId); return (User.RootUserID === userId);
} }

View File

@ -30,6 +30,7 @@ const {
const async = require('async'); const async = require('async');
const _ = require('lodash'); const _ = require('lodash');
const assert = require('assert'); const assert = require('assert');
const moment = require('moment');
exports.userLogin = userLogin; exports.userLogin = userLogin;
exports.recordLogin = recordLogin; exports.recordLogin = recordLogin;
@ -176,6 +177,8 @@ function recordLogin(client, cb) {
assert(client.user.authenticated); // don't get in situations where this isn't true assert(client.user.authenticated); // don't get in situations where this isn't true
const user = client.user; const user = client.user;
const loginTimestamp = StatLog.now;
async.parallel( async.parallel(
[ [
(callback) => { (callback) => {
@ -183,7 +186,7 @@ function recordLogin(client, cb) {
return StatLog.incrementSystemStat(SysProps.LoginCount, 1, callback); return StatLog.incrementSystemStat(SysProps.LoginCount, 1, callback);
}, },
(callback) => { (callback) => {
return StatLog.setUserStat(user, UserProps.LastLoginTs, StatLog.now, callback); return StatLog.setUserStat(user, UserProps.LastLoginTs, loginTimestamp, callback);
}, },
(callback) => { (callback) => {
return StatLog.incrementUserStat(user, UserProps.LoginCount, 1, callback); return StatLog.incrementUserStat(user, UserProps.LoginCount, 1, callback);
@ -202,6 +205,24 @@ function recordLogin(client, cb) {
StatLog.KeepType.Max, StatLog.KeepType.Max,
callback callback
); );
},
(callback) => {
// Update live last login information which includes additional
// (pre-resolved) information such as user name/etc.
const lastLogin = {
userId : user.userId,
sessionId : user.sessionId,
userName : user.username,
realName : user.getProperty(UserProps.RealName),
affiliation : user.getProperty(UserProps.Affiliations),
emailAddress : user.getProperty(UserProps.EmailAddress),
sex : user.getProperty(UserProps.Sex),
location : user.getProperty(UserProps.Location),
timestamp : moment(loginTimestamp),
};
StatLog.setNonPersistentSystemStat(SysProps.LastLogin, lastLogin);
return callback(null);
} }
], ],
err => { err => {

View File

@ -100,7 +100,9 @@ There are many predefined MCI codes that can be used anywhere on the system (pla
| `LA` | System load average (e.g. 0.25)<br>(Not available for all platforms) | | `LA` | System load average (e.g. 0.25)<br>(Not available for all platforms) |
| `CL` | System current load percentage<br>(Not available for all platforms) | | `CL` | System current load percentage<br>(Not available for all platforms) |
| `UU` | System uptime in friendly format | | `UU` | System uptime in friendly format |
| `LC` | Last caller to the system (username) |
| `LT` | Time of last caller |
| `LD` | Date of last caller |
Some additional special case codes also exist: Some additional special case codes also exist: