Hi guys,
Problem:
In modern software development, logs doesn’t live in files – they live in databases.
We would like to be able to ship the logs from all XP-applications to a log-aggregation-service (Sentry for us). This will give us insight into all errors happening on the XP-server, enabling us to take action and fix those issues quickly.
Current situation:
We are able catch errors in our own applications, and ship the error events, but we can’t do this to 3rd-party applications, and it doesn’t feel right to do it in our open source applications on Enonic Market.
So we will always have a huge blind spot in the logs, unless we are able to forward all the logs.
What I have tried:
Getting the Root-logger
dependencies {
include "ch.qos.logback:logback-classic:1.5.18"
include "org.slf4j:slf4j-simple:2.0.17"
include "org.slf4j:slf4j-api:2.0.17"
}
Getting access to the root-logger to register a new “appender”.
// import ch.qos.logback.classic.Logger;
// import ch.qos.logback.classic.LoggerContext;
LoggerContext loggerContext = new LoggerContext();
Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
This results in:
java.util.concurrent.CompletionException: java.lang.NoClassDefFoundError: org/slf4j/spi/LoggingEventAware
at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:315)
at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:320)
at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:649)
at java.base/java.util.concurrent.CompletableFuture$Completion.run(CompletableFuture.java:482)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.NoClassDefFoundError: org/slf4j/spi/LoggingEventAware
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1017)
at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.defineClass(BundleWiringImpl.java:2338)
at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.defineClassParallel(BundleWiringImpl.java:2156)
at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.findClass(BundleWiringImpl.java:2090)
at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1556)
at org.apache.felix.framework.BundleWiringImpl.access$300(BundleWiringImpl.java:79)
at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.loadClass(BundleWiringImpl.java:1976)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)
at ch.qos.logback.classic.LoggerContext.<init>(LoggerContext.java:90)
at no.item.sentry.SentryHandler.start(SentryHandler.java:24)
at org.openjdk.nashorn.internal.scripts.Script$Recompilation$313$177$main.L:1#start(no.item.sentry:/main.js:5)
at org.openjdk.nashorn.internal.scripts.Script$Recompilation$312$440$main.L:1#L:13(no.item.sentry:/main.js:14)
at org.openjdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:646)
at org.openjdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:513)
at org.openjdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:520)
at org.openjdk.nashorn.javaadapters.java_util_concurrent_Callable.call(Unknown Source)
at com.enonic.xp.context.ContextImpl.callWith(ContextImpl.java:100)
at com.enonic.xp.lib.context.ContextHandlerBean.run(ContextHandlerBean.java:36)
at org.openjdk.nashorn.internal.scripts.Script$Recompilation$311$1299AA$context.L:1#run(no.item.sentry:/lib/xp/context.js:51)
at org.openjdk.nashorn.internal.scripts.Script$Recompilation$308$1AAAAAA$main.L:1(no.item.sentry:/main.js:10)
at org.openjdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:678)
at org.openjdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:513)
at org.openjdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:520)
at org.openjdk.nashorn.api.scripting.ScriptObjectMirror.call(ScriptObjectMirror.java:111)
at com.enonic.xp.script.impl.executor.ScriptExecutorImpl.executeRequire(ScriptExecutorImpl.java:164)
at com.enonic.xp.script.impl.executor.ScriptExecutorImpl.requireJs(ScriptExecutorImpl.java:216)
at com.enonic.xp.script.impl.executor.ScriptExecutorImpl.requireJsOrJson(ScriptExecutorImpl.java:205)
at com.enonic.xp.script.impl.executor.ScriptExportsCache.getOrCompute(ScriptExportsCache.java:53)
at com.enonic.xp.script.impl.executor.ScriptExecutorImpl.executeRequire(ScriptExecutorImpl.java:135)
at com.enonic.xp.script.impl.executor.ScriptExecutorImpl.doExecuteMain(ScriptExecutorImpl.java:118)
at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:646)
... 4 common frames omitted
I’m not sure if it’s possible to get beyond this…
Log browser approach
There exists an application called “Log Browser” that displays all the logs.
I had high hopes that I could copy it’s approach to be able to access the logs, but it appears it uses the log files on the server, and not a programmatic logger interface. It actually reads the “logback.xml”-file to create its own logging context.
This will be too lossy for our purposes, as we would need to deserialize the string in the file again. I think it also might be a performance issue to have an application running reading a file all the time.
Possible solutions
- Someone tells me what I need to do to get past my issue in the stack trace above
- Someone tells me that there already exists an interface/Java-service that I can use to get access to the root-logger or log-stream.
- XP exposes a new Java-service (in a new version of XP) that returns the root
ch.qos.logback.classic.Logger
object (Then I can hopefully register aSentryAppender
programmatically). - XP exposes another interface to get access to the stream from the logs
- Enonic Cloud re-opens the possibility to add jars to the classpath of XP so that we can start using the Sentry Logback integration again.
Best regards,
– Tom Arild