@@ -147,44 +147,44 @@ public Logger computeIfAbsent(
147
147
return logger ;
148
148
}
149
149
150
+ // Creating a logger is expensive and might cause lookups and locks, possibly deadlocks:
151
+ // https://github.com/apache/logging-log4j2/issues/3252
152
+ // https://github.com/apache/logging-log4j2/issues/3399
153
+ //
154
+ // Creating loggers without a lock, allows multiple threads to create loggers in parallel, which also
155
+ // improves performance.
156
+ // Since all loggers with the same parameters are equivalent, we can safely return the logger from the
157
+ // thread that finishes first.
158
+ Logger newLogger = loggerSupplier .apply (name , messageFactory );
159
+
160
+ // Report name and message factory mismatch if there are any
161
+ final String loggerName = newLogger .getName ();
162
+ final MessageFactory loggerMessageFactory = newLogger .getMessageFactory ();
163
+ if (!loggerName .equals (name ) || !loggerMessageFactory .equals (messageFactory )) {
164
+ StatusLogger .getLogger ()
165
+ .error (
166
+ "Newly registered logger with name `{}` and message factory `{}`, is requested to be associated with a different name `{}` or message factory `{}`.\n "
167
+ + "Effectively the message factory of the logger will be used and the other one will be ignored.\n "
168
+ + "This generally hints a problem at the logger context implementation.\n "
169
+ + "Please report this using the Log4j project issue tracker." ,
170
+ loggerName ,
171
+ loggerMessageFactory ,
172
+ name ,
173
+ messageFactory );
174
+ }
175
+
150
176
// Write lock slow path: Insert the logger
151
177
writeLock .lock ();
152
178
try {
153
179
154
- // See if the logger is created by another thread in the meantime
155
- final Map <String , WeakReference <Logger >> loggerRefByName =
156
- loggerRefByNameByMessageFactory .computeIfAbsent (messageFactory , ignored -> new HashMap <>());
157
- WeakReference <Logger > loggerRef = loggerRefByName .get (name );
158
- if (loggerRef != null && (logger = loggerRef .get ()) != null ) {
159
- return logger ;
160
- }
161
-
162
- // Create the logger
163
- logger = loggerSupplier .apply (name , messageFactory );
164
-
165
- // Report name and message factory mismatch if there are any
166
- final String loggerName = logger .getName ();
167
- final MessageFactory loggerMessageFactory = logger .getMessageFactory ();
168
- if (!loggerMessageFactory .equals (messageFactory )) {
169
- StatusLogger .getLogger ()
170
- .error (
171
- "Newly registered logger with name `{}` and message factory `{}`, is requested to be associated with a different name `{}` or message factory `{}`.\n "
172
- + "Effectively the message factory of the logger will be used and the other one will be ignored.\n "
173
- + "This generally hints a problem at the logger context implementation.\n "
174
- + "Please report this using the Log4j project issue tracker." ,
175
- loggerName ,
176
- loggerMessageFactory ,
177
- name ,
178
- messageFactory );
179
- // Register logger under alternative keys
180
- loggerRefByNameByMessageFactory
181
- .computeIfAbsent (loggerMessageFactory , ignored -> new HashMap <>())
182
- .putIfAbsent (loggerName , new WeakReference <>(logger ));
183
- }
184
-
185
- // Insert the logger
186
- loggerRefByName .put (name , new WeakReference <>(logger ));
187
- return logger ;
180
+ Logger currentLogger = loggerRefByNameByMessageFactory
181
+ .computeIfAbsent (messageFactory , ignored -> new HashMap <>())
182
+ .computeIfAbsent (name , ignored -> new WeakReference <>(newLogger ))
183
+ .get ();
184
+ // Null check, in the rare case a GC occurs between:
185
+ // * new WeakReference (last reference to `newLogger`)
186
+ // * WeakReference.get()
187
+ return currentLogger == null ? newLogger : currentLogger ;
188
188
} finally {
189
189
writeLock .unlock ();
190
190
}
0 commit comments