Skip to content

Commit fbe2766

Browse files
authored
Fix prefix logging
Today we add a prefix when logging within Elasticsearch. This prefix contains the node name, and index and shard-level components if appropriate. Due to some implementation details with Log4j 2 , this does not work for integration tests; instead what we see is the node name for the last node to startup. The implementation detail here is that Log4j 2 there is only one logger for a name, message factory pair, and the key derived from the message factory is the class name of the message factory. So, when the last node starts up and starts setting prefixes on its message factories, it will impact the loggers for the other nodes. Additionally, the prefixes are lost when logging an exception. This is due to another implementation detail in Log4j 2. Namely, since we log exceptions using a parameterized message, Log4j 2 decides that that means that we do not want to use the message factory that we have provided (the prefix message factory) and so logs the exception without the prefix. This commit fixes both of these issues. Relates #20429
1 parent 1a60e1c commit fbe2766

File tree

17 files changed

+158
-271
lines changed

17 files changed

+158
-271
lines changed

core/src/main/java/org/elasticsearch/common/logging/ESLoggerFactory.java

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,18 @@
2222
import org.apache.logging.log4j.Level;
2323
import org.apache.logging.log4j.LogManager;
2424
import org.apache.logging.log4j.Logger;
25-
import org.apache.logging.log4j.message.MessageFactory;
25+
import org.apache.logging.log4j.spi.ExtendedLogger;
2626
import org.elasticsearch.common.settings.Setting;
2727
import org.elasticsearch.common.settings.Setting.Property;
2828

29-
import java.util.Locale;
30-
import java.util.function.Function;
31-
3229
/**
3330
* Factory to get {@link Logger}s
3431
*/
35-
public abstract class ESLoggerFactory {
32+
public final class ESLoggerFactory {
33+
34+
private ESLoggerFactory() {
35+
36+
}
3637

3738
public static final Setting<Level> LOG_DEFAULT_LEVEL_SETTING =
3839
new Setting<>("logger.level", Level.INFO.name(), Level::valueOf, Property.NodeScope);
@@ -42,23 +43,12 @@ public abstract class ESLoggerFactory {
4243

4344
public static Logger getLogger(String prefix, String name) {
4445
name = name.intern();
45-
final Logger logger = getLogger(new PrefixMessageFactory(), name);
46-
final MessageFactory factory = logger.getMessageFactory();
47-
// in some cases, we initialize the logger before we are ready to set the prefix
48-
// we can not re-initialize the logger, so the above getLogger might return an existing
49-
// instance without the prefix set; thus, we hack around this by resetting the prefix
50-
if (prefix != null && factory instanceof PrefixMessageFactory) {
51-
((PrefixMessageFactory) factory).setPrefix(prefix.intern());
52-
}
53-
return logger;
54-
}
55-
56-
public static Logger getLogger(MessageFactory messageFactory, String name) {
57-
return LogManager.getLogger(name, messageFactory);
46+
final Logger logger = LogManager.getLogger(name);
47+
return new PrefixLogger((ExtendedLogger)logger, name, prefix);
5848
}
5949

6050
public static Logger getLogger(String name) {
61-
return getLogger((String)null, name);
51+
return getLogger(null, name);
6252
}
6353

6454
public static DeprecationLogger getDeprecationLogger(String name) {
@@ -73,8 +63,4 @@ public static Logger getRootLogger() {
7363
return LogManager.getRootLogger();
7464
}
7565

76-
private ESLoggerFactory() {
77-
// Utility class can't be built.
78-
}
79-
8066
}

core/src/main/java/org/elasticsearch/common/logging/Loggers.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ public static Logger getLogger(String loggerName, Settings settings, String... p
9797
}
9898

9999
public static Logger getLogger(Logger parentLogger, String s) {
100-
return ESLoggerFactory.getLogger(parentLogger.<MessageFactory>getMessageFactory(), getLoggerName(parentLogger.getName() + s));
100+
assert parentLogger instanceof PrefixLogger;
101+
return ESLoggerFactory.getLogger(((PrefixLogger)parentLogger).prefix(), getLoggerName(parentLogger.getName() + s));
101102
}
102103

103104
public static Logger getLogger(String s) {
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.common.logging;
21+
22+
import org.apache.logging.log4j.Level;
23+
import org.apache.logging.log4j.Marker;
24+
import org.apache.logging.log4j.MarkerManager;
25+
import org.apache.logging.log4j.message.Message;
26+
import org.apache.logging.log4j.spi.ExtendedLogger;
27+
import org.apache.logging.log4j.spi.ExtendedLoggerWrapper;
28+
29+
import java.lang.ref.WeakReference;
30+
import java.util.WeakHashMap;
31+
32+
class PrefixLogger extends ExtendedLoggerWrapper {
33+
34+
// we can not use the built-in Marker tracking (MarkerManager) because the MarkerManager holds
35+
// a permanent reference to the marker; however, we have transient markers from index-level and
36+
// shard-level components so this would effectively be a memory leak
37+
private static final WeakHashMap<String, WeakReference<Marker>> markers = new WeakHashMap<>();
38+
39+
private final Marker marker;
40+
41+
public String prefix() {
42+
return marker.getName();
43+
}
44+
45+
PrefixLogger(final ExtendedLogger logger, final String name, final String prefix) {
46+
super(logger, name, null);
47+
48+
final String actualPrefix = (prefix == null ? "" : prefix).intern();
49+
final Marker actualMarker;
50+
// markers is not thread-safe, so we synchronize access
51+
synchronized (markers) {
52+
final WeakReference<Marker> marker = markers.get(actualPrefix);
53+
final Marker maybeMarker = marker == null ? null : marker.get();
54+
if (maybeMarker == null) {
55+
actualMarker = new MarkerManager.Log4jMarker(actualPrefix);
56+
markers.put(actualPrefix, new WeakReference<>(actualMarker));
57+
} else {
58+
actualMarker = maybeMarker;
59+
}
60+
}
61+
this.marker = actualMarker;
62+
}
63+
64+
@Override
65+
public void logMessage(final String fqcn, final Level level, final Marker marker, final Message message, final Throwable t) {
66+
assert marker == null;
67+
super.logMessage(fqcn, level, this.marker, message, t);
68+
}
69+
70+
}

core/src/main/java/org/elasticsearch/common/logging/PrefixMessageFactory.java

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

0 commit comments

Comments
 (0)