/**
* _ _
* | \| |
* | .` |
* |_|\_|eptune
*
* Capstone Project 2022
*
* Logging module
*/
const EventEmitter = require("node:events");
const fs = require("node:fs");
const Util = require("node:util");
class ExtensibleFunction extends Function {
constructor(f) {
return Object.setPrototypeOf(f, new.target.prototype);
}
}
/**
* Provides an easy to use logging functionality
*
* Outputs to the console and to a file (if enabled)
*
* This class is to be used as a manager, represents a log. To log to that log, use `getLogger` to return a `Logger` that can be used to actual log data.
*
* ```javascript
* const LogMan = require('logman');
*
* var myLog = LogMan("application");
* var securityLogger = myLog.getLogger("SecurityObj");
* var miscLogger = myLog.getLogger("Misc");
*
* securityLogger.info("Security looking good!"); // Outputs: [Security][Info] Security looking good!
* miscLogger.warn("Something went wrong!"); // Outputs: [Misc][Warn] Something went wrong!
* ```
*
* You can log any string, but note if you pass an object we'll _try_ our best to print it using `Util.inspect`
*
*
* Close event tells you this logger is done.
*
* Events for each level, such as `logMan.on('info', (sectionName, message) => console.log);`
*
* Or for generic logs: `logMan.on('log', (level, sectionName, message) => console.log);`
*
* When a call to getLogger() is made: `logMan.on('newLogger', (loggerName) => console.log);`
*
*/
class LogMan extends EventEmitter {
/**
* The name of this log (application, administration, security)
* @type {string}
*/
#logName;
get logName() {
return this.#logName;
}
/**
* Write stream that writes to a file.
* @type {fs.WriteStream}
*/
#logFile;
/**
* Name of the log file (dynamic, made at runtime, logFolde + "/" + logName + ".log")
* @type {string}
*/
#logFileName;
/**
* Whether the log is open (ready to receive data) or closed (not available). Default false, but when `Logger.close()` is called and the `logFile` is no longer open, this flips to `true`.
* @type {boolean}
*/
#closed = false;
get closed() {
return this.#closed || this.#logFile.closed;
}
get logFileName() {
return this.#logFileName;
}
/**
* String of text at the front of ALL messages.
*
* You can include %date% for the date and %time% for the time
*
* %logName% for the name of this log
*
* `<consolePrefix><consolePrefixes.level> message <consoleSufixes.level><consoleSufix>`
* @type {string}
*/
consolePrefix = "";
/**
* String of text at the end of ALL messages (reset color)
*
* `<consolePrefix><consolePrefixes.level> message <consoleSufixes.level><consoleSufix>`
* @type {string}
*/
consoleSufix = "\x1b[0m";
/**
* The maximum number of characters we'll print to the console each message.
*
* @type {int}
*/
consoleMessageCharacterLimit = 1000;
/**
* This is the string appended on to a message that we've fit to the consoleMessageCharacterLimit.
*
* That is, if your message is too long we'll truncate it fit within your limit and add this string to the end to signal the string was truncated.
*
* @type {string}
*/
consoleMessageCharacterLimitString = " ...|";
/**
* New line character in log file
*
* `\r\n` for Win32
*
* `\n` for not Win32
*/
fileLineTerminator = (process.platform === "win32")? "\r\n" : "\n";
/**
* @typedef {object} logLevelsString
* @property {string} critical
* @property {string} error
* @property {string} warn
* @property {string} info
* @property {string} http
* @property {string} verbose
* @property {string} debug
* @property {string} silly
*/
/**
* @typedef {object} logLevelsBoolean
* @property {boolean} critical
* @property {boolean} error
* @property {boolean} warn
* @property {boolean} info
* @property {boolean} http
* @property {boolean} verbose
* @property {boolean} debug
* @property {boolean} silly
*/
/**
* String in front of messages logged to the console
*
* `<consolePrefix><consolePrefixes.level> message <consoleSufixes.level><consoleSufix>`
* @type {logLevelsString}
*/
consolePrefixes = {
critical: "\x1b[91m", // Red, but bright
error: "\x1b[31m", // Red
warn: "\x1b[31m", // Red
info: "",
http: "",
verbose: "",
debug: "",
silly: "",
}
/**
* String at the end of messages logged to the console
*
* `<consolePrefix><consolePrefixes.level> message <consoleSufixes.level><consoleSufix>`
* @type {logLevelsString}
*/
consoleSufixes = {
critical: "",
error: "",
warn: "",
info: "",
http: "",
verbose: "",
debug: "",
silly: "",
}
/**
* Which levels to output to the console, (default is all, except debug and silly if `process.env.NODE_ENV !== "development"`)
* @type {logLevelsBoolean}
*/
consoleDisplayLevel = {
critical: true,
error: true,
warn: true,
info: true,
http: true,
verbose: true,
debug: (process.env.NODE_ENV === "development")? true : false,
silly: (process.env.NODE_ENV === "development")? true : false,
}
/**
* Which levels to make a ding sound on (output bell character at the end of the console suffix)
* @type {logLevelsBoolean}
*/
consoleBeepOnLevel = {
critical: true,
error: false,
warn: false,
info: false,
http: false,
verbose: false,
debug: false,
silly: false,
}
/**
* String of text at the front of ALL messages written to FILE
*
* You can include %date% for the date and %time% for the time
*
* %logName% for the name of this log
*
* `<filePrefix><filePrefixes.level> message <fileSufixes.level><fileSufix><fileLineTerminator>`
* @type {string}
*/
filePrefix = "[%date%T%time%]";
/**
* String of text at the end of ALL messages written to FILE
*
* `<filePrefix><filePrefixes.level> message <fileSufixes.level><fileSufix><fileLineTerminator>`
* @type {string}
*/
fileSufix = "";
/**
* The maximum number of characters written out each log message to the file.
*
* @type {int}
*/
fileMessageCharacterLimit = 7500;
/**
* This is the string appended on to a message that we've fit to the fileMessageCharacterLimit.
*
* That is, if your message is too long we'll truncate it fit within your limit and add this string to the end to signal the string was truncated.
*
* @type {string}
*/
fileMessageCharacterLimitString = " ...|";
/**
* String in front of messages written to the file
*
* `<filePrefix><filePrefixes.level> message <fileSufixes.level><fileSufix><fileLineTerminator>`
* @type {logLevelsString}
*/
filePrefixes = {
critical: "",
error: "",
warn: "",
info: "",
http: "",
verbose: "",
debug: "",
silly: "",
}
/**
* String at the end of messages written to the file
*
* `<filePrefix><filePrefixes.level> message <fileSufixes.level><fileSufix><fileLineTerminator>`
* @type {logLevelsString}
*/
fileSufixes = {
critical: "",
error: "",
warn: "",
info: "",
http: "",
verbose: "",
debug: "",
silly: "",
}
/**
* Which log levels are written to the log file (default is all, except debug and silly if `process.env.NODE_ENV !== "development"`)
* @type {logLevelsBoolean}
*/
fileWriteLevel = {
critical: true,
error: true,
warn: true,
info: true,
http: true,
verbose: true,
debug: true,
silly: true,
}
/**
* Whether we output to the console. True by default
* @type {boolean}
*/
outputToConsole = true;
/**
* Whether we output to the console. True by default
* @type {boolean}
*/
outputToFile = true;
/**
* When logging objects, this dictates how deep we render the object. Default 2
* @type {number}
*/
objectRenderDepth = 2;
/**
* @typedef {object} ConstructorOptions
* @property {logLevelsString} [consolePrefixes] - String in front of messages logged to the console
* @property {logLevelsString} [consoleSufixes] - String at the end of messages logged to the console
* @property {string} [consolePrefix=""] - String of text at the front of ALL messages.
* @property {string} [consoleSufix="\x1b[0m"] - String of text at the end of ALL messages (reset color)
*
* @property {logLevelsString} [filePrefixes] - String in front of messages written to file
* @property {logLevelsString} [fileSufixes] - String at the end of messages written to file
* @property {string} [filePrefix="[%date%-%time%]"] - String of text at the front of ALL messages written to file
* @property {string} [fileSufix=""] - String of text at the end of ALL messages written to file
*
* @property {logLevelsBoolean} [consoleDisplayLevel] - Which levels to output to the console, (default is all, except debug and silly if `process.env.NODE_ENV !== "development"`)
* @property {logLevelsBoolean} [consoleBeepOnLevel] - Which levels to make a ding sound on (output bell character)
* @property {logLevelsBoolean} [fileWriteLevel] - Which log levels are written to the log file (default is all)
*
* @property {string} [fileLineTerminator="\r\n"] - New line character, sets to appropriate value based on platform.
* @property {boolean} [outputToConsole=true] - Output to the console
* @property {boolean} [outputToFile=true] - Output to the file
*
* @property {number} [objectRenderDepth=2] - When logging objects, this dictates how deep we render the object. Default 2
* @property {boolean} [cleanLog=false] - Delete any log file
*
* @property {number} [consoleMessageCharacterLimit=1000] - The maximum number of characters we'll print to the console each message.
* @property {string} [consoleMessageCharacterLimitString="...|"] - This is the string appended on to a message that we've fit to the consoleMessageCharacterLimit.
* @property {number} [fileMessageCharacterLimit=7500] - The maximum number of characters written out each log message to the file.
* @property {string} [fileMessageCharacterLimitString="...|"] - This is the string appended on to a message that we've fit to the fileMessageCharacterLimit.
*/
/**
* Constructor, provide the folder containing the logs and name of this log.
*
* Logs are saved as: logFolder/logName.log
*
* @param {string} logName
* @param {string} [logFolder="."]
* @param {ConstructorOptions} [options]
*/
constructor(logName, logFolder, options) {
super();
if (typeof logName !== "string")
throw new TypeError("logName expected string got " + (typeof logName).toString());
if (logFolder === undefined)
logFolder = ".";
if (typeof logFolder !== "string")
throw new TypeError("logFolder expected string got " + (typeof logFolder).toString());
this.#logFileName = logFolder + "/" + logName + ".log";
this.#logName = logName;
// Make log directory
if (fs.existsSync(logFolder) === true) { // Exists
if (!fs.lstatSync(logFolder).isDirectory()) { // Is not dir
fs.rmSync(logFolder);
fs.mkdirSync(logFolder);
}
} else { // Does not exist
fs.mkdirSync(logFolder);
}
let flag = 'a';
if (options !== undefined)
if (options.cleanLog === true)
flag = 'w';
this.#logFile = fs.createWriteStream(this.#logFileName, {flags: flag});
// Set options
if (options !== undefined && typeof options === "object") {
if (options.consolePrefixes !== undefined) {
for (var [key, value] of Object.entries(options.consolePrefixes)) {
if (typeof value === "string" && this.consolePrefixes[key] !== undefined)
this.consolePrefixes[key] = value; // Only add strings, and if valid level
}
}
if (options.consoleSufixes !== undefined) {
for (var [key, value] of Object.entries(options.consoleSufixes)) {
if (typeof value === "string" && this.consoleSufixes[key] !== undefined)
this.consoleSufixes[key] = value; // Only add strings, and if valid level
}
}
if (options.filePrefixes !== undefined) {
for (var [key, value] of Object.entries(options.filePrefixes)) {
if (typeof value === "string" && this.filePrefixes[key] !== undefined)
this.filePrefixes[key] = value; // Only add strings, and if valid level
}
}
if (options.fileSufixes !== undefined) {
for (var [key, value] of Object.entries(options.fileSufixes)) {
if (typeof value === "string" && this.fileSufixes[key] !== undefined)
this.fileSufixes[key] = value; // Only add strings, and if valid level
}
}
// Displayable / write-able
if (options.consoleDisplayLevel !== undefined) {
for (var [key, value] of Object.entries(options.consoleDisplayLevel)) {
if (typeof value === "boolean" && this.consoleDisplayLevel[key] !== undefined)
this.consoleDisplayLevel[key] = value; // Only add boolean, and if valid level
}
}
if (options.fileWriteLevel !== undefined) {
for (var [key, value] of Object.entries(options.fileWriteLevel)) {
if (typeof value === "boolean" && this.fileWriteLevel[key] !== undefined)
this.fileWriteLevel[key] = value; // Only add boolean, and if valid level
}
}
// Beep
if (options.consoleBeepOnLevel !== undefined) {
for (var [key, value] of Object.entries(options.consoleBeepOnLevel)) {
if (typeof value === "boolean" && this.consoleBeepOnLevel[key] !== undefined)
this.consoleBeepOnLevel[key] = value; // Only add boolean, and if valid level
}
}
if (typeof options.consoleSufix === "string")
this.consoleSufix = options.consoleSufix;
if (typeof options.consolePrefix === "string")
this.consolePrefix = options.consolePrefix;
if (typeof options.fileSufix === "string")
this.fileSufix = options.fileSufix;
if (typeof options.filePrefix === "string")
this.filePrefix = options.filePrefix;
if (typeof options.fileLineTerminator === "boolean")
this.fileLineTerminator = options.fileLineTerminator;
if (typeof options.outputToConsole === "boolean")
this.outputToConsole = options.outputToConsole;
if (typeof options.outputToFile === "boolean")
this.outputToFile = options.outputToFile;
if (typeof options.consoleMessageCharacterLimit === "string")
this.consoleMessageCharacterLimit = options.consoleMessageCharacterLimit;
if (typeof options.consoleMessageCharacterLimitString === "string")
this.consoleMessageCharacterLimitString = options.consoleMessageCharacterLimitString;
if (typeof options.fileMessageCharacterLimit === "string")
this.fileMessageCharacterLimit = options.fileMessageCharacterLimit;
if (typeof options.fileMessageCharacterLimitString === "string")
this.fileMessageCharacterLimitString = options.fileMessageCharacterLimitString;
}
if (this.#logFile.writable) {
let dateTime = new Date();
let date = dateTime.toISOString().split('T')[0];
let time = dateTime.toISOString().split('T')[1].split('.')[0]; // very cool
this.#logFile.write(this.fileLineTerminator + this.fileLineTerminator + "--- Logger {" + logName + "} started @" + time + " on " + date + " ---" + this.fileLineTerminator);
}
}
/**
* Outputs the message to the console (if enabled) and file (if enabled)
* @fires LogMan#log
* @param {string} [sectionName] - Name of the Logger logging this. If undefined we'll ignore it
* @param {string} - level The level of this data (info, warn, etc)
* @param {any} msg - Message being logged. Wish it was a string, but we'll try our best.
* @param {boolean} [outputToConsole=true] - Output this particular message to the console. Defaults to true
*/
#log(sectionName, level, msg, outputToConsole) {
if (this.#closed === true)
throw new Error("Log is closed. (Someone called Logger.close())");
if (typeof sectionName !== "string" && sectionName !== undefined)
throw new TypeError("sectionName expected string got " + (typeof sectionName).toString());
if (typeof level !== "string")
throw new TypeError("level expected string got " + (typeof level).toString());
if (typeof msg === "object") {
msg = Util.inspect(msg, {depth: (this.objectRenderDepth!=undefined)? this.objectRenderDepth : 2});
} else if (typeof msg !== "string") {
msg = (new String(msg)).toString();
}
outputToConsole = (outputToConsole===false)? false : true; // Set to false if false, true if literally anything else.
let l = level.toLowerCase();
let dateTime = new Date();
let date = dateTime.toISOString().split('T')[0];
let time = dateTime.toISOString().split('T')[1].split('.')[0]; // very cool
var consolePrefix = this.consolePrefix.toString();
if (this.consolePrefixes[l] !== undefined)
consolePrefix += this.consolePrefixes[l].toString();
consolePrefix = consolePrefix.replace("%date%", date)
consolePrefix = consolePrefix.replace("%time%", time)
consolePrefix = consolePrefix.replace("%logName%", this.#logName);
var consoleMessage = ((sectionName!==undefined)? ("[" + sectionName + "]") : "") + "[" + level + "] " + msg; // Only display section name if defined
var fileMessage = "[" + level + "]" + ((sectionName!==undefined)? ("[" + sectionName + "] ") : " ") + msg; // Only display section name if defined
// File: [Debug][Section] msg... || console: [Section][Debug] msg ...
var consoleSufix = "";
if (this.consoleSufixes[l] !== undefined)
consoleSufix += this.consoleSufixes[l].toString();
consoleSufix += this.consoleSufix.toString();
let maxConsoleLenght = (this.consoleMessageCharacterLimit !== undefined? this.consoleMessageCharacterLimit : 1000);
let truncateString = this.consoleMessageCharacterLimitString !== undefined? this.consoleMessageCharacterLimitString : " ...|";
if (consoleMessage.length > maxConsoleLenght) {
consoleMessage = consoleMessage.substring(0, maxConsoleLenght-(truncateString.length)); // Limit the number of characters
consoleMessage += truncateString;
}
consoleMessage = consolePrefix + consoleMessage + consoleSufix;
if (this.outputToConsole == true && outputToConsole === true) {
if (this.consoleBeepOnLevel[l] === true) {
consoleSufix += "\u0007";
}
if (this.consoleDisplayLevel[l] === true) { // Output?
if (l == "critical" || l == "error")
console.error(consoleMessage);
else if (l == "warn")
console.warn(consoleMessage);
else
console.log(consoleMessage);
}
}
if (this.outputToFile == true) {
if (!this.#logFile.closed && this.fileWriteLevel[l] === true) {
var filePrefix = this.filePrefix.toString();
if (this.filePrefixes[l] !== undefined)
filePrefix += this.filePrefixes[l].toString();
filePrefix = filePrefix.replace("%date%", date)
filePrefix = filePrefix.replace("%time%", time)
filePrefix = filePrefix.replace("%logName%", this.#logName);
var fileSufix = "";
if (this.fileSufixes[l] !== undefined)
fileSufix += this.fileSufixes[l].toString();
fileSufix += this.fileSufix.toString();
// the regex strips any ansi stuff
fileMessage = fileMessage.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, ""); // Strip ANSI
let maxFileMessageLenght = (this.fileMessageCharacterLimit !== undefined? this.fileMessageCharacterLimit : 1000);
let truncateString = this.fileMessageCharacterLimitString !== undefined? this.fileMessageCharacterLimitString : " ...|";
if (fileMessage.length > maxFileMessageLenght) {
fileMessage = fileMessage.substring(0, maxFileMessageLenght-(truncateString.length)); // Limit the number of characters
fileMessage += truncateString;
}
this.#logFile.write(filePrefix + fileMessage + fileSufix + this.fileLineTerminator);
}
}
//this.emit(l, sectionName, msg.toString()); // If you had the error type it would crash the whole thing!
this.emit('log', l, sectionName, msg.toString());
}
/**
* Closes the log file, no logging is permitted to file OR console after this. You can reopen via `Logger.reopen()`
* Only works if `this.outputToFile` is true.
* @fires LogMan#close
*/
close() {
let dateTime = new Date();
let date = dateTime.toISOString().split('T')[0];
let time = dateTime.toISOString().split('T')[1].split('.')[0]; // very cool
this.#logFile.write("--- Logger {" + this.#logName + "} ended @" + time + " on " + date + " ---" + this.fileLineTerminator);
this.#logFile.end();
this.#closed = true;
this.emit("close");
}
/**
* Reopens the log files. Permits logging if previously closed.
*/
open() {
if (this.closed) {
if (this.#logFile.writable) {
let dateTime = new Date();
let date = dateTime.toISOString().split('T')[0];
let time = dateTime.toISOString().split('T')[1].split('.')[0]; // very cool
this.#logFile.write("--- Logger {" + logName + "} reopened @" + time + " on " + date + " ---" + this.fileLineTerminator);
this.#closed = false;
}
}
}
/**
* Gives you a logger object so you can actually log data out.
* @fires Logger#newLogger
*
* @param {string} loggerName - Name of this logger (section, class)
* @param {boolean} [hideSectionName] - Allows you to hide the name of this logger
*
* @return {Logger} Logger instance
*/
getLogger(loggerName, hideSectionName) {
this.emit("newLogger", loggerName);
return new Logger((name, level, msg, printOnConsole) => {
this.#log((hideSectionName===true)? undefined : name, level, msg, printOnConsole);
}, loggerName);
}
}
/**
* Class used to actual log data. Can treat an instance like a function, will just log as info.
* Levels include: (RFC5424)
*
* error: `0`, warn: `1`, info: `2`, http: `3`, verbose: `4`, debug: `5`, silly: `6`
*/
class Logger extends ExtensibleFunction {
/**
* Logger name (section name)
* @type {string}
*/
#name;
/**
* Parent log function
* @type {function}
*/
#logManLogFunction
#options = {
objectRenderDepth: 2,
};
/**
* Constructor, pass the logging function and log name
* @param {function} logManLogFunction Function called when we want to log data
* @param {string} logName Name of this log (section)
*/
constructor(logManLogFunction, logName, options) {
super(function(msg, outputToConsole) {
// `this` isn't a thing here, we're in the shadow realm. Work around:
if (typeof msg === "object") {
var uO = {depth: 2}
if (options !== undefined && typeof options === "object")
if (options.objectRenderDepth !== undefined)
uO.depth = options.objectRenderDepth;
msg = Util.inspect(msg, uO);
} else if (typeof msg !== "string") {
msg = (new String(msg)).toString();
}
outputToConsole = (outputToConsole===false)? false : true; // Set to false if false, true if literally anything else.
logManLogFunction(logName, "Info", msg, outputToConsole);
});
if (typeof logManLogFunction !== "function")
throw new TypeError("logManLogFunction expected function, got " + (typeof logManLogFunction).toString() )
if (typeof logName !== "string")
throw new TypeError("logName expected string got " + (typeof logName).toString());
if (options !== undefined && typeof options === "object")
if (options.objectRenderDepth !== undefined)
this.#options.objectRenderDepth = options.objectRenderDepth
this.#logManLogFunction = logManLogFunction;
this.#name = logName;
//return this.bind(this);
}
/**
* What actually does the logging* sorta, we pass it up to logMan
* @param {string} - level The level of this data (info, warn, etc)
* @param {any} msg - Message being logged. Wish it was a string, but we'll try our best.
* @param {boolean} [outputToConsole=true] - Output this particular message to the console. Defaults to true
*/
#log(level, msg, outputToConsole) {
if (typeof msg === "object") {
msg = Util.inspect(msg, {depth: (this.#options.objectRenderDepth!=undefined)? this.#options.objectRenderDepth : 2});
} else if (typeof msg !== "string") {
msg = (new String(msg)).toString();
}
outputToConsole = (outputToConsole===false)? false : true; // Set to false if false, true if literally anything else.
this.#logManLogFunction(this.#name, level, msg, outputToConsole);
}
/**
* Log data at level error
* @param {(string|object|any)} msg - Message / data you wish to log
* @param {boolean} [outputToConsole=true] - Output this particular message to the console. Defaults to true
*/
critical(msg, outputToConsole) {
this.#log("Critical", msg, outputToConsole);
}
/**
* Log data at level error
* @param {(string|object|any)} msg - Message / data you wish to log
* @param {boolean} [outputToConsole=true] - Output this particular message to the console. Defaults to true
*/
error(msg, outputToConsole) {
this.#log("Error", msg, outputToConsole);
}
/**
* Log data at level warn
* @param {(string|object|any)} msg - Message / data you wish to log
* @param {boolean} [outputToConsole=true] - Output this particular message to the console. Defaults to true
*/
warn(msg, outputToConsole) {
this.#log("Warn", msg, outputToConsole);
}
/**
* Log data at level info
* @param {(string|object|any)} msg - Message / data you wish to log
* @param {boolean} [outputToConsole=true] - Output this particular message to the console. Defaults to true
*/
info(msg, outputToConsole) {
this.#log("Info", msg, outputToConsole);
}
/**
* Log data at level http
* @param {(string|object|any)} msg - Message / data you wish to log
* @param {boolean} [outputToConsole=true] - Output this particular message to the console. Defaults to true
*/
http(msg, outputToConsole) {
this.#log("HTTP", msg, outputToConsole);
}
/**
* Log data at level verbose
* @param {(string|object|any)} msg - Message / data you wish to log
* @param {boolean} [outputToConsole=true] - Output this particular message to the console. Defaults to true
*/
verbose(msg, outputToConsole) {
this.#log("Verbose", msg, outputToConsole);
}
/**
* Log data at level debug
* @param {(string|object|any)} msg - Message / data you wish to log
* @param {boolean} [outputToConsole=true] - Output this particular message to the console. Defaults to true
*/
debug(msg, outputToConsole) {
this.#log("Debug", msg, outputToConsole);
}
/**
* Log data at level silly
* @param {(string|object|any)} msg - Message / data you wish to log
* @param {boolean} [outputToConsole=true] - Output this particular message to the console. Defaults to true
*/
silly(msg, outputToConsole) {
this.#log("Silly", msg, outputToConsole);
}
}
module.exports = {
LogMan: LogMan,
Logger: Logger
};