Apply custom log levels to Truffle logger (#8162)

Previously custom log levels applied only to non-Truffle loggers. To allow it, filtering has to be applied appropriately at two places - first at Java's Handler and then essentially re-confirmed at SLF4J's logger to which the former forwards to.
Filters compose in an `AND` condition, therefore default log level check had to be merged into our custom filters.

`TruffleLogger` has a builtin functionality to perform the filtering when context is configured appropriately. This should be much more efficient than adding a `Filter` to the JUL Handler explicitly.

# Important Notes
```
JAVA_OPTS="-org.enso.compiler.SerializationManager.Logger.level=debug" ./built-distribution/enso-engine-0.0.0-dev-linux-amd64/enso-0.0.0-dev/bin/enso  --run
```
will now assign a custom log level to `SerializationManager` Logger.
This commit is contained in:
Hubert Plociniczak 2023-11-10 17:34:04 +01:00 committed by GitHub
parent 18c7135769
commit 51abb3e1b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 73 additions and 24 deletions

View File

@ -1,12 +1,16 @@
package org.enso.runner
import org.enso.logger.Converter
import org.enso.logger.JulHandler
import org.enso.logger.{Converter, JulHandler, LoggerSetup}
import org.enso.polyglot.debugger.{
DebugServerInfo,
DebuggerSessionManagerEndpoint
}
import org.enso.polyglot.{HostAccessFactory, PolyglotContext, RuntimeOptions}
import org.enso.polyglot.{
HostAccessFactory,
LanguageInfo,
PolyglotContext,
RuntimeOptions
}
import org.graalvm.polyglot.Engine
import org.graalvm.polyglot.Context
import org.slf4j.event.Level
@ -61,7 +65,8 @@ class ContextFactory {
if (javaHome == null) {
throw new IllegalStateException("Specify JAVA_HOME environment property");
}
val logLevelName = Converter.toJavaLevel(logLevel).getName
val julLogLevel = Converter.toJavaLevel(logLevel)
val logLevelName = julLogLevel.getName
val builder = Context
.newBuilder()
.allowExperimentalOptions(true)
@ -101,11 +106,21 @@ class ContextFactory {
new DebuggerSessionManagerEndpoint(repl, peer)
} else null
}
.option(
RuntimeOptions.LOG_LEVEL,
logLevelName
)
.logHandler(JulHandler.get())
builder.option(RuntimeOptions.LOG_LEVEL, logLevelName)
val logHandler = JulHandler.get()
val logLevels = LoggerSetup.get().getConfig.getLoggers
if (logLevels.hasEnsoLoggers()) {
logLevels.entrySet().forEach { entry =>
builder.option(
s"log.${LanguageInfo.ID}.${entry.getKey}.level",
Converter.toJavaLevel(entry.getValue).getName
)
}
}
builder
.logHandler(logHandler)
val graalpy = new File(
new File(
new File(new File(new File(projectRoot), "polyglot"), "python"),

View File

@ -55,6 +55,15 @@ public class LoggersLevels {
return loggers.isEmpty();
}
/**
* Checks if any of the custom levels refer to Enso's loggers.
*
* @return true if any of the custom log levels are for `org.enso.*` class
*/
public boolean hasEnsoLoggers() {
return loggers.keySet().stream().anyMatch(k -> k.startsWith(ENSO_PKG_PREFIX));
}
/**
* Read any loggers' levels set via `-Dfoo.bar.Logger.level=<level>` env variables.
*
@ -94,4 +103,5 @@ public class LoggersLevels {
}
private static String SYS_PROP_SUFFIX = ".Logger.level";
private static String ENSO_PKG_PREFIX = "org.enso";
}

View File

@ -9,18 +9,27 @@ import org.enso.logger.config.LoggersLevels;
/**
* An implementation of ch.qos.logback.core.filter.Filter that is created from configuration's and
* user's custom logger levels.
*
* <p>ApplicationFilter combines custom log levels filter with threshold filter.
*/
public class ApplicationFilter extends Filter<ILoggingEvent> {
private final LoggersLevels loggers;
private final Level level;
private final String prefix;
private ApplicationFilter(LoggersLevels loggers) {
private final int prefixLength;
private ApplicationFilter(LoggersLevels loggers, Level level, String prefix) {
this.loggers = loggers;
this.level = level;
this.prefix = prefix;
this.prefixLength = prefix != null ? prefix.length() + 1 : 0; // inlude `.` in `enso.`
}
@Override
public FilterReply decide(ILoggingEvent event) {
for (var entry : loggers.entrySet()) {
if (event.getLoggerName().startsWith(entry.getKey())) {
if (loggerNameMatches(entry.getKey(), event.getLoggerName())) {
Level loggerLevel = Level.convertAnSLF4JLevel(entry.getValue());
if (event.getLevel().isGreaterOrEqual(loggerLevel)) {
return FilterReply.NEUTRAL;
@ -29,10 +38,24 @@ public class ApplicationFilter extends Filter<ILoggingEvent> {
}
}
}
return FilterReply.NEUTRAL;
if (event.getLevel().isGreaterOrEqual(level)) {
return FilterReply.NEUTRAL;
} else {
return FilterReply.DENY;
}
}
public static Filter<ILoggingEvent> fromLoggers(LoggersLevels loggers) {
return new ApplicationFilter(loggers);
private boolean loggerNameMatches(String validLoggerName, String eventLoggerName) {
if (prefix != null && eventLoggerName.startsWith(prefix)) {
return eventLoggerName.substring(prefixLength).startsWith(validLoggerName);
} else {
return eventLoggerName.startsWith(validLoggerName);
}
}
public static Filter<ILoggingEvent> fromLoggers(
LoggersLevels loggers, org.slf4j.event.Level level, String prefix) {
return new ApplicationFilter(loggers, Level.convertAnSLF4JLevel(level), prefix);
}
}

View File

@ -296,7 +296,7 @@ public final class LogbackSetup extends LoggerSetup {
Filter<ILoggingEvent> filter;
LoggersLevels loggers = config != null ? config.getLoggers() : null;
if (loggers != null && !loggers.isEmpty()) {
filter = ApplicationFilter.fromLoggers(loggers);
filter = ApplicationFilter.fromLoggers(loggers, level, LANG_PREFIX);
} else {
filter = null;
}
@ -310,11 +310,13 @@ public final class LogbackSetup extends LoggerSetup {
encoder.start();
}
void finalizeAppender(ch.qos.logback.core.Appender<ILoggingEvent> appender) {
ThresholdFilter threshold = new ThresholdFilter();
threshold.setLevel(ch.qos.logback.classic.Level.convertAnSLF4JLevel(level).toString());
appender.addFilter(threshold);
threshold.setContext(ctx);
threshold.start();
if (filter == null) {
ThresholdFilter threshold = new ThresholdFilter();
threshold.setLevel(ch.qos.logback.classic.Level.convertAnSLF4JLevel(level).toString());
appender.addFilter(threshold);
threshold.setContext(ctx);
threshold.start();
}
// Root's log level is set to the minimal required log level.
// Log level is controlled by `ThresholdFilter` instead, allowing is to specify different
@ -333,4 +335,6 @@ public final class LogbackSetup extends LoggerSetup {
logger.addAppender(appender);
}
}
private static final String LANG_PREFIX = "enso";
}

View File

@ -2,10 +2,7 @@ package org.enso.logger;
import static java.util.logging.Level.*;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.logging.SimpleFormatter;
import java.util.logging.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;