structurizr / java

Structurizr for Java
https://docs.structurizr.com/java
Apache License 2.0
1.01k stars 290 forks source link

!script file name and path #344

Open neopaf opened 1 month ago

neopaf commented 1 month ago

Description

Currently scripts have no file name in debug info, so when I'm trying to debug them I get no breakpoints and no stepping around.

Priority

I have no budget and there's no rush, please add this feature for free

More information

Workaround: breakpoint in some lib method, like System.println and inspect whatever passed in there: Снимок экрана 2024-09-27 в 13 43 57

(This is much faster than reading tons of manuals/sources sometimes, for some people)

Yet, it would be easier still if that debug info would've been provided. I guess, it's pretty simple option of GoogleScriptEngine, which costs next to nothing runtime. Maybe you'd turn it on by default (or provide some way to enable it)?

simonbrowndotje commented 1 month ago

I guess, it's pretty simple option of GoogleScriptEngine, which costs next to nothing runtime. Maybe you'd turn it on by default (or provide some way to enable it)?

I don't have any knowledge of GoogleScriptEngine, and it isn't obvious how it helps from a quick Google search. Please feel free to submit a PR or provide more information. Thanks!

neopaf commented 1 month ago

Thanks for quick reaction, Simon. I'm referring to object, obtained here https://github.com/structurizr/java/blob/d5e0d201e5fc9e1d6e6d4f3ea6d694e2706afbb0/structurizr-dsl/src/main/java/com/structurizr/dsl/ScriptDslContext.java#L47

Alas, I forgot how I did it like 5 years ago. But please consider changing this approach: https://github.com/structurizr/java/blob/d5e0d201e5fc9e1d6e6d4f3ea6d694e2706afbb0/structurizr-dsl/src/main/java/com/structurizr/dsl/ScriptDslContext.java#L73

Without name of file consequent stack trace (and IDE debugging) can not show file name (and open it in IDE, handle breakpoints), because runtime has no idea about original file name, and even if file was external (deployment.groovy), it shows this (Script136.groovy):

org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:277)
        at Script136$_run_closure2.doCall(Script136.groovy:15)

Let us start by passing eval not string, but Reader, along these lines:

if(input is external)
  engine.eval(new FileReader(scriptFile), bindings); // when input is external file; when 
else
  engine.eval(script.toString(), bindings); // as is

This will give proper file name to IDE and backtrace, hopefully enabling breakpoints and make life of script developers a tad bit happier?

simonbrowndotje commented 1 month ago

I've pushed some changes (see https://github.com/structurizr/java/commit/ff37cc29fd4a9bfd9a79b669bae9406a95e1f750) ... but they only seem to work for Ruby unfortunately. If you have any further ideas, please let me know.

neopaf commented 1 month ago

wow, that was fast, Simon. thanks for your effort. testing... will consider a bit deeper this weekend

neopaf commented 1 month ago

For Googlers: take structurizr java, remove signing part from build.gradle,

./gradlew publishToMavenLocal 

Check out https://github.com/structurizr/lite, change structurizrVersion to 3.1.0,

./gradlew bootWar
#use build/libs/structurizr-lite.war 

Simon, thanks a lot: I'm super happy now, breakpoints and stack filtering work now: Снимок экрана 2024-09-28 в 09 10 24

Maybe some stack trace sanitation would help, reading....

neopaf commented 1 month ago

While IntelliJ IDEA manages to filter stack fine. Currently message has no line number where bad things in script happened:

Error

workspace.dsl: Error running script at deployment.groovy, caused by javax.script.ScriptException: javax.script.ScriptException: java.lang.reflect.UndeclaredThrowableException at line 711 of /Users/paf/Documents/Hermes/c4/./workspace.dsl: }

Neither do log files:

Name: PROD_MSK -> PROD/msk
javax.script.ScriptException: javax.script.ScriptException: java.lang.reflect.UndeclaredThrowableException
        at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:158)
        at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:132)
        at com.structurizr.dsl.ScriptDslContext.run(ScriptDslContext.java:70)
        at com.structurizr.dsl.ExternalScriptDslContext.end(ExternalScriptDslContext.java:23)
        at com.structurizr.dsl.StructurizrDslParser.endContext(StructurizrDslParser.java:1154)
        at com.structurizr.dsl.StructurizrDslParser.parse(StructurizrDslParser.java:238)
        at com.structurizr.dsl.StructurizrDslParser.parse(StructurizrDslParser.java:136)
        at com.structurizr.lite.component.workspace.FileSystemWorkspaceComponentImpl.loadWorkspaceFromDsl(FileSystemWorkspaceComponentImpl.java:146)
        at com.structurizr.lite.component.workspace.FileSystemWorkspaceComponentImpl.loadWorkspace(FileSystemWorkspaceComponentImpl.java:113)
        at com.structurizr.lite.component.workspace.FileSystemWorkspaceComponentImpl.getWorkspace(FileSystemWorkspaceComponentImpl.java:203)
        at com.structurizr.lite.web.ApiController.getWorkspace(ApiController.java:37)
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
        at java.base/java.lang.reflect.Method.invoke(Method.java:580)
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:255)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:188)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:926)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:831)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903)
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
        at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:384)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:905)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1741)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1190)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
        at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: javax.script.ScriptException: java.lang.reflect.UndeclaredThrowableException
        at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:320)
        at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:155)
        ... 62 more
Caused by: java.lang.reflect.UndeclaredThrowableException
        at jdk.proxy1/jdk.proxy1.$Proxy84.accept(Unknown Source)
        at java.base/java.lang.Iterable.forEach(Iterable.java:75)
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
        at java.base/java.lang.reflect.Method.invoke(Method.java:580)
        at org.codehaus.groovy.runtime.callsite.PlainObjectMetaMethodSite.doInvoke(PlainObjectMetaMethodSite.java:48)
        at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoCachedMethodSiteNoUnwrap.invoke(PojoMetaMethodSite.java:198)
        at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:51)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:139)
        at deployment.run(deployment.groovy:8)
        at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:317)
        ... 63 more
Caused by: java.lang.Exception: Exception while handling {{struct}} in relation[{1812 | vrf SIG | } ---[Diameter Gy запросы]---> {1839 | mn-hmsfe | }]
        at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62)
        at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502)
        at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
        at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:72)
        at org.codehaus.groovy.reflection.CachedConstructor.doConstructorInvoke(CachedConstructor.java:59)
        at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrap.callConstructor(ConstructorSite.java:84)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:59)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:263)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:286)
        at deployment$_run_closure2.doCall(deployment.groovy:22)
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
        at java.base/java.lang.reflect.Method.invoke(Method.java:580)
        at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:107)
        at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:323)
        at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:274)
        at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1030)
        at groovy.lang.Closure.call(Closure.java:427)
        at org.codehaus.groovy.runtime.ConvertedClosure.invokeCustom(ConvertedClosure.java:50)
        at org.codehaus.groovy.runtime.ConversionHandler.invoke(ConversionHandler.java:112)
        ... 75 more
Caused by: java.lang.Exception: For DeploymentEnvironment[PROD_MSK] did not find inventory, please check deployment.groovy parameters
        at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62)
        at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502)
        at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
        at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:72)
        at org.codehaus.groovy.reflection.CachedConstructor.doConstructorInvoke(CachedConstructor.java:59)
        at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrap.callConstructor(ConstructorSite.java:84)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:59)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:263)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:277)
        at deployment$_run_closure2.doCall(deployment.groovy:15)
        ... 84 more
2024-09-28T09:06:59.536+03:00 ERROR 
neopaf commented 1 month ago

I guess, most interesting lines with .groovy file line on them got hidden under ".... N more" cuts.

Not sure how to handle that properly. Googling gives this: https://stackoverflow.com/questions/6259202/how-do-i-print-a-groovy-stack-trace

Not sure how to do that in properly, since you don't really know about Groovy implementation details under the ScriptEngine's hood.

neopaf commented 1 month ago

Maybe just simple call would work, since we know that GroovyScriptEngine is on your class path. And it removes only groovy implementation calls, not affecting any other language. So I assume it is save to call it. Prior to printing error messages to log/ui.

neopaf commented 1 month ago

Just call this one methods around exception to cleanup stack trace. https://docs.groovy-lang.org/latest/html/gapi/org/codehaus/groovy/runtime/StackTraceUtils.html#sanitize(java.lang.Throwable)

Hopefully this would reach all targets...