-
-
Notifications
You must be signed in to change notification settings - Fork 69
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
Initial contribution core/log.js #268
base: master
Are you sure you want to change the base?
Initial contribution core/log.js #268
Conversation
After getting a little bit of sleep, I think I want to make the compatibility functionality, which was the last thing I added in, the default behavior. This means getting rid of the need to wrap the message in an Error object. Also I noticed that the console.log function in utils.js just creates an INFO log entry with the text „console.log“. I initially kept the behavior in the rewrite, but it would make more sense to fix it.So I will do that. |
…nsole log functions improved/fixed
I completed the changes:
This should be it (except for the missing documentation). PS: Next step would then be probably to replace all core logging calls to use log.js. It is not mandatory, as the utils.js log functions work as before (output format is slightly different), but I saw that a lot of error logging uses LINE to specify the error position instead of err.lineNumber. This makes the error messages point to the line where the error is logged instead of the actual line, where the error occurs, which is not ideal. |
All of the logging in the JS libraries should go through this new lib. The other logging functions should be removed. I will review as soon as I can find some time. Hopefully @lewie will go through this too! @jpg0, you had a library for logging in your test commit. Does the functionality in this PR align with yours? Do you have any suggestions or improvements? The JS libraries are still under construction, so do what you can, but don't get blocked if you can't keep backwards compatibility. |
Another remark: One thing utils.js does right now is to override console.log and the other console.* logging functions. I did not move this to log.js, as I am not sure it is needed, but kept in in utils.js instead. My thought also is that at some point the backwards compatibility support could be dropped by just cleaning up utils.js. Or we could meanwhile collect these things in a separate compatibility library, so we can also clean up utils.js earlier. |
Just got this from your library: I expect that I fed it a bad (error) object to log or something, but that error message is pretty cryptic and looks like it hit an edge-case. (The error I fed it was: |
Yes, this is caused by providing an object, which is not an Error object. I wanted to check, if I got an Error object or a child object of Error object, but knew this will be tricky. So I just made a "cheaper" implementation first but then forgot to revisit it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's great to see some other people working in the repo! Your technical JS support will best come from @lewie, but you'll find his time is even more limited than mine!
As a general statement, the preference is to use snake_case in the helper libraries for all languages. You'll see this in the newer JS libs. This helps to keep documentation consistent with Python and makes it easier to recognize when you are using Java. WDYT?
case "logError": | ||
case "logWarn": | ||
case "logInfo": | ||
case "logDebug": | ||
case "logTrace": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm concerned about the use of the LogAction method names and potential conflicts. The utils.js lib did the same. My guess is that this was to provide a familiar name for people who had used the rules DSL. Now that actions.js is available and LogAction is easily accessible, this should be removed. This library should provide similar functionality to the Python core.log and provide a bridge between the native JS logging (console.log) and slf4j. Although, I have questioned the bridging to slf4j and have considered swapping it with LogAction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part was done specifically for support backwards compability support. If we get rid of logError, logWarn, ... in utils.log, this part goes away as well.
The new log functions are used like this:
load(__DIR__+'/log.js');
var log = Logger();
log.error("Message");
log.warn("Message");
log.info("Message");
log.debug("Message");
log.trace("Message");
utils.js does actually already provide this bridge between console.log and slf4j. I can move this part to log.js by simply instantiating a logger instance called console. var console = Logger();
Just need to add a few methods like log()
and methods to change the logger name, log level and notification feature configuration, which currently can only be set at initialization.
But I also want to check, if we can so easily pretend to be console.log. Console.log also has a few tricks up it´s sleeve and I am not sure, if we can emulate all of that. But most I can remember is about color and font styles, which we just have to ignore / do not need to support.
I could convert log.js to use LogAction pretty easily instead of using slf4j directly, as LogAction does nothing else than wrap slf4j.
This means loosing the level trace, which is no big issue. But it would make log.js slower. All the compilation of data about the log call position is very slow (a few milliseconds - forever for a logger). Therefore I check using slf4j's isErrorEnabled() and the likes, if it even makes sense to do all of this. Unfortunately LogAction does not expose these methods. (Actually, I am guilty of the same and should expose these in log.js.)
If we just want to look and behave like we are using LogAction under the hood, we could simply call our logger "org.eclipse.smarthome.model.script.jsr223.javascript" and get rid of the level trace.
Not sure, if LogAction adds some fancy formatting, which we would need to emulate.
I keep you posted on the console.log front.
Give me your thoughts on LogAction. Maybe we should try a pull request there to get the isEnabled() methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But I also want to check, if we can so easily pretend to be console.log. Console.log also has a few tricks up it´s sleeve and I am not sure, if we can emulate all of that.
The big question is if there is any benefit to using this over LogAction 😄. If concole.log has some cool stuff in it, then that may be what tips the scale. I think it is great to equalize the functionality between the JS and Jython libraries, but I really question if we need this (or the Jython) library at all.
'use strict';
var OPENHAB_CONF = Java.type("java.lang.System").getenv("OPENHAB_CONF");
load(OPENHAB_CONF + '/automation/lib/javascript/core/actions.js');
LogAction.logWarn("Rules", "Test")
The Jython core.log provides a helpful decorator for getting a traceback, and it sends a notification too, but LogAction can do everything else.
A big benefit to slf4j is the parameterized logging and only calculating the value to log if the log entry fits the logger. It's nice to have some performance heavy logging in place that doesn't slow things down when not in use. I haven't found a way to make use of this in Jython. Is this the isErrorEnabled() (I'll need to look that up)? A PR for LogAction to enable this would be really nice!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I mentioned elsewhere, I believe that there is benefit to having a console.log defined (so that existing JS code works), but what that forwards the actual log messages to I have no opinion on, and it could be either slf4j or LogAction with no impact to consumers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a look at console. Browser and node.js implementation.
The node.js is probably, the one we would like to emulate, as we are also a backend implementation.
But looking at the node.js documetation at https://nodejs.org/api/console.html, I would say console is a different, much bigger beast. Already the message formatting syntax is completely different and then there are quite some methods to be implemented.
Therefore I do not think that what I built should pretend to be console. At least the formatting syntax would need to be supported, then it would be just about features not available, but not differences in functionality.
We could make quick progress when we canibalize the node.js implementation, but I am not sure, if I am up for that right now. Maybe.
|
||
load(__DIR__+'/log.js'); | ||
var logger = Logger(null); | ||
var loggerConsole = Logger("console"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of the logging functionality should be stripped out of utils.js. The utils.js library will end up being used to hold helper functions for use in scripts and libs. This is a breaking change, but these should be expected... https://github.com/openhab-scripters/openhab-helper-libraries/blob/master/README.md#-under-construction-.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. I will the go ahead and remove this from utils.js.
But this means I have to rework all log calls all exiting core libraries.
So I first want to resolve, if we will emulate console.log (most likely yes).
Also it might be better that @jpg0 gets his CommonJS stuff done and merged into the master, before I start also changing all libraries.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But this means I have to rework all log calls all exiting core libraries.
I'm missing something... I did not think there was much effort to pull logging out of utils.js. But yes... if the core libs need reworked for logging, then it should wait for CommonJS, if that is the direction we're going... haven't dug into that one yet!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I could just take the log methods as they are in utils.js and move them to log.js. But if we completely forego backwards compatibility and get rid of logError, logWarn, ..., I do not see much value in that. Better to leave them in utils.js, until we actually get rid of them and not pollute the new library with it.
And logError, logWarn, ...are used by the core libraries, So this needs to be changed to get rid of these methods.
Fixed this. |
@openhab-5iver sorry I never responded to your original questions:
The logging library that I had was actually just some handy utilities that I was using, but wasn't really a logging library. The only function that it had over this was the ability to pass in the level in case it needed to be dynamic: Looking at this contribution, I think most of the things that I thought about are already being covered. The only other one is the whole notification functionality. I'm undecided about that. I do think that it's useful to be able to notify of errors, however this seems like a weird place to put it. Typically you wouldn't do it like this as it's pretty high coupling and at a high level in the stack, but would instead expect something like:
Also, as for the compatibility with a move to CommonJS, this works fine and I've already switched over to using it. I agree that we should remove all the |
if (config.DEBUG.prefix === undefined) config.DEBUG.prefix = "short"; | ||
|
||
return Object.create(Object.prototype, { | ||
_notificationLevel: { value: (notificationLevel === undefined || notificationLevel === null) ? context.NOTIFY_OFF : notificationLevel }, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
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!).
There was a problem hiding this comment.
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!
I agree. I put it into Jython core.log to test and almost forgot it was there, but was reminded by a random error that showed up on my phone. It's caught several that I probably would have missed. I'm sure there is a better way, but I've been glad that I put it in. |
After using this library for quite a bit, I wanted to raise a UX issue: the prepending of location can be a positive, but can also be a big drawback when log statements start in differing columns.
|
I agree to the UX issue. I myself noticed that it sometime is difficult to quickly see the actual message as there so much stuff is going on before it. |
Log message format changed for better readability. Private functions actually made private. Blank line added after end of code. Logger name prefix aligned with LogAction. Signed-off-by: Martin Stangl <[email protected]> Signed-off-by: Martin Stangl <[email protected]>
I agree, this should be decoupled. Right now I am thinking in the following direction:
|
@jpg0, could you please include an image of what you mean? I have commented out the "Common pattern layout for appenders" section at the top of the org.ops4j.pax.logging.cfg file, so my logging looks different than stock... |
@jpg0 please let me know how you like the new format, once you had a chance to try it out. Thanks. |
case "info": | ||
case "debug": | ||
case "log": | ||
var stackArray = msg.stack.split('\n\tat '); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use /\s+at\s+/
rather than '\n\tat '
?
The current version is very specific, and (for example) fails on GraalJS which uses spaces rather than tabs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should be able to adjust it to also work with GraalJS.
But we have to be very specific, as the stack also contains the message text, which can be whatever the user wants it to be, including multi-line.
Therefore I think your suggestions is way too unspecific, as it would grab occurrence of the word "at".
Worst case, I might need to identify the engine we are running on and define a regex for each specifically.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, good point. I just dropped that in to keep it working in my env. I would expect that ensuring it's the start of the line /^\s+at\s+
would be better, or even just fixing it to tab OR 4x space /^(\t| )at\s+
(<- github butchers the 4 spaces in this, but you know what I mean). Personally I wouldn't try to detect the engine.
} | ||
|
||
var _getCallerDetails = function _getCallerDetails (msg) { | ||
var matches = msg.stack.split('\n\tat ')[3].match(/(.+?) \((.+):(\d+?)\)/); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as below - this stack parsing is specific to Nashorn; /\s+at\s+/
should work for Nashorn + others.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, same comment as below.
@Martin-Stangl aplologies for the delay; been busy this week. Anyway, yes I much prefer this. I did add a couple of comments regarding stack parsing, which make this code fail on Graal (I've managed to get this working; I'll submit a PR once I'm happy with it all). Additionally the fileName property on an error is not present in Graal. Up to you if you want to attempt to handle these at this point. Also, one other question: why did you do this as an object with Object.create, rather than just a closure with error/warn/etc as methods in the returned object? Was it to try to allow polymorphism in the future? I ask because this style I find much more complex (and that's even with me coming from Java and initially attempting to OO all my JS). |
|
I noticed that while Graal doesn't have the
Not sure if that is useful! I do think at a minimum being defensive over the property not being present would be worthwhile (at least for me right now!). |
Regarding the class construction stuff, what I meant was instead of:
to use a closure, like the other existing files (by lewie I gather) use:
(Not sure what your dev background is, but I had primarily Java experience before I started with JS, but was quickly shown to stop trying to make everything OO and embrace closures and functional style, and only use classes for specific cases.) |
Thanks, I must have totally failed to see this approach in lewie's code. I actually tried to copy lewie's style before doing something else, but back then my JS knowledge did not allow me to understand it all too well. (Not that I became an expert in the past 2 weeks...) Yes, this would get rid of I think the other stuff is the exact same thing: It should be possible to use So I am wondering, if it is an advantage to keep (I guess my dev background has not much to do with the choice. I already coded in 25+ languages but programming stopped being my main business over a decade ago. And back than I mostly worked in SAP ABAP, before it knew about any OO concepts - and old ABAP does not translate to JS very well. |
The only benefit I know of explicitly using Object.create is that it allows you to develop an object hierarchy if you want polymorphism. I don't think it's necessary here though so I'd drop it.
I don't understand this? All the variables here are writable AFAIK? |
Sorry, my wording was not precise: I am referring to the object properties.
It will throw the following error: The reason is that all object properties are write protected by default and I did not specify anything changing this:
If I would want name to be writable, I would need to change it to:
I really like this, as I can make some information available without additional effort, but still be ensured that nobody changes my variables unexpectedly. Otherwise I would need to make these properties private and write getters. |
Ah, I wasn't aware that you were trying to expose readonly properties. TBH I have never really used these as I've not really seen them used much in APIs so haven't used them myself. If you want to retain them, then yes you'd need to keep this style (or alternatively Also: another question as I'm regularly butchering the code to ensure it works as a module:
Much nicer than |
I tried to simplify the style based on your example but with Yes, I intend to keep it the only one exposed function. |
@openhab-5iver: I really like your idea of creating a console log functionality. But right now I have to focus on actually doing my rule implementations and then a different project. So I will not come around to it soon. But I could wrap up the current state by replacing the core library logging with this logger in a way that it would be compatible to a future actual console implementation.
For documentation I could document the full feature set or only the console compatible usage for now. The other approach would be to be logAction compatible, what we basically are, if we ignore the log.trace and notification functionality. What do you think? Question is also on what to do with the community stuff. This all would probably break. Should we update this too or leave it to the creators? |
@Martin-Stangl re: Object.freeze, I think you only need to change your code to:
ps. now I'm using ES6, all the class-based stuff is far nicer! |
@Martin-Stangl I've just ported this to ES6 as it kept failing on Graal stacktraces; in case you're interested it's here: https://gist.github.com/jpg0/5c991b486255b015221a04056548a74b (I removed notifications in favour of providing a callback which could process them). |
It would almost work. |
That's true, but I was just trying to emulate what you've already done - right now making the config a readonly property still allows consumers to mess with what's inside it. My personal view on this is that it's not worth exposing at this point, as the use-cases for getting something that you've just set are pretty slim for a logger. For simplicity, I wouldn't expose anything that is passed in until (a) it's really needed or (b) it becomes much easier with ES6. |
Thought that I'd leave a comment here as I finally got around to adding custom appender support to my logging module, for another approach on how it could be done.
I think it strikes a good balance between not hardcoding any specific appenders (for mine I have error messages going to a telegram group for example), performance (very little code in the logging hot path) and simplicity. |
Integration of my logger as a core library.
Fully backwards compatible: Logging functions in core/utils.js have been kept but were adjusted to use the new core/log.js.
Documentation is not done yet. The sun is already rising and I want some sleep :-).
I will add it, once the implementation is considered good to merge.
But it works pretty much as described here: https://github.com/Martin-Stangl/openHAB-javascript-rule-libraries/blob/master/README.md. The relevant differences are:
logger.debug("The new entry is {}. It replaces {}.", entry, oldEntry);
Let me know if this needs any major changes or if I can go ahead an prepare the documentation.
Thanks.