Skip to content

Commit 2bdf1bc

Browse files
authored
Add JUL bridge (elastic#96683)
This commit adds the Log4j JUL bridge so that messages using JUL are more nicely converted to log4j messages. Currently these messages are captured via the stdout logging stream. This commit also adds a log4j filter to replace the logging stream filtering mechanism used to quiet some Lucene log messages that may be confusing to users. closes elastic#94613
1 parent bb3ef2b commit 2bdf1bc

File tree

11 files changed

+46
-267
lines changed

11 files changed

+46
-267
lines changed

gradle/verification-metadata.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2478,6 +2478,11 @@
24782478
<sha256 value="47f768ffd66107a66f0c2a19445ab1e42ce6719a7f30f9aa9ef96157c83949fd" origin="Generated by Gradle"/>
24792479
</artifact>
24802480
</component>
2481+
<component group="org.apache.logging.log4j" name="log4j-jul" version="2.19.0">
2482+
<artifact name="log4j-jul-2.19.0.jar">
2483+
<sha256 value="c3f0cbd1e455b1f3443c1bf0860fa3a91f5ae721d1acfeff393629fdefc23b6b" origin="Generated by Gradle"/>
2484+
</artifact>
2485+
</component>
24812486
<component group="org.apache.logging.log4j" name="log4j-slf4j-impl" version="2.19.0">
24822487
<artifact name="log4j-slf4j-impl-2.19.0.jar">
24832488
<sha256 value="015d5c229f3cd5c0ebf175c1da08d596d94043362ae9d92637d88848c90537c8" origin="Generated by Gradle"/>

qa/evil-tests/src/test/java/org/elasticsearch/common/logging/EvilLoggerConfigurationTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ public void testLoggingLevelsFromSettings() throws IOException, UserException {
144144
final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
145145
final Configuration config = ctx.getConfiguration();
146146
final Map<String, LoggerConfig> loggerConfigs = config.getLoggers();
147-
assertThat(loggerConfigs.size(), equalTo(3));
147+
assertThat(loggerConfigs.size(), equalTo(5));
148148
assertThat(loggerConfigs, hasKey(""));
149149
assertThat(loggerConfigs.get("").getLevel(), equalTo(rootLevel));
150150
assertThat(loggerConfigs, hasKey("foo"));

server/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ dependencies {
6464
// logging
6565
api "org.apache.logging.log4j:log4j-api:${versions.log4j}"
6666
api "org.apache.logging.log4j:log4j-core:${versions.log4j}"
67+
implementation "org.apache.logging.log4j:log4j-jul:${versions.log4j}"
6768

6869
api "net.java.dev.jna:jna:${versions.jna}"
6970

@@ -248,6 +249,7 @@ tasks.named("thirdPartyAudit").configure {
248249

249250
tasks.named("dependencyLicenses").configure {
250251
mapping from: /lucene-.*/, to: 'lucene'
252+
mapping from: /log4j-.*/, to: 'log4j'
251253
dependencies = project.configurations.runtimeClasspath.fileCollection {
252254
it.group.startsWith('org.elasticsearch') == false ||
253255
// keep the following org.elasticsearch jars in

server/licenses/log4j-core-LICENSE.txt

Lines changed: 0 additions & 202 deletions
This file was deleted.

server/licenses/log4j-core-NOTICE.txt

Lines changed: 0 additions & 20 deletions
This file was deleted.

server/src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939

4040
requires org.apache.logging.log4j;
4141
requires org.apache.logging.log4j.core;
42+
requires org.apache.logging.log4j.jul;
4243

4344
requires org.apache.lucene.analysis.common;
4445
requires org.apache.lucene.backward_codecs;

server/src/main/java/org/elasticsearch/common/logging/LogConfigurator.java

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.apache.logging.log4j.LogManager;
1313
import org.apache.logging.log4j.Logger;
1414
import org.apache.logging.log4j.core.Appender;
15+
import org.apache.logging.log4j.core.Filter;
1516
import org.apache.logging.log4j.core.LoggerContext;
1617
import org.apache.logging.log4j.core.appender.ConsoleAppender;
1718
import org.apache.logging.log4j.core.config.AbstractConfiguration;
@@ -21,11 +22,13 @@
2122
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
2223
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory;
2324
import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;
25+
import org.apache.logging.log4j.core.config.builder.impl.DefaultConfigurationBuilder;
2426
import org.apache.logging.log4j.core.config.composite.CompositeConfiguration;
2527
import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
2628
import org.apache.logging.log4j.core.config.properties.PropertiesConfiguration;
2729
import org.apache.logging.log4j.core.config.properties.PropertiesConfigurationBuilder;
2830
import org.apache.logging.log4j.core.config.properties.PropertiesConfigurationFactory;
31+
import org.apache.logging.log4j.jul.Log4jBridgeHandler;
2932
import org.apache.logging.log4j.status.StatusConsoleListener;
3033
import org.apache.logging.log4j.status.StatusData;
3134
import org.apache.logging.log4j.status.StatusListener;
@@ -222,6 +225,7 @@ public PropertiesConfiguration getConfiguration(final LoggerContext loggerContex
222225
properties.setProperty(name, value.replace("%marker", "[%node_name]%marker "));
223226
}
224227
}
228+
225229
// end hack
226230
return new PropertiesConfigurationBuilder().setConfigurationSource(source)
227231
.setRootProperties(properties)
@@ -241,6 +245,8 @@ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attr
241245
});
242246
assert configurations.isEmpty() == false;
243247

248+
configurations.add(createStaticConfiguration(context));
249+
244250
context.start(new CompositeConfiguration(configurations));
245251

246252
configureLoggerLevels(settings);
@@ -257,26 +263,13 @@ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attr
257263
);
258264
}
259265

266+
Log4jBridgeHandler.install(true, "", true);
267+
260268
// Redirect stdout/stderr to log4j. While we ensure Elasticsearch code does not write to those streams,
261269
// third party libraries may do that. Note that we do NOT close the streams because other code may have
262270
// grabbed a handle to the streams and intend to write to it, eg log4j for writing to the console
263-
System.setOut(
264-
new PrintStream(new LoggingOutputStream(LogManager.getLogger("stdout"), Level.INFO, List.of()), false, StandardCharsets.UTF_8)
265-
);
266-
System.setErr(
267-
new PrintStream(
268-
new LoggingOutputStream(
269-
LogManager.getLogger("stderr"),
270-
Level.WARN,
271-
// MMapDirectory messages come from Lucene, suggesting to users as a warning that they should enable preview features in
272-
// the JDK. Vector logs come from Lucene too, but only if the used explicitly disables the Vector API - no point warning
273-
// in this case.
274-
List.of("MMapDirectory", "VectorUtilProvider", "WARNING: Java vector incubator module is not readable")
275-
),
276-
false,
277-
StandardCharsets.UTF_8
278-
)
279-
);
271+
System.setOut(new PrintStream(new LoggingOutputStream(LogManager.getLogger("stdout"), Level.INFO), false, StandardCharsets.UTF_8));
272+
System.setErr(new PrintStream(new LoggingOutputStream(LogManager.getLogger("stderr"), Level.WARN), false, StandardCharsets.UTF_8));
280273

281274
final Logger rootLogger = LogManager.getRootLogger();
282275
Appender appender = Loggers.findAppender(rootLogger, ConsoleAppender.class);
@@ -289,6 +282,29 @@ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attr
289282
}
290283
}
291284

285+
/**
286+
* Creates a log4j configuration that is not changeable by users.
287+
*/
288+
private static AbstractConfiguration createStaticConfiguration(LoggerContext context) {
289+
var builder = new DefaultConfigurationBuilder<>();
290+
builder.setConfigurationSource(ConfigurationSource.NULL_SOURCE);
291+
builder.setLoggerContext(context);
292+
293+
// adding filters for confusing Lucene messages
294+
addRegexFilter(builder, "org.apache.lucene.store.MemorySegmentIndexInputProvider", "Using MemorySegmentIndexInput.*");
295+
addRegexFilter(builder, "org.apache.lucene.util.VectorUtilProvider", ".* incubator module is not readable.*");
296+
297+
return builder.build();
298+
}
299+
300+
private static void addRegexFilter(DefaultConfigurationBuilder<BuiltConfiguration> builder, String loggerName, String pattern) {
301+
var filterBuilder = builder.newFilter("RegexFilter", Filter.Result.DENY, Filter.Result.NEUTRAL);
302+
filterBuilder.addAttribute("regex", pattern);
303+
var loggerBuilder = builder.newLogger(loggerName);
304+
loggerBuilder.add(filterBuilder);
305+
builder.add(loggerBuilder);
306+
}
307+
292308
/**
293309
* Removes the appender for the console, if one exists.
294310
*/

0 commit comments

Comments
 (0)