Skip to content
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

Log full exception information when calling JSR223 scripts #3176

Merged
merged 2 commits into from
Nov 27, 2022

Conversation

ccutrer
Copy link
Contributor

@ccutrer ccutrer commented Nov 25, 2022

The scripts are user code, so in order to help them debug their code, the full stack trace is very useful.

The scripts are user code, so in order to help them debug their code,
the full stack trace is very useful.

Signed-off-by: Cody Cutrer <cody@cutrer.us>
@ccutrer ccutrer requested a review from a team as a code owner November 25, 2022 23:15
@@ -198,7 +198,7 @@ public void loadScript(String engineIdentifier, InputStreamReader scriptData,
logger.trace("ScriptEngine does not support Invocable interface");
}
} catch (Exception ex) {
logger.error("Error during evaluation of script '{}': {}", engineIdentifier, ex.getMessage());
logger.error("Error during evaluation of script '{}'", engineIdentifier, ex);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

on the JRuby side, this will always be a ScriptException, and it will always have a cause, and the cause will only have the stack trace from the Ruby code. So it'd be most useful to automatically unwrap one level down. But I'm not familiar with other script engines, and didn't want to lose information, so I just left it exposing the main exception.

Copy link
Contributor

Choose a reason for hiding this comment

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

At JS Scripting, this will also be very useful for debugging.
We have a new issue, that relates to this (openhab/openhab-js#176).

I haven’t tested how the stack trace looks for JS Scripting here, but in our case, the exception is thrown because the user passes a parameter of wrong type to a Java method.
I‘ll try to have check the stack trace later.

Copy link
Contributor

Choose a reason for hiding this comment

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

FYI: Wrapping down wouldn’t work here, because for example in JS Scripting we can also have a IllegalArgumentException in the case described above.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

FYI: Wrapping down wouldn’t work here, because for example in JS Scripting we can also have a IllegalArgumentException in the case described above.

In the jrubyscripting addon there are a couple of places that the engine is called directly to set things up, and exceptions are caught there. I did do unwrapping of those, but did it in a safe manner in case there isn't a cause:

openhab/openhab-addons@4739f12#diff-e433203688ef4765215fe6f7efe301f64fe4a0365cc635a07c3a91adc18d6f0fR296

Copy link
Contributor

Choose a reason for hiding this comment

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

I‘ve just checked the JS Scripting code, there is already some code for the logging in place, I‘ll see if I can improve it.

@ccutrer
Copy link
Contributor Author

ccutrer commented Nov 26, 2022

Here's some examples of what's logged for JRuby:

For this Ruby code (throwing a Ruby exception)

def throws_exception
  x.nil?
end

throws_exception

You get:

2022-11-26 09:29:23.113 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/etc/openhab/automation/jsr223/ruby/personal/test.rb'
javax.script.ScriptException: Error during evaluation of Ruby in /etc/openhab/scripts/lib/ruby/gem_home/gems/openhab-jrubyscripting-5.0.0.rc2/lib/openhab/core/entity_lookup.rb at line 34: (NameError) undefined local variable or method `x' for main:Object
        at org.jruby.embed.jsr223.JRubyEngine.wrapRaiseException(JRubyEngine.java:277) ~[?:?]
        at org.jruby.embed.jsr223.JRubyEngine.doEval(JRubyEngine.java:102) ~[?:?]
        at org.jruby.embed.jsr223.JRubyEngine.eval(JRubyEngine.java:126) ~[?:?]
        at org.jruby.embed.jsr223.JRubyEngine.eval(JRubyEngine.java:152) ~[?:?]
        at org.openhab.core.automation.module.script.internal.ScriptEngineManagerImpl.loadScript(ScriptEngineManagerImpl.java:189) ~[?:?]
        at org.openhab.core.automation.module.script.rulesupport.loader.ScriptFileWatcher.createAndLoad(ScriptFileWatcher.java:251) ~[?:?]
        at org.openhab.core.automation.module.script.rulesupport.loader.ScriptFileWatcher.importFile(ScriptFileWatcher.java:231) ~[?:?]
        at org.openhab.core.automation.module.script.rulesupport.loader.ScriptFileWatcher.lambda$2(ScriptFileWatcher.java:223) ~[?:?]
        at java.util.Optional.ifPresent(Optional.java:183) ~[?:?]
        at org.openhab.core.automation.module.script.rulesupport.loader.ScriptFileWatcher.importFileWhenReady(ScriptFileWatcher.java:221) ~[?:?]
        at org.openhab.core.automation.module.script.rulesupport.loader.ScriptFileWatcher.processWatchEvent(ScriptFileWatcher.java:198) ~[?:?]
        at org.openhab.core.service.WatchQueueReader.lambda$5(WatchQueueReader.java:357) ~[?:?]
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) [?:?]
        at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) [?:?]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
        at java.lang.Thread.run(Thread.java:829) [?:?]
Caused by: org.jruby.exceptions.NameError: (NameError) undefined local variable or method `x' for main:Object
        at RUBY.method_missing(/etc/openhab/scripts/lib/ruby/gem_home/gems/openhab-jrubyscripting-5.0.0.rc2/lib/openhab/core/entity_lookup.rb:34) ~[?:?]
        at RUBY.method_missing(/etc/openhab/scripts/lib/ruby/gem_home/gems/openhab-jrubyscripting-5.0.0.rc2/lib/openhab/core/actions.rb:149) ~[?:?]
        at RUBY.throws_exception(/etc/openhab/automation/jsr223/ruby/personal/test.rb:7) ~[?:?]
        at RUBY.<main>(/etc/openhab/automation/jsr223/ruby/personal/test.rb:10) ~[?:?]

For this Ruby code (throwing a Java NPE):

java.util.ArrayList.new(nil)

You get:

2022-11-26 09:30:51.405 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/etc/openhab/automation/jsr223/ruby/personal/test.rb'
javax.script.ScriptException: java.lang.NullPointerException
        at org.jruby.embed.jsr223.JRubyEngine.wrapException(JRubyEngine.java:263) ~[?:?]
        at org.jruby.embed.jsr223.JRubyEngine.doEval(JRubyEngine.java:104) ~[?:?]
        at org.jruby.embed.jsr223.JRubyEngine.eval(JRubyEngine.java:126) ~[?:?]
        at org.jruby.embed.jsr223.JRubyEngine.eval(JRubyEngine.java:152) ~[?:?]
        at org.openhab.core.automation.module.script.internal.ScriptEngineManagerImpl.loadScript(ScriptEngineManagerImpl.java:189) ~[?:?]
        at org.openhab.core.automation.module.script.rulesupport.loader.ScriptFileWatcher.createAndLoad(ScriptFileWatcher.java:251) ~[?:?]
        at org.openhab.core.automation.module.script.rulesupport.loader.ScriptFileWatcher.importFile(ScriptFileWatcher.java:231) ~[?:?]
        at org.openhab.core.automation.module.script.rulesupport.loader.ScriptFileWatcher.lambda$2(ScriptFileWatcher.java:223) ~[?:?]
        at java.util.Optional.ifPresent(Optional.java:183) ~[?:?]
        at org.openhab.core.automation.module.script.rulesupport.loader.ScriptFileWatcher.importFileWhenReady(ScriptFileWatcher.java:221) ~[?:?]
        at org.openhab.core.automation.module.script.rulesupport.loader.ScriptFileWatcher.processWatchEvent(ScriptFileWatcher.java:198) ~[?:?]
        at org.openhab.core.service.WatchQueueReader.lambda$5(WatchQueueReader.java:357) ~[?:?]
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) [?:?]
        at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) [?:?]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
        at java.lang.Thread.run(Thread.java:829) [?:?]
Caused by: java.lang.NullPointerException
        at java.util.ArrayList.<init>(java/util/ArrayList.java:179) ~[?:?]
        at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:?]
        at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(jdk/internal/reflect/NativeConstructorAccessorImpl.java:62) ~[?:?]
        at jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(jdk/internal/reflect/DelegatingConstructorAccessorImpl.java:45) ~[?:?]
        at java.lang.reflect.Constructor.newInstance(java/lang/reflect/Constructor.java:490) ~[?:?]
        at org.jruby.javasupport.JavaConstructor.newInstanceDirect(org/jruby/javasupport/JavaConstructor.java:269) ~[?:?]
        at org.jruby.RubyClass.new(org/jruby/RubyClass.java:890) ~[?:?]
        at org.jruby.RubyClass$INVOKER$i$newInstance.call(org/jruby/RubyClass$INVOKER$i$newInstance.gen) ~[?:?]
        at RUBY.<main>(/etc/openhab/automation/jsr223/ruby/personal/test.rb:6) ~[?:?]
        at org.jruby.Ruby.runInterpreter(org/jruby/Ruby.java:1294) ~[?:?]
        at org.jruby.Ruby.runInterpreter(org/jruby/Ruby.java:1298) ~[?:?]
        at org.jruby.embed.internal.EmbedEvalUnitImpl.run(org/jruby/embed/internal/EmbedEvalUnitImpl.java:119) ~[?:?]
        at org.jruby.embed.jsr223.JRubyEngine.doEval(org/jruby/embed/jsr223/JRubyEngine.java:99) ~[?:?]
        at org.jruby.embed.jsr223.JRubyEngine.eval(org/jruby/embed/jsr223/JRubyEngine.java:126) ~[?:?]
        at org.jruby.embed.jsr223.JRubyEngine.eval(org/jruby/embed/jsr223/JRubyEngine.java:152) ~[?:?]
        at org.openhab.core.automation.module.script.internal.ScriptEngineManagerImpl.loadScript(org/openhab/core/automation/module/script/internal/ScriptEngineManagerImpl.java:189) ~[?:?]
        at org.openhab.core.automation.module.script.rulesupport.loader.ScriptFileWatcher.createAndLoad(org/openhab/core/automation/module/script/rulesupport/loader/ScriptFileWatcher.java:251) ~[?:?]
        at org.openhab.core.automation.module.script.rulesupport.loader.ScriptFileWatcher.importFile(org/openhab/core/automation/module/script/rulesupport/loader/ScriptFileWatcher.java:231) ~[?:?]
        at org.openhab.core.automation.module.script.rulesupport.loader.ScriptFileWatcher.lambda$2(org/openhab/core/automation/module/script/rulesupport/loader/ScriptFileWatcher.java:223) ~[?:?]
        at java.util.Optional.ifPresent(java/util/Optional.java:183) ~[?:?]
        at org.openhab.core.automation.module.script.rulesupport.loader.ScriptFileWatcher.importFileWhenReady(org/openhab/core/automation/module/script/rulesupport/loader/ScriptFileWatcher.java:221) ~[?:?]
        at org.openhab.core.automation.module.script.rulesupport.loader.ScriptFileWatcher.processWatchEvent(org/openhab/core/automation/module/script/rulesupport/loader/ScriptFileWatcher.java:198) ~[?:?]
        at org.openhab.core.service.WatchQueueReader.lambda$5(org/openhab/core/service/WatchQueueReader.java:357) ~[?:?]
        at java.util.concurrent.Executors$RunnableAdapter.call(java/util/concurrent/Executors.java:515) ~[?:?]
        at java.util.concurrent.FutureTask.run(java/util/concurrent/FutureTask.java:264) ~[?:?]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(java/util/concurrent/ScheduledThreadPoolExecutor.java:304) ~[?:?]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(java/util/concurrent/ThreadPoolExecutor.java:1128) ~[?:?]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(java/util/concurrent/ThreadPoolExecutor.java:628) ~[?:?]
        at java.lang.Thread.run(java/lang/Thread.java:829) ~[?:?]

@J-N-K
Copy link
Member

J-N-K commented Nov 26, 2022

In the first case I think that

2022-11-26 09:29:23.113 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/etc/openhab/automation/jsr223/ruby/personal/test.rb'
javax.script.ScriptException: Error during evaluation of Ruby in /etc/openhab/scripts/lib/ruby/gem_home/gems/openhab-jrubyscripting-5.0.0.rc2/lib/openhab/core/entity_lookup.rb at line 34: (NameError) undefined local variable or method `x' for main:Object

would be logged with the current code. That's pretty clear and the stack trace does not provide any additional information. In the second case it's true that the source of the NPE is not visible. However I wonder if someone without knowledge of Java is able to figure out what the relevant information from that stack trace is and if the stack trace in the first case is not adding confusion instead of removing it.

@ccutrer
Copy link
Contributor Author

ccutrer commented Nov 26, 2022

In the first case I think that

2022-11-26 09:29:23.113 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/etc/openhab/automation/jsr223/ruby/personal/test.rb'
javax.script.ScriptException: Error during evaluation of Ruby in /etc/openhab/scripts/lib/ruby/gem_home/gems/openhab-jrubyscripting-5.0.0.rc2/lib/openhab/core/entity_lookup.rb at line 34: (NameError) undefined local variable or method `x' for main:Object

would be logged with the current code. That's pretty clear and the stack trace does not provide any additional information.

Log line under the current code is

2022-11-26 13:50:05.478 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/etc/openhab/automation/jsr223/ruby/personal/test.rb': Error during evaluation of Ruby in /etc/openhab/scripts/lib/ruby/gem_home/gems/openhab-jrubyscripting-5.0.0.rc3/lib/openhab/core/entity_lookup.rb at line 34: (NameError) undefined local variable or method `x' for main:Object

Except that entity_lookup.rb is a line inside the helper library, and does not point at all to what in the user code caused it (test.rb:7).

In the second case it's true that the source of the NPE is not visible. However I wonder if someone without knowledge of Java is able to figure out what the relevant information from that stack trace is and if the stack trace in the first case is not adding confusion instead of removing it.

Even myself have no idea what all the intervening java-ish stack frames are, but it's easy enough for me to search it for test.rb and get the line:

at RUBY.<main>(/etc/openhab/automation/jsr223/ruby/personal/test.rb:6) ~[?:?]

which tells me exactly where the problem is.

Maybe a compromise would be to do it like RuleEngineImpl.java does, only logging the stack trace if set to debug level?:

        } catch (Throwable t) {
            logger.error("Failed to execute rule '{}': {}", ruleUID, t.getMessage());
            logger.debug("", t);
        }

@J-N-K
Copy link
Member

J-N-K commented Nov 26, 2022

Sounds good.

@ccutrer
Copy link
Contributor Author

ccutrer commented Nov 26, 2022

Sounds good.

The split logging (exception message at error level, full stack trace at debug level)?

exception message at error level; full exception details at debug level

Signed-off-by: Cody Cutrer <cody@cutrer.us>
@J-N-K
Copy link
Member

J-N-K commented Nov 26, 2022

Yes

@ccutrer
Copy link
Contributor Author

ccutrer commented Nov 26, 2022

Sounds good.

The split logging (exception message at error level, full stack trace at debug level)?

I'm assuming that's what you meant. I've tested that configuration, and updated the PR.

Copy link
Member

@J-N-K J-N-K left a comment

Choose a reason for hiding this comment

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

Thanks

@J-N-K J-N-K added enhancement An enhancement or new feature of the Core automation labels Nov 27, 2022
@J-N-K J-N-K added this to the 3.4 milestone Nov 27, 2022
@J-N-K J-N-K merged commit 0cceb5b into openhab:main Nov 27, 2022
@ccutrer ccutrer deleted the script-engine-full-exception branch November 29, 2022 16:15
@wborn wborn changed the title log full exception information when calling JSR223 scripts Log full exception information when calling JSR223 scripts Nov 30, 2022
splatch pushed a commit to ConnectorIO/copybara-hab-core that referenced this pull request Jul 12, 2023
)

* log full exception information when calling JSR223 scripts

The scripts are user code, so in order to help them debug their code,
the full stack trace is very useful.

Signed-off-by: Cody Cutrer <cody@cutrer.us>
GitOrigin-RevId: 0cceb5b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
automation enhancement An enhancement or new feature of the Core
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants