--- /dev/null
+/* \r
+ * JSNLog 2.22.0\r
+ * Open source under the MIT License.\r
+ * Copyright 2016 Mattijs Perdeck All rights reserved.\r
+ */\r
+/// <reference path="Definitions/jsnlog_interfaces.d.ts"/>\r
+var __extends = (this && this.__extends) || function (d, b) {\r
+ for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];\r
+ function __() { this.constructor = d; }\r
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r
+};\r
+function JL(loggerName) {\r
+ // If name is empty, return the root logger\r
+ if (!loggerName) {\r
+ return JL.__;\r
+ }\r
+ // Implements Array.reduce. JSNLog supports IE8+ and reduce is not supported in that browser.\r
+ // Same interface as the standard reduce, except that \r
+ if (!Array.prototype.reduce) {\r
+ Array.prototype.reduce = function (callback, initialValue) {\r
+ var previousValue = initialValue;\r
+ for (var i = 0; i < this.length; i++) {\r
+ previousValue = callback(previousValue, this[i], i, this);\r
+ }\r
+ return previousValue;\r
+ };\r
+ }\r
+ var accumulatedLoggerName = '';\r
+ var logger = ('.' + loggerName).split('.').reduce(function (prev, curr, idx, arr) {\r
+ // if loggername is a.b.c, than currentLogger will be set to the loggers\r
+ // root (prev: JL, curr: '')\r
+ // a (prev: JL.__, curr: 'a')\r
+ // a.b (prev: JL.__.__a, curr: 'b')\r
+ // a.b.c (prev: JL.__.__a.__a.b, curr: 'c')\r
+ // Note that when a new logger name is encountered (such as 'a.b.c'),\r
+ // a new logger object is created and added as a property to the parent ('a.b').\r
+ // The root logger is added as a property of the JL object itself.\r
+ // It is essential that the name of the property containing the child logger\r
+ // contains the full 'path' name of the child logger ('a.b.c') instead of\r
+ // just the bit after the last period ('c').\r
+ // This is because the parent inherits properties from its ancestors.\r
+ // So if the root has a child logger 'c' (stored in a property 'c' of the root logger),\r
+ // then logger 'a.b' has that same property 'c' through inheritance.\r
+ // The names of the logger properties start with __, so the root logger \r
+ // (which has name ''), has a nice property name '__'. \r
+ // accumulatedLoggerName evaluates false ('' is falsy) in first iteration when prev is the root logger.\r
+ // accumulatedLoggerName will be the logger name corresponding with the logger in currentLogger.\r
+ // Keep in mind that the currentLogger may not be defined yet, so can't get the name from\r
+ // the currentLogger object itself. \r
+ if (accumulatedLoggerName) {\r
+ accumulatedLoggerName += '.' + curr;\r
+ }\r
+ else {\r
+ accumulatedLoggerName = curr;\r
+ }\r
+ var currentLogger = prev['__' + accumulatedLoggerName];\r
+ // If the currentLogger (or the actual logger being sought) does not yet exist, \r
+ // create it now.\r
+ if (currentLogger === undefined) {\r
+ // Set the prototype of the Logger constructor function to the parent of the logger\r
+ // to be created. This way, __proto of the new logger object will point at the parent.\r
+ // When logger.level is evaluated and is not present, the JavaScript runtime will \r
+ // walk down the prototype chain to find the first ancestor with a level property.\r
+ //\r
+ // Note that prev at this point refers to the parent logger.\r
+ JL.Logger.prototype = prev;\r
+ currentLogger = new JL.Logger(accumulatedLoggerName);\r
+ prev['__' + accumulatedLoggerName] = currentLogger;\r
+ }\r
+ return currentLogger;\r
+ }, JL.__);\r
+ return logger;\r
+}\r
+var JL;\r
+(function (JL) {\r
+ // Initialise requestId to empty string. If you don't do this and the user\r
+ // does not set it via setOptions, then the JSNLog-RequestId header will\r
+ // have value "undefined", which doesn't look good in a log.\r
+ //\r
+ // Note that you always want to send a requestId as part of log requests,\r
+ // otherwise the server side component doesn't know this is a log request\r
+ // and may create a new request id for the log request, causing confusion\r
+ // in the log.\r
+ JL.requestId = '';\r
+ /**\r
+ Copies the value of a property from one object to the other.\r
+ This is used to copy property values as part of setOption for loggers and appenders.\r
+\r
+ Because loggers inherit property values from their parents, it is important never to\r
+ create a property on a logger if the intent is to inherit from the parent.\r
+\r
+ Copying rules:\r
+ 1) if the from property is undefined (for example, not mentioned in a JSON object), the\r
+ to property is not affected at all.\r
+ 2) if the from property is null, the to property is deleted (so the logger will inherit from\r
+ its parent).\r
+ 3) Otherwise, the from property is copied to the to property.\r
+ */\r
+ function copyProperty(propertyName, from, to) {\r
+ if (from[propertyName] === undefined) {\r
+ return;\r
+ }\r
+ if (from[propertyName] === null) {\r
+ delete to[propertyName];\r
+ return;\r
+ }\r
+ to[propertyName] = from[propertyName];\r
+ }\r
+ /**\r
+ Returns true if a log should go ahead.\r
+ Does not check level.\r
+\r
+ @param filters\r
+ Filters that determine whether a log can go ahead.\r
+ */\r
+ function allow(filters) {\r
+ // If enabled is not null or undefined, then if it is false, then return false\r
+ // Note that undefined==null (!)\r
+ if (!(JL.enabled == null)) {\r
+ if (!JL.enabled) {\r
+ return false;\r
+ }\r
+ }\r
+ // If maxMessages is not null or undefined, then if it is 0, then return false.\r
+ // Note that maxMessages contains number of messages that are still allowed to send.\r
+ // It is decremented each time messages are sent. It can be negative when batch size > 1.\r
+ // Note that undefined==null (!)\r
+ if (!(JL.maxMessages == null)) {\r
+ if (JL.maxMessages < 1) {\r
+ return false;\r
+ }\r
+ }\r
+ // If the regex contains a bug, that will throw an exception.\r
+ // Ignore this, and pass the log item (better too much than too little).\r
+ try {\r
+ if (filters.userAgentRegex) {\r
+ if (!new RegExp(filters.userAgentRegex).test(navigator.userAgent)) {\r
+ return false;\r
+ }\r
+ }\r
+ }\r
+ catch (e) { }\r
+ try {\r
+ if (filters.ipRegex && JL.clientIP) {\r
+ if (!new RegExp(filters.ipRegex).test(JL.clientIP)) {\r
+ return false;\r
+ }\r
+ }\r
+ }\r
+ catch (e) { }\r
+ return true;\r
+ }\r
+ /**\r
+ Returns true if a log should go ahead, based on the message.\r
+\r
+ @param filters\r
+ Filters that determine whether a log can go ahead.\r
+\r
+ @param message\r
+ Message to be logged.\r
+ */\r
+ function allowMessage(filters, message) {\r
+ // If the regex contains a bug, that will throw an exception.\r
+ // Ignore this, and pass the log item (better too much than too little).\r
+ try {\r
+ if (filters.disallow) {\r
+ if (new RegExp(filters.disallow).test(message)) {\r
+ return false;\r
+ }\r
+ }\r
+ }\r
+ catch (e) { }\r
+ return true;\r
+ }\r
+ // If logObject is a function, the function is evaluated (without parameters)\r
+ // and the result returned.\r
+ // Otherwise, logObject itself is returned.\r
+ function stringifyLogObjectFunction(logObject) {\r
+ if (typeof logObject == "function") {\r
+ if (logObject instanceof RegExp) {\r
+ return logObject.toString();\r
+ }\r
+ else {\r
+ return logObject();\r
+ }\r
+ }\r
+ return logObject;\r
+ }\r
+ var StringifiedLogObject = (function () {\r
+ // * msg - \r
+ // if the logObject is a scalar (after possible function evaluation), this is set to\r
+ // string representing the scalar. Otherwise it is left undefined.\r
+ // * meta -\r
+ // if the logObject is an object (after possible function evaluation), this is set to\r
+ // that object. Otherwise it is left undefined.\r
+ // * finalString -\r
+ // This is set to the string representation of logObject (after possible function evaluation),\r
+ // regardless of whether it is an scalar or an object. An object is stringified to a JSON string.\r
+ // Note that you can't call this field "final", because as some point this was a reserved\r
+ // JavaScript keyword and using final trips up some minifiers.\r
+ function StringifiedLogObject(msg, meta, finalString) {\r
+ this.msg = msg;\r
+ this.meta = meta;\r
+ this.finalString = finalString;\r
+ }\r
+ return StringifiedLogObject;\r
+ }());\r
+ // Takes a logObject, which can be \r
+ // * a scalar\r
+ // * an object\r
+ // * a parameterless function, which returns the scalar or object to log.\r
+ // Returns a stringifiedLogObject\r
+ function stringifyLogObject(logObject) {\r
+ // Note that this works if logObject is null.\r
+ // typeof null is object.\r
+ // JSON.stringify(null) returns "null".\r
+ var actualLogObject = stringifyLogObjectFunction(logObject);\r
+ var finalString;\r
+ // Note that typeof actualLogObject should not be "function", because that has \r
+ // been resolved with stringifyLogObjectFunction.\r
+ switch (typeof actualLogObject) {\r
+ case "string":\r
+ return new StringifiedLogObject(actualLogObject, null, actualLogObject);\r
+ case "number":\r
+ finalString = actualLogObject.toString();\r
+ return new StringifiedLogObject(finalString, null, finalString);\r
+ case "boolean":\r
+ finalString = actualLogObject.toString();\r
+ return new StringifiedLogObject(finalString, null, finalString);\r
+ case "undefined":\r
+ return new StringifiedLogObject("undefined", null, "undefined");\r
+ case "object":\r
+ if ((actualLogObject instanceof RegExp) ||\r
+ (actualLogObject instanceof String) ||\r
+ (actualLogObject instanceof Number) ||\r
+ (actualLogObject instanceof Boolean)) {\r
+ finalString = actualLogObject.toString();\r
+ return new StringifiedLogObject(finalString, null, finalString);\r
+ }\r
+ else {\r
+ if (typeof JL.serialize === 'function') {\r
+ finalString = JL.serialize.call(this, actualLogObject);\r
+ }\r
+ else {\r
+ finalString = JSON.stringify(actualLogObject);\r
+ }\r
+ return new StringifiedLogObject(null, actualLogObject, finalString);\r
+ }\r
+ default:\r
+ return new StringifiedLogObject("unknown", null, "unknown");\r
+ }\r
+ }\r
+ function setOptions(options) {\r
+ copyProperty("enabled", options, this);\r
+ copyProperty("maxMessages", options, this);\r
+ copyProperty("defaultAjaxUrl", options, this);\r
+ copyProperty("clientIP", options, this);\r
+ copyProperty("requestId", options, this);\r
+ copyProperty("defaultBeforeSend", options, this);\r
+ copyProperty("serialize", options, this);\r
+ return this;\r
+ }\r
+ JL.setOptions = setOptions;\r
+ function getAllLevel() { return -2147483648; }\r
+ JL.getAllLevel = getAllLevel;\r
+ function getTraceLevel() { return 1000; }\r
+ JL.getTraceLevel = getTraceLevel;\r
+ function getDebugLevel() { return 2000; }\r
+ JL.getDebugLevel = getDebugLevel;\r
+ function getInfoLevel() { return 3000; }\r
+ JL.getInfoLevel = getInfoLevel;\r
+ function getWarnLevel() { return 4000; }\r
+ JL.getWarnLevel = getWarnLevel;\r
+ function getErrorLevel() { return 5000; }\r
+ JL.getErrorLevel = getErrorLevel;\r
+ function getFatalLevel() { return 6000; }\r
+ JL.getFatalLevel = getFatalLevel;\r
+ function getOffLevel() { return 2147483647; }\r
+ JL.getOffLevel = getOffLevel;\r
+ function levelToString(level) {\r
+ if (level <= 1000) {\r
+ return "trace";\r
+ }\r
+ if (level <= 2000) {\r
+ return "debug";\r
+ }\r
+ if (level <= 3000) {\r
+ return "info";\r
+ }\r
+ if (level <= 4000) {\r
+ return "warn";\r
+ }\r
+ if (level <= 5000) {\r
+ return "error";\r
+ }\r
+ return "fatal";\r
+ }\r
+ // ---------------------\r
+ var Exception = (function () {\r
+ // data replaces message. It takes not just strings, but also objects and functions, just like the log function.\r
+ // internally, the string representation is stored in the message property (inherited from Error)\r
+ //\r
+ // inner: inner exception. Can be null or undefined. \r
+ function Exception(data, inner) {\r
+ this.inner = inner;\r
+ this.name = "JL.Exception";\r
+ this.message = stringifyLogObject(data).finalString;\r
+ }\r
+ return Exception;\r
+ }());\r
+ JL.Exception = Exception;\r
+ // Derive Exception from Error (a Host object), so browsers\r
+ // are more likely to produce a stack trace for it in their console.\r
+ //\r
+ // Note that instanceof against an object created with this constructor\r
+ // will return true in these cases:\r
+ // <object> instanceof JL.Exception);\r
+ // <object> instanceof Error);\r
+ Exception.prototype = new Error();\r
+ // ---------------------\r
+ var LogItem = (function () {\r
+ // l: level\r
+ // m: message\r
+ // n: logger name\r
+ // t (timeStamp) is number of milliseconds since 1 January 1970 00:00:00 UTC\r
+ //\r
+ // Keeping the property names really short, because they will be sent in the\r
+ // JSON payload to the server.\r
+ function LogItem(l, m, n, t) {\r
+ this.l = l;\r
+ this.m = m;\r
+ this.n = n;\r
+ this.t = t;\r
+ }\r
+ return LogItem;\r
+ }());\r
+ JL.LogItem = LogItem;\r
+ // ---------------------\r
+ var Appender = (function () {\r
+ // sendLogItems takes an array of log items. It will be called when\r
+ // the appender has items to process (such as, send to the server).\r
+ // Note that after sendLogItems returns, the appender may truncate\r
+ // the LogItem array, so the function has to copy the content of the array\r
+ // in some fashion (eg. serialize) before returning.\r
+ function Appender(appenderName, sendLogItems) {\r
+ this.appenderName = appenderName;\r
+ this.sendLogItems = sendLogItems;\r
+ this.level = JL.getTraceLevel();\r
+ // set to super high level, so if user increases level, level is unlikely to get \r
+ // above sendWithBufferLevel\r
+ this.sendWithBufferLevel = 2147483647;\r
+ this.storeInBufferLevel = -2147483648;\r
+ this.bufferSize = 0; // buffering switch off by default\r
+ this.batchSize = 1;\r
+ // Holds all log items with levels higher than storeInBufferLevel \r
+ // but lower than level. These items may never be sent.\r
+ this.buffer = [];\r
+ // Holds all items that we do want to send, until we have a full\r
+ // batch (as determined by batchSize).\r
+ this.batchBuffer = [];\r
+ }\r
+ Appender.prototype.setOptions = function (options) {\r
+ copyProperty("level", options, this);\r
+ copyProperty("ipRegex", options, this);\r
+ copyProperty("userAgentRegex", options, this);\r
+ copyProperty("disallow", options, this);\r
+ copyProperty("sendWithBufferLevel", options, this);\r
+ copyProperty("storeInBufferLevel", options, this);\r
+ copyProperty("bufferSize", options, this);\r
+ copyProperty("batchSize", options, this);\r
+ if (this.bufferSize < this.buffer.length) {\r
+ this.buffer.length = this.bufferSize;\r
+ }\r
+ return this;\r
+ };\r
+ /**\r
+ Called by a logger to log a log item.\r
+ If in response to this call one or more log items need to be processed\r
+ (eg., sent to the server), this method calls this.sendLogItems\r
+ with an array with all items to be processed.\r
+\r
+ Note that the name and parameters of this function must match those of the log function of\r
+ a Winston transport object, so that users can use these transports as appenders.\r
+ That is why there are many parameters that are not actually used by this function.\r
+\r
+ level - string with the level ("trace", "debug", etc.) Only used by Winston transports.\r
+ msg - human readable message. Undefined if the log item is an object. Only used by Winston transports.\r
+ meta - log object. Always defined, because at least it contains the logger name. Only used by Winston transports.\r
+ callback - function that is called when the log item has been logged. Only used by Winston transports.\r
+ levelNbr - level as a number. Not used by Winston transports.\r
+ message - log item. If the user logged an object, this is the JSON string. Not used by Winston transports.\r
+ loggerName: name of the logger. Not used by Winston transports.\r
+ */\r
+ Appender.prototype.log = function (level, msg, meta, callback, levelNbr, message, loggerName) {\r
+ var logItem;\r
+ if (!allow(this)) {\r
+ return;\r
+ }\r
+ if (!allowMessage(this, message)) {\r
+ return;\r
+ }\r
+ if (levelNbr < this.storeInBufferLevel) {\r
+ // Ignore the log item completely\r
+ return;\r
+ }\r
+ logItem = new LogItem(levelNbr, message, loggerName, (new Date).getTime());\r
+ if (levelNbr < this.level) {\r
+ // Store in the hold buffer. Do not send.\r
+ if (this.bufferSize > 0) {\r
+ this.buffer.push(logItem);\r
+ // If we exceeded max buffer size, remove oldest item\r
+ if (this.buffer.length > this.bufferSize) {\r
+ this.buffer.shift();\r
+ }\r
+ }\r
+ return;\r
+ }\r
+ if (levelNbr < this.sendWithBufferLevel) {\r
+ // Want to send the item, but not the contents of the buffer\r
+ this.batchBuffer.push(logItem);\r
+ }\r
+ else {\r
+ // Want to send both the item and the contents of the buffer.\r
+ // Send contents of buffer first, because logically they happened first.\r
+ if (this.buffer.length) {\r
+ this.batchBuffer = this.batchBuffer.concat(this.buffer);\r
+ this.buffer.length = 0;\r
+ }\r
+ this.batchBuffer.push(logItem);\r
+ }\r
+ if (this.batchBuffer.length >= this.batchSize) {\r
+ this.sendBatch();\r
+ return;\r
+ }\r
+ };\r
+ // Processes the batch buffer\r
+ Appender.prototype.sendBatch = function () {\r
+ if (this.batchBuffer.length == 0) {\r
+ return;\r
+ }\r
+ if (!(JL.maxMessages == null)) {\r
+ if (JL.maxMessages < 1) {\r
+ return;\r
+ }\r
+ }\r
+ // If maxMessages is not null or undefined, then decrease it by the batch size.\r
+ // This can result in a negative maxMessages.\r
+ // Note that undefined==null (!)\r
+ if (!(JL.maxMessages == null)) {\r
+ JL.maxMessages -= this.batchBuffer.length;\r
+ }\r
+ this.sendLogItems(this.batchBuffer);\r
+ this.batchBuffer.length = 0;\r
+ };\r
+ return Appender;\r
+ }());\r
+ JL.Appender = Appender;\r
+ // ---------------------\r
+ var AjaxAppender = (function (_super) {\r
+ __extends(AjaxAppender, _super);\r
+ function AjaxAppender(appenderName) {\r
+ _super.call(this, appenderName, AjaxAppender.prototype.sendLogItemsAjax);\r
+ }\r
+ AjaxAppender.prototype.setOptions = function (options) {\r
+ copyProperty("url", options, this);\r
+ copyProperty("beforeSend", options, this);\r
+ _super.prototype.setOptions.call(this, options);\r
+ return this;\r
+ };\r
+ AjaxAppender.prototype.sendLogItemsAjax = function (logItems) {\r
+ // JSON.stringify is only supported on IE8+\r
+ // Use try-catch in case we get an exception here.\r
+ //\r
+ // The "r" field is now obsolete. When writing a server side component, \r
+ // read the HTTP header "JSNLog-RequestId"\r
+ // to get the request id.\r
+ //\r
+ // The .Net server side component\r
+ // now uses the JSNLog-RequestId HTTP Header, because this allows it to\r
+ // detect whether the incoming request has a request id.\r
+ // If the request id were in the json payload, it would have to read the json\r
+ // from the stream, interfering with normal non-logging requests.\r
+ //\r
+ // To see what characters you can use in the HTTP header, visit:\r
+ // http://stackoverflow.com/questions/3561381/custom-http-headers-naming-conventions/3561399#3561399\r
+ //\r
+ // It needs this ability, so users of NLog can set a requestId variable in NLog\r
+ // before the server side component tries to log the client side log message\r
+ // through an NLog logger.\r
+ // Unlike Log4Net, NLog doesn't allow you to register an object whose ToString()\r
+ // is only called when it tries to log something, so the requestId has to be \r
+ // determined right at the start of request processing.\r
+ try {\r
+ // Only determine the url right before you send a log request.\r
+ // Do not set the url when constructing the appender.\r
+ //\r
+ // This is because the server side component sets defaultAjaxUrl\r
+ // in a call to setOptions, AFTER the JL object and the default appender\r
+ // have been created. \r
+ var ajaxUrl = "/jsnlog.logger";\r
+ // This evaluates to true if defaultAjaxUrl is null or undefined\r
+ if (!(JL.defaultAjaxUrl == null)) {\r
+ ajaxUrl = JL.defaultAjaxUrl;\r
+ }\r
+ if (this.url) {\r
+ ajaxUrl = this.url;\r
+ }\r
+ // Send the json to the server. \r
+ // Note that there is no event handling here. If the send is not\r
+ // successful, nothing can be done about it.\r
+ var xhr = this.getXhr(ajaxUrl);\r
+ var json = {\r
+ r: JL.requestId,\r
+ lg: logItems\r
+ };\r
+ // call beforeSend callback\r
+ // first try the callback on the appender\r
+ // then the global defaultBeforeSend callback\r
+ if (typeof this.beforeSend === 'function') {\r
+ this.beforeSend.call(this, xhr, json);\r
+ }\r
+ else if (typeof JL.defaultBeforeSend === 'function') {\r
+ JL.defaultBeforeSend.call(this, xhr, json);\r
+ }\r
+ var finalmsg = JSON.stringify(json);\r
+ xhr.send(finalmsg);\r
+ }\r
+ catch (e) { }\r
+ };\r
+ // Creates the Xhr object to use to send the log request.\r
+ // Sets out to create an Xhr object that can be used for CORS.\r
+ // However, if there seems to be no CORS support on the browser,\r
+ // returns a non-CORS capable Xhr.\r
+ AjaxAppender.prototype.getXhr = function (ajaxUrl) {\r
+ var xhr = new XMLHttpRequest();\r
+ // Check whether this xhr is CORS capable by checking whether it has\r
+ // withCredentials. \r
+ // "withCredentials" only exists on XMLHTTPRequest2 objects.\r
+ if (!("withCredentials" in xhr)) {\r
+ // Just found that no XMLHttpRequest2 available.\r
+ // Check if XDomainRequest is available.\r
+ // This only exists in IE, and is IE's way of making CORS requests.\r
+ if (typeof XDomainRequest != "undefined") {\r
+ // Note that here we're not setting request headers on the XDomainRequest\r
+ // object. This is because this object doesn't let you do that:\r
+ // http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx\r
+ // This means that for IE8 and IE9, CORS logging requests do not carry request ids.\r
+ var xdr = new XDomainRequest();\r
+ xdr.open('POST', ajaxUrl);\r
+ return xdr;\r
+ }\r
+ }\r
+ // At this point, we're going with XMLHttpRequest, whether it is CORS capable or not.\r
+ // If it is not CORS capable, at least will handle the non-CORS requests.\r
+ xhr.open('POST', ajaxUrl);\r
+ xhr.setRequestHeader('Content-Type', 'application/json');\r
+ xhr.setRequestHeader('JSNLog-RequestId', JL.requestId);\r
+ return xhr;\r
+ };\r
+ return AjaxAppender;\r
+ }(Appender));\r
+ JL.AjaxAppender = AjaxAppender;\r
+ // ---------------------\r
+ var ConsoleAppender = (function (_super) {\r
+ __extends(ConsoleAppender, _super);\r
+ function ConsoleAppender(appenderName) {\r
+ _super.call(this, appenderName, ConsoleAppender.prototype.sendLogItemsConsole);\r
+ }\r
+ ConsoleAppender.prototype.clog = function (logEntry) {\r
+ console.log(logEntry);\r
+ };\r
+ ConsoleAppender.prototype.cerror = function (logEntry) {\r
+ if (console.error) {\r
+ console.error(logEntry);\r
+ }\r
+ else {\r
+ this.clog(logEntry);\r
+ }\r
+ };\r
+ ConsoleAppender.prototype.cwarn = function (logEntry) {\r
+ if (console.warn) {\r
+ console.warn(logEntry);\r
+ }\r
+ else {\r
+ this.clog(logEntry);\r
+ }\r
+ };\r
+ ConsoleAppender.prototype.cinfo = function (logEntry) {\r
+ if (console.info) {\r
+ console.info(logEntry);\r
+ }\r
+ else {\r
+ this.clog(logEntry);\r
+ }\r
+ };\r
+ // IE11 has a console.debug function. But its console doesn't have \r
+ // the option to show/hide debug messages (the same way Chrome and FF do),\r
+ // even though it does have such buttons for Error, Warn, Info.\r
+ //\r
+ // For now, this means that debug messages can not be hidden on IE.\r
+ // Live with this, seeing that it works fine on FF and Chrome, which\r
+ // will be much more popular with developers.\r
+ ConsoleAppender.prototype.cdebug = function (logEntry) {\r
+ if (console.debug) {\r
+ console.debug(logEntry);\r
+ }\r
+ else {\r
+ this.cinfo(logEntry);\r
+ }\r
+ };\r
+ ConsoleAppender.prototype.sendLogItemsConsole = function (logItems) {\r
+ try {\r
+ if (!console) {\r
+ return;\r
+ }\r
+ var i;\r
+ for (i = 0; i < logItems.length; ++i) {\r
+ var li = logItems[i];\r
+ var msg = li.n + ": " + li.m;\r
+ // Only log the timestamp if we're on the server\r
+ // (window is undefined). On the browser, the user\r
+ // sees the log entry probably immediately, so in that case\r
+ // the timestamp is clutter.\r
+ if (typeof window === 'undefined') {\r
+ msg = new Date(li.t) + " | " + msg;\r
+ }\r
+ if (li.l <= JL.getDebugLevel()) {\r
+ this.cdebug(msg);\r
+ }\r
+ else if (li.l <= JL.getInfoLevel()) {\r
+ this.cinfo(msg);\r
+ }\r
+ else if (li.l <= JL.getWarnLevel()) {\r
+ this.cwarn(msg);\r
+ }\r
+ else {\r
+ this.cerror(msg);\r
+ }\r
+ }\r
+ }\r
+ catch (e) {\r
+ }\r
+ };\r
+ return ConsoleAppender;\r
+ }(Appender));\r
+ JL.ConsoleAppender = ConsoleAppender;\r
+ // --------------------\r
+ var Logger = (function () {\r
+ function Logger(loggerName) {\r
+ this.loggerName = loggerName;\r
+ // Create seenRexes, otherwise this logger will use the seenRexes\r
+ // of its parent via the prototype chain.\r
+ this.seenRegexes = [];\r
+ }\r
+ Logger.prototype.setOptions = function (options) {\r
+ copyProperty("level", options, this);\r
+ copyProperty("userAgentRegex", options, this);\r
+ copyProperty("disallow", options, this);\r
+ copyProperty("ipRegex", options, this);\r
+ copyProperty("appenders", options, this);\r
+ copyProperty("onceOnly", options, this);\r
+ // Reset seenRegexes, in case onceOnly has been changed.\r
+ this.seenRegexes = [];\r
+ return this;\r
+ };\r
+ // Turns an exception into an object that can be sent to the server.\r
+ Logger.prototype.buildExceptionObject = function (e) {\r
+ var excObject = {};\r
+ if (e.stack) {\r
+ excObject.stack = e.stack;\r
+ }\r
+ else {\r
+ excObject.e = e;\r
+ }\r
+ if (e.message) {\r
+ excObject.message = e.message;\r
+ }\r
+ if (e.name) {\r
+ excObject.name = e.name;\r
+ }\r
+ if (e.data) {\r
+ excObject.data = e.data;\r
+ }\r
+ if (e.inner) {\r
+ excObject.inner = this.buildExceptionObject(e.inner);\r
+ }\r
+ return excObject;\r
+ };\r
+ // Logs a log item.\r
+ // Parameter e contains an exception (or null or undefined).\r
+ //\r
+ // Reason that processing exceptions is done at this low level is that\r
+ // 1) no need to spend the cpu cycles if the logger is switched off\r
+ // 2) fatalException takes both a logObject and an exception, and the logObject\r
+ // may be a function that should only be executed if the logger is switched on.\r
+ //\r
+ // If an exception is passed in, the contents of logObject is attached to the exception\r
+ // object in a new property logData.\r
+ // The resulting exception object is than worked into a message to the server.\r
+ //\r
+ // If there is no exception, logObject itself is worked into the message to the server.\r
+ Logger.prototype.log = function (level, logObject, e) {\r
+ var i = 0;\r
+ var compositeMessage;\r
+ var excObject;\r
+ // If we can't find any appenders, do nothing\r
+ if (!this.appenders) {\r
+ return this;\r
+ }\r
+ if (((level >= this.level)) && allow(this)) {\r
+ if (e) {\r
+ excObject = this.buildExceptionObject(e);\r
+ excObject.logData = stringifyLogObjectFunction(logObject);\r
+ }\r
+ else {\r
+ excObject = logObject;\r
+ }\r
+ compositeMessage = stringifyLogObject(excObject);\r
+ if (allowMessage(this, compositeMessage.finalString)) {\r
+ // See whether message is a duplicate\r
+ if (this.onceOnly) {\r
+ i = this.onceOnly.length - 1;\r
+ while (i >= 0) {\r
+ if (new RegExp(this.onceOnly[i]).test(compositeMessage.finalString)) {\r
+ if (this.seenRegexes[i]) {\r
+ return this;\r
+ }\r
+ this.seenRegexes[i] = true;\r
+ }\r
+ i--;\r
+ }\r
+ }\r
+ // Pass message to all appenders\r
+ // Note that these appenders could be Winston transports\r
+ // https://github.com/flatiron/winston\r
+ //\r
+ // These transports do not take the logger name as a parameter.\r
+ // So add it to the meta information, so even Winston transports will\r
+ // store this info.\r
+ compositeMessage.meta = compositeMessage.meta || {};\r
+ compositeMessage.meta.loggerName = this.loggerName;\r
+ i = this.appenders.length - 1;\r
+ while (i >= 0) {\r
+ this.appenders[i].log(levelToString(level), compositeMessage.msg, compositeMessage.meta, function () { }, level, compositeMessage.finalString, this.loggerName);\r
+ i--;\r
+ }\r
+ }\r
+ }\r
+ return this;\r
+ };\r
+ Logger.prototype.trace = function (logObject) { return this.log(getTraceLevel(), logObject); };\r
+ Logger.prototype.debug = function (logObject) { return this.log(getDebugLevel(), logObject); };\r
+ Logger.prototype.info = function (logObject) { return this.log(getInfoLevel(), logObject); };\r
+ Logger.prototype.warn = function (logObject) { return this.log(getWarnLevel(), logObject); };\r
+ Logger.prototype.error = function (logObject) { return this.log(getErrorLevel(), logObject); };\r
+ Logger.prototype.fatal = function (logObject) { return this.log(getFatalLevel(), logObject); };\r
+ Logger.prototype.fatalException = function (logObject, e) { return this.log(getFatalLevel(), logObject, e); };\r
+ return Logger;\r
+ }());\r
+ JL.Logger = Logger;\r
+ function createAjaxAppender(appenderName) {\r
+ return new AjaxAppender(appenderName);\r
+ }\r
+ JL.createAjaxAppender = createAjaxAppender;\r
+ function createConsoleAppender(appenderName) {\r
+ return new ConsoleAppender(appenderName);\r
+ }\r
+ JL.createConsoleAppender = createConsoleAppender;\r
+ // -----------------------\r
+ // In the browser, the default appender is the AjaxAppender.\r
+ // Under nodejs (where there is no "window"), use the ConsoleAppender instead.\r
+ var defaultAppender = new AjaxAppender("");\r
+ if (typeof window === 'undefined') {\r
+ defaultAppender = new ConsoleAppender("");\r
+ }\r
+ // Create root logger\r
+ //\r
+ // Note that this is the parent of all other loggers.\r
+ // Logger "x" will be stored at\r
+ // JL.__.x\r
+ // Logger "x.y" at\r
+ // JL.__.x.y\r
+ JL.__ = new JL.Logger("");\r
+ JL.__.setOptions({\r
+ level: JL.getDebugLevel(),\r
+ appenders: [defaultAppender]\r
+ });\r
+})(JL || (JL = {}));\r
+if (typeof exports !== 'undefined') {\r
+ exports.JL = JL;\r
+}\r
+// Support AMD module format\r
+var define;\r
+if (typeof define == 'function' && define.amd) {\r
+ define('jsnlog', [], function () {\r
+ return JL;\r
+ });\r
+}\r
+// If the __jsnlog_configure global function has been\r
+// created, call it now. This allows you to create a global function\r
+// setting logger options etc. inline in the page before jsnlog.js\r
+// has been loaded.\r
+if (typeof __jsnlog_configure == 'function') {\r
+ __jsnlog_configure(JL);\r
+}\r
+// Create onerror handler to log uncaught exceptions to the server side log, but only if there \r
+// is no such handler already.\r
+// Must use "typeof window" here, because in NodeJs, window is not defined at all, so cannot refer to window in any way.\r
+if (typeof window !== 'undefined' && !window.onerror) {\r
+ window.onerror = function (errorMsg, url, lineNumber, column, errorObj) {\r
+ // Send object with all data to server side log, using severity fatal, \r
+ // from logger "onerrorLogger"\r
+ JL("onerrorLogger").fatalException({\r
+ "msg": "Uncaught Exception",\r
+ "errorMsg": errorMsg, "url": url,\r
+ "line number": lineNumber, "column": column\r
+ }, errorObj);\r
+ // Tell browser to run its own error handler as well \r
+ return false;\r
+ };\r
+}\r
+// Deal with unhandled exceptions thrown in promises\r
+if (typeof window !== 'undefined' && !window.onunhandledrejection) {\r
+ window.onunhandledrejection = function (event) {\r
+ // Send object with all data to server side log, using severity fatal, \r
+ // from logger "onerrorLogger"\r
+ JL("onerrorLogger").fatalException({\r
+ "msg": "unhandledrejection",\r
+ "errorMsg": event.reason.message\r
+ }, event.reason);\r
+ // Tell browser to run its own error handler as well \r
+ return false;\r
+ };\r
+}\r