Skip to content

Initial contribution core/log.js #268

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
199 changes: 199 additions & 0 deletions Core/automation/lib/javascript/core/log.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/**
* Logging functions and variables
*
* Copyright (c) 2019 Contributors to the openHAB Scripters project
*
* @author Martin Stangl - initial contribution
*/
'use strict';

(function context (context) {
'use strict';

load(__DIR__+'/actions.js');

context.NOTIFY_OFF = 0;
context.NOTIFY_ERROR = 200;
context.NOTIFY_WARN = 300;
context.NOTIFY_INFO = 400;
context.NOTIFY_DEBUG = 500;

context.Logger = function Logger (name, notificationLevel, config) {
if (name === undefined) {
name = Error().stack.split('\n')[2].split('/').slice(-2).join('.').split(':')[0];
name = name.slice(-3) == ".js" ? name.slice(0,-3) : null;
}
var _logger = Java.type("org.slf4j.LoggerFactory").getLogger(name === null ? "jsr223.javascript" : "jsr223.javascript." + name.toString().toLowerCase());

try {
// Set default config for config params not provided.
if (config === undefined) config = {};
if (config.ERROR === undefined) config.ERROR = {};
if (config.WARN === undefined) config.WARN = {};
if (config.INFO === undefined) config.INFO = {};
if (config.DEBUG === undefined) config.DEBUG = {};
if (config.ERROR.prefix === undefined) config.ERROR.prefix = "short";
if (config.WARN.prefix === undefined) config.WARN.prefix = "none";
if (config.INFO.prefix === undefined) config.INFO.prefix = "none";
if (config.DEBUG.prefix === undefined) config.DEBUG.prefix = "short";

return Object.create(Object.prototype, {
_notificationLevel: { value: (notificationLevel === undefined || notificationLevel === null) ? context.NOTIFY_OFF : notificationLevel },
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are using an underscore prefix for private vars. @openhab-5iver is this a coding style that we should be using in general?

Also note that these vars are still accessible; if you want to actually make them inaccessible, I'd suggest that you move the definition of these vars from the object block into the containing function body.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I am aware of that and even did it this way originally. But then I got worried if this would not cause an issue when I instantiate multiple Loggers as I got confused by all these JS context mechanism. So I just moved it into the returned object, well aware that I loose the privacy.
Maybe I should have just taken the time and test it out. I will do actually do this and move them into the function body, if it works.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@openhab-5iver is this a coding style that we should be using in general?

That would be my preference. That's how we do it in Python, so it fit in perfectly IMO!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as the vars are defined in the function, they should map 1:1 to the object being returned so you should be fine.

@openhab-5iver I can certainly see the bias for Python here! I agree that striving for alignment is good. I would only caution that there are likely to be areas requiring divergence if you want the JS support to be idiomatic to JS, not Python (although I don't think this is one of them!).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Truly, it is not bias. I'm attempting to keep some consistency in place to ease the future migration to a Scripting API, which will absorb pretty much all of the helper library functionality. That will take some time though, and development is continuing. I'm hopeful that, with the new development efforts on the JS libs, there will be some collaboration on how to move forward with bringing their functionality to the level of the Python libs.

I have done plenty of development, but my background is from the management side of things. I'm currently the only active maintainer in this repo, and I am very open to collaboration on its technical direction, especially with the JS libs!

_config: { value: config },
_name: { value: name },

error: { value: function error (msg) {
try {
if (_logger.isErrorEnabled()) {
eval("_logger.error" + this._loggerArgs(this._getLogMessage(msg), arguments.length));
}
if (this._notificationLevel >= context.NOTIFY_ERROR) {
this._sendNotification(this._getLogMessage(msg, this._config.ERROR.prefix, "ERROR"), "fire", "ERROR");
}
} catch (err) {
_logger.error(this._getLogMessage(err));
}
}},

warn: { value: function warn (msg) {
try {
if (_logger.isWarnEnabled()) {
eval("_logger.warn" + this._loggerArgs(this._getLogMessage(msg), arguments.length));
}
if (this._notificationLevel >= context.NOTIFY_WARN) {
this._sendNotification(this._getLogMessage(msg, this._config.WARN.prefix, "WARN"), "error", "WARN");
}
} catch (err) {
_logger.error(this._getLogMessage(err));
}
}},

info: { value: function info (msg) {
try {
if (_logger.isInfoEnabled()) {
eval("_logger.info" + this._loggerArgs(this._getLogMessage(msg), arguments.length));
}
if (this._notificationLevel >= context.NOTIFY_INFO) {
this._sendNotification(this._getLogMessage(msg, this._config.INFO.prefix, "INFO"), "lightbulb", "INFO");
}
} catch (err) {
_logger.error(this._getLogMessage(err));
}
}},

debug: { value: function debug (msg) {
try {
if (_logger.isDebugEnabled()) {
eval("_logger.debug" + this._loggerArgs(this._getLogMessage(msg), arguments.length));
}
if (this._notificationLevel >= context.NOTIFY_DEBUG) {
this._sendNotification(this._getLogMessage(msg, this._config.DEBUG.prefix, "DEBUG"), "text", "DEBUG");
}
} catch (err) {
_logger.error(this._getLogMessage(err));
}
}},

trace: { value: function trace (msg) {
try {
if (_logger.isTraceEnabled()) {
eval("_logger.trace" + this._loggerArgs(this._getLogMessage(msg), arguments.length));
}
} catch (err) {
_logger.error(this._getLogMessage(err));
}
}},

_getCaller: { value: function _getCaller (stack) {
try {
return stack.split('\n\tat ')[1].split(' ')[0];
} catch (err) {
return null;
}
}},

_getLogMessage: { value: function _getLogMessage (msg, prefix, levelString) {
msg = this._legacyLoggerCorrection(msg);

if (prefix === undefined) prefix = "log";

if (prefix == "none") {
return msg.message;
}

var level = "";
var name = "";
if (prefix != "log") {
level = "[" + levelString + "] ";
name = this._name === null ? "" : this._name + ": ";
}

if (prefix == "level") {
return (level + msg.message);
}

var caller = this._getCaller(msg.stack);
var callerText;
if (caller === null) {
callerText = "";
} else if (caller.substr(0,1) == "<") {
callerText = ", in " + caller;
} else {
callerText = ", function " + caller;
}
var message = msg.message == "" ? "" : "] " + msg.message;

if (prefix == "short") {
return (level + "[" + name + msg.fileName.split('/').pop() + ":" + msg.lineNumber + callerText + message);
}

return (level + "[" + name + "source " + msg.fileName + ", line " + msg.lineNumber + callerText + message);
}},

_legacyLoggerCorrection: { value: function _legacyLoggerCorrection (msg) {
if (msg.fileName.search(/automation\/lib\/javascript\/core\/utils\.js$/) !== -1) {
switch (this._getCaller(msg.stack)) {
case "logError":
case "logWarn":
case "logInfo":
case "logDebug":
case "logTrace":
case "log":
msg.stack = msg.stack.split('\n\tat ').slice(1).join('\n\tat ');
msg.fileName = msg.stack.split('\n\tat ')[1].match(/.*? \((.*):/)[1];
msg.lineNumber = msg.stack.split('\n\tat ')[1].match(/.*:(.*?)\)/)[1];
}
}
return msg;
}},

_sendNotification: { value: function _sendNotification (message, icon, levelString) {
if (this._config[levelString].recipients !== undefined) {
this._config[levelString].recipients.forEach(function(mail){
NotificationAction.sendNotification(mail, message, icon, levelString);
})
if (this._config[levelString].recipients.length > 0) {
this.trace(Error("Notification sent to " + this._config[levelString].recipients.join(", ") + ". Message: \"" + message + "\""));
}
} else {
NotificationAction.sendBroadcastNotification(message, icon, levelString);
this.trace(Error("Broadcast notification sent. Message: \"" + message + "\""));
}
}},

_loggerArgs: { value: function _loggerArgs (msg, length) {
var str = "(\"" + msg.replace(/\n/g, '\\n') + "\"";
for (var i = 1; i < length; i++) {
str = str+",arguments["+i+"]";
}
str = str+");"
return str;
}}

})
} catch (err) {
_logger.error(err.fileName + ", line " + err.lineNumber + ": " + err.message);
}
}

})(this);
28 changes: 15 additions & 13 deletions Core/automation/lib/javascript/core/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
var automationPath = OPENHAB_CONF+'/automation/';
var mainPath = automationPath + 'lib/javascript/core/';
//https://wiki.shibboleth.net/confluence/display/IDP30/ScriptedAttributeDefinition
var logger = Java.type("org.slf4j.LoggerFactory").getLogger("jsr223.javascript");

load(__DIR__+'/log.js');
var logger = Logger(null);

try {
var RuleBuilder = Java.type("org.openhab.core.automation.util.RuleBuilder");
Expand Down Expand Up @@ -87,20 +89,20 @@

context.uuid = uuid;

context.logInfo = function(type , value) {
logger.info(args(arguments));
context.logInfo = function (type , value) {
logger.info(Error(args(arguments)));
};
context.logWarn = function(type , value) {
logger.warn(args(arguments));
context.logWarn = function (type , value) {
logger.warn(Error(args(arguments)));
};
context.logDebug = function(type , value) {
logger.debug(args(arguments));
context.logDebug = function (type , value) {
logger.debug(Error(args(arguments)));
};
context.logError = function(type , value) {
logger.error(args(arguments));
context.logError = function (type , value) {
logger.error(Error(args(arguments)));
};
context.logTrace = function(type , value) {
logger.trace(args(arguments));
context.logTrace = function (type , value) {
logger.trace(Error(args(arguments)));
};


Expand All @@ -110,8 +112,8 @@
context.console.debug = context.logDebug;
context.console.error = context.logError;

context.console.log = function(value) {
logger.info("console.log", value);
context.console.log = function (value) {
logger.info(Error("console.log"), value);
};

context.isUndefined = function(item) {
Expand Down