UNPKG

sgapps-server

Version:
378 lines (344 loc) 9.38 kB
var _fs = require("fs"); var _path = require("path"); var _cluster = require("cluster"); /** * @class * @name AccessLogger * @description Access Logger for HTTP Web Servers */ function AccessLogger() { this.months = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ]; /** * @memberof AccessLogger# * @name combined * @type {boolean} * @description log format as combined, with user agent: %h %e %^[%x] "%r" %s %b "%R" "{%u}" */ this.combined = false; /** * @memberof AccessLogger# * @name logsIncludeHostname * @type {boolean} * @description log format for vhosts %v, ex: %h %e %^[%x] "{%v}" "%r" %s %b "%R" */ this.logsIncludeHostname = false; }; /** * @memberof AccessLogger# * @description for go access: tail -c 67108864 -f '/var/logs/default/2024/2/data-master.log' | goaccess --log-format='%h %e %^[%x] "%v" "%r" %s %b "%R" "%u"' --date-format='%d/%b/%Y:%H:%M:%S %z' --time-format='%d/%b/%Y:%H:%M:%S %z' - * with combined and logger %h %e %^[%x] "%r" %s %b "%R" * @method logRequest * @param {IncomingMessage} request * @param {ServerResponse} response * @returns {string} */ AccessLogger.prototype.logRequest = function (request, response) { // %h %e %^[%x] "{%v}" "%r" %s %b "%R" "{%u}" return this.getRemoteIp(request) + ' - ' + this.getUsername(request) + ' [' + this.formattedDate(new Date()) + '] ' + ( this.logsIncludeHostname ? ( '"' + (request.headers.host + '').toLowerCase() + '" ' ) : '-' ) + '"' + (request.method || '-') + ' ' + (request.url || '-') + ' ' + this.getProtocol(request) + '/' + request.httpVersion + '" ' + response.statusCode + ' ' + ((response.socket || response.connection || {}).bytesWritten || '-') + ' "' + (this.getReferer(request) || "-" ) + '"' + ( this.combined ? ( ' "' + (request.headers['user-agent'] || "-") + '"' ) : '' ); }; /** * @memberof AccessLogger# * @method formattedDate * @param {Date} timeStamp * @returns {string} */ AccessLogger.prototype.formattedDate = function formattedDate(timeStamp) { var _2d = function (i) { return ((i < 10) ? ('0' + i) : ( i > 99 ? ( i + '' ) : ('' + i) )); }; return _2d(timeStamp.getUTCDate()) + '/' + this.months[timeStamp.getMonth()].substring(0, 3) + '/' + _2d(timeStamp.getUTCFullYear()) + ':' + _2d(timeStamp.getUTCHours()) + ':' + _2d(timeStamp.getUTCMinutes()) + ':' + _2d(timeStamp.getUTCSeconds()) + ' +0000'; }; /** * @memberof AccessLogger# * @method getUsername * @param {IncomingMessage|SGAppsServerRequest} request * @returns {string} */ AccessLogger.prototype.getUsername = function getUsername(request) { var username = "-"; //@ts-ignore if (request.session && request.session.user) { //@ts-ignore username = request.session.user ? request.session.user.username : ''; } //@ts-ignore if (username !== '-' && request.session && (request.session.id || request.session._id)) { //@ts-ignore username = request.session._id || request.session.id; } return username; }; /** * @memberof AccessLogger# * @method getReferer * @param {IncomingMessage|SGAppsServerRequest} request * @returns {string} */ AccessLogger.prototype.getReferer = function (request) { //@ts-ignore return (request.request || request).headers.referer || (request.request || request).headers.referrer || null; }; /** * @memberof AccessLogger# * @method getProtocol * @param {IncomingMessage|SGAppsServerRequest} request * @returns {string} */ AccessLogger.prototype.getProtocol = function (request) { /** * @private * @type {IncomingMessage} */ //@ts-ignore var message = (request.request || request); if (message.socket && message.socket.localPort === 443) { return "HTTPS"; } return "HTTP"; }; /** * @memberof AccessLogger# * @method getSize * @param {Buffer|string} data * @returns {number} */ AccessLogger.prototype.getSize = function (data) { if (data === null) return 0; if (Buffer.isBuffer(data)) { return data.length; } if (typeof data === 'string') { return Buffer.byteLength(data); } return null; }; /** * @memberof AccessLogger# * @method getRemoteIp * @param {IncomingMessage} request * @returns {string} */ AccessLogger.prototype.getRemoteIp = function (request) { var getData = function (data) { var result = data; if (Array.isArray(data)) result = data[0]; if (result) { result += ''; result = result.replace(/[\s\n]+/g, ',').replace(/\,+/g, ',') } return result; }; return getData(request.headers['x-forwarded-for']) || getData(request.headers['X-Forwarded-For']) || getData(request.connection.remoteAddress) || "???.???.???.???"; }; /** * @memberof AccessLogger * @callback AccessLoggerHandle * @param {string} dataLog * @returns {null|string} */ /** * @memberof AccessLogger * @typedef {object} AccessLoggerPath * @property {boolean} [isEnabled=false] * @property {string|null} [path] file path where logs will be written, placeholders: {year} {month} {date} {day} {pid} {worker-id} * @property {boolean} [waitAllHandlers=false] file path where logs will be written * @property {AccessLogger.AccessLoggerHandle|null} [handle] */ /*** * @private * @type {Object<string,import("fs").WriteStream>} */ var AccessLoggerWriteStreams = {}; var AccessLoggerWriter = function (path, data) { var err; var logPath = _path.resolve(path); if (logPath in AccessLoggerWriteStreams) { if (AccessLoggerWriteStreams[logPath] !== null) { if (AccessLoggerWriteStreams[logPath].writable) { AccessLoggerWriteStreams[logPath].write(data + '\n'); } } } else { var date = new Date(); var filePath = logPath.replace( '{pid}', process.pid + '' ).replace( '{year}', date.getFullYear() + '' ).replace( '{month}', (date.getMonth() + 1) + '' ).replace( '{date}', date.getDate() + '' ).replace( '{day}', date.getDay() + '' ).replace( '{worker-id}', ( _cluster.worker ? _cluster.worker.id : 'master' ) + '' ); var dirPath = _path.dirname(filePath); var err, dataStream; try { _fs.mkdirSync(dirPath, { recursive: true }); dataStream = _fs.createWriteStream( filePath, { flags: 'a', mode: parseInt('0644', 8) } ); if (dataStream.writable) { dataStream.write(data + '\n'); } AccessLoggerWriteStreams[logPath] = dataStream; } catch (err) { AccessLoggerWriteStreams[logPath] = null; }; } }; /** * @private * @function * @param {SGAppsServerRequest} request * @param {SGAppsServerResponse} response * @param {SGAppsServer} server */ var AccessLoggerHandler = function (server, request, response) { var logData = server.AccessLogger.logRequest(request.request, response.response); var pathId, loggerPath, updatedLogData = logData, currentLogData; var paths = []; for (pathId in server.AccessLoggerPaths) { if (!updatedLogData) continue; loggerPath = server.AccessLoggerPaths[pathId]; if (loggerPath.isEnabled) { if (loggerPath.handle) { currentLogData = loggerPath.handle(logData); } else { currentLogData = updatedLogData; } if (currentLogData) { if (loggerPath.waitAllHandlers) { updatedLogData = currentLogData; if (loggerPath.path) { paths.push(loggerPath.path); } } else { if (loggerPath.path) { AccessLoggerWriter(loggerPath.path, currentLogData); } } } } } for (pathId in request.AccessLoggerPaths) { if (!updatedLogData) continue; loggerPath = server.AccessLoggerPaths[pathId]; if (loggerPath.isEnabled) { if (loggerPath.handle) { currentLogData = loggerPath.handle(logData); } else { currentLogData = updatedLogData; } if (currentLogData) { if (loggerPath.waitAllHandlers) { updatedLogData = currentLogData; if (loggerPath.path) { paths.push(loggerPath.path); } } else { if (loggerPath.path) { AccessLoggerWriter(loggerPath.path, currentLogData); } } } } } if (updatedLogData) { paths.forEach(function (path) { AccessLoggerWriter(path, updatedLogData); }); } }; /** * this decorator is not enabled by default * @memberof SGAppsServerDecoratorsLibrary * @method AccessLoggerDecorator * @param {SGAppsServerRequest} request * @param {SGAppsServerResponse} response * @param {SGAppsServer} server * @param {function} callback */ var AccessLoggerDecorator = function (request, response, server, callback) { if ( request === null && response === null && server ) { /** * @memberof SGAppsServer# * @var {AccessLogger} AccessLogger */ server.AccessLogger = new AccessLogger(); /** * @memberof SGAppsServer# * @var {Object<string,AccessLogger.AccessLoggerPath>} AccessLoggerPaths */ server.AccessLoggerPaths = {}; } else { /** * @memberof SGAppsServerRequest# * @var {Object<string,AccessLogger.AccessLoggerPath>} AccessLoggerPaths */ request.AccessLoggerPaths = {}; response.response.on('close', function () { AccessLoggerHandler(server, request, response); }); } callback(); }; AccessLoggerDecorator.AccessLogger = AccessLogger; module.exports = AccessLoggerDecorator;