Skip to content

Commit 1c66671

Browse files
committed
Stabilized LogFileManager logging and class+method resolution
Signed-off-by: David Matějček <[email protected]>
1 parent 8687757 commit 1c66671

File tree

7 files changed

+98
-46
lines changed

7 files changed

+98
-46
lines changed

nucleus/glassfish-jul-extension/src/main/java/org/glassfish/main/jul/record/GlassFishLogRecord.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2024 Eclipse Foundation and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2025 Contributors to the Eclipse Foundation
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -54,23 +54,28 @@ public class GlassFishLogRecord extends LogRecord {
5454
* @param autodetectSource autodetect source class and method
5555
*/
5656
public GlassFishLogRecord(final Level level, final String message, final boolean autodetectSource) {
57-
this(new LogRecord(level, message), autodetectSource);
57+
super(level, null);
58+
this.threadName = Thread.currentThread().getName();
59+
this.record = new LogRecord(level, message);
60+
if (autodetectSource) {
61+
SourceDetector.detectClassAndMethod(record);
62+
} else {
63+
// Prevents autodetection done passively by JUL
64+
record.setSourceClassName(null);
65+
record.setSourceMethodName(null);
66+
}
5867
}
5968

6069

6170
/**
6271
* Wraps the log record.
6372
*
6473
* @param record the log record
65-
* @param autodetectSource autodetect source class and method
6674
*/
67-
public GlassFishLogRecord(final LogRecord record, final boolean autodetectSource) {
75+
public GlassFishLogRecord(final LogRecord record) {
6876
super(record.getLevel(), null);
6977
this.threadName = Thread.currentThread().getName();
7078
this.record = record;
71-
if (autodetectSource) {
72-
SourceDetector.detectClassAndMethod(record);
73-
}
7479
}
7580

7681

@@ -294,6 +299,7 @@ private static class SourceDetector {
294299
private static final Set<String> LOGGING_CLASSES = Set.of(
295300
"org.glassfish.main.jul.GlassFishLogger",
296301
"org.glassfish.main.jul.GlassFishLoggerWrapper",
302+
"org.glassfish.main.jul.rotation.LogFileManager$AsyncLogger",
297303
// see LogDomains in GlassFish sources
298304
"com.sun.logging.LogDomainsLogger",
299305
// remaining classes are in the JDK

nucleus/glassfish-jul-extension/src/main/java/org/glassfish/main/jul/record/MessageResolver.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022 Eclipse Foundation and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2025 Contributors to the Eclipse Foundation
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -85,7 +85,7 @@ private GlassFishLogRecord toGlassFishLogRecord(final LogRecord record) {
8585
if (GlassFishLogRecord.class.isInstance(record)) {
8686
return (GlassFishLogRecord) record;
8787
}
88-
return new GlassFishLogRecord(record, false);
88+
return new GlassFishLogRecord(record);
8989
}
9090

9191

nucleus/glassfish-jul-extension/src/main/java/org/glassfish/main/jul/rotation/LogFileManager.java

Lines changed: 74 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2024 Eclipse Foundation and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2025 Contributors to the Eclipse Foundation
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -20,19 +20,22 @@
2020
import java.io.File;
2121
import java.io.FileOutputStream;
2222
import java.io.IOException;
23-
import java.lang.System.Logger;
2423
import java.nio.charset.Charset;
2524
import java.nio.file.Files;
2625
import java.nio.file.StandardCopyOption;
2726
import java.time.LocalDateTime;
2827
import java.time.format.DateTimeFormatter;
28+
import java.util.concurrent.ConcurrentLinkedQueue;
29+
import java.util.concurrent.atomic.AtomicBoolean;
2930
import java.util.concurrent.locks.ReentrantLock;
30-
import java.util.function.Supplier;
31+
import java.util.logging.Level;
32+
import java.util.logging.Logger;
3133

34+
import org.glassfish.main.jul.record.GlassFishLogRecord;
3235
import org.glassfish.main.jul.tracing.GlassFishLoggingTracer;
3336

34-
import static java.lang.System.Logger.Level.ERROR;
35-
import static java.lang.System.Logger.Level.INFO;
37+
import static java.util.logging.Level.INFO;
38+
import static java.util.logging.Level.SEVERE;
3639
import static org.glassfish.main.jul.tracing.GlassFishLoggingTracer.trace;
3740

3841

@@ -49,7 +52,6 @@
4952
* @author David Matejcek
5053
*/
5154
public class LogFileManager {
52-
private static final Logger LOG = System.getLogger(LogFileManager.class.getName());
5355

5456
private static final DateTimeFormatter SUFFIX_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH-mm-ss");
5557

@@ -181,10 +183,9 @@ public void rollIfFileNotEmpty() {
181183
*/
182184
public void roll() {
183185
lock.lock();
184-
try {
186+
try (AsyncLogger logger = new AsyncLogger()) {
185187
final boolean wasOutputEnabled = isOutputEnabled();
186-
logInfoAsync(
187-
() -> "Rolling the file " + this.logFile + "; output was originally enabled: " + wasOutputEnabled);
188+
logger.logInfo("Rolling the file " + this.logFile + "; output was originally enabled: " + wasOutputEnabled);
188189
disableOutput();
189190
File archivedFile = null;
190191
try {
@@ -193,11 +194,11 @@ public void roll() {
193194
}
194195
archivedFile = prepareAchivedLogFileTarget();
195196
trace(LogFileManager.class, "Archived file: " + archivedFile);
196-
moveFile(logFile, archivedFile);
197+
moveFile(logFile, archivedFile, logger);
197198
forceOSFilesync(logFile);
198199
return;
199200
} catch (Exception e) {
200-
logErrorAsync("Error, could not rotate log file " + logFile, e);
201+
logger.logError("Error, could not rotate log file " + logFile, e);
201202
} finally {
202203
if (wasOutputEnabled) {
203204
enableOutput();
@@ -322,36 +323,23 @@ private void forceOSFilesync(final File file) throws IOException {
322323
}
323324

324325

325-
private void moveFile(final File logFileToArchive, final File target) throws IOException {
326-
logInfoAsync(() -> "Archiving file " + logFileToArchive + " to " + target);
326+
private void moveFile(final File logFileToArchive, final File target, final AsyncLogger logger) throws IOException {
327+
logger.logInfo("Archiving file " + logFileToArchive + " to " + target);
327328
try {
328329
Files.move(logFileToArchive.toPath(), target.toPath(), StandardCopyOption.ATOMIC_MOVE);
329330
} catch (UnsupportedOperationException | IOException e) {
330331
// If we don't succeed with file rename which most likely can happen on
331332
// Windows because of multiple file handles opened. We go through Plan B to
332333
// copy bytes explicitly to a renamed file.
333334
// Can happen on some windows file systems - then we try non-atomic version at least.
334-
logErrorAsync(String.format(
335+
logger.logError(String.format(
335336
"File %s could not be renamed to %s atomically, now trying to move it without this request.",
336337
logFileToArchive, target), e);
337338
Files.move(logFileToArchive.toPath(), target.toPath());
338339
}
339340
}
340341

341342

342-
/**
343-
* This logs in a separate thread to avoid deadlocks. The separate thread can be blocked when
344-
* the LogRecordBuffer is full while the LogFileManager is still locked and doesn't process
345-
* any records until it finishes rolling the file.
346-
* <p>
347-
* The count of messages is limited, so we can do this.
348-
*/
349-
private void logInfoAsync(final Supplier<String> message) {
350-
trace(getClass(), message);
351-
new Thread(() -> LOG.log(INFO, message), "LogFileManager-Async-Info-Logger").start();
352-
}
353-
354-
355343
/**
356344
* This logs in a separate thread to avoid deadlocks. The separate thread can be blocked when
357345
* the LogRecordBuffer is full while the LogFileManager is still locked and doesn't process
@@ -362,8 +350,64 @@ private void logInfoAsync(final Supplier<String> message) {
362350
* However it is not suitable for all errors - if we cannot write to the file, this would just create
363351
* another record which could not be written.
364352
*/
365-
private void logErrorAsync(final String message, final Exception exception) {
366-
GlassFishLoggingTracer.error(getClass(), message, exception);
367-
new Thread(() -> LOG.log(ERROR, message, exception), "LogFileManager-Async-Error-Logger").start();
353+
private static class AsyncLogger extends Thread implements AutoCloseable {
354+
355+
private final AtomicBoolean stop;
356+
private final ConcurrentLinkedQueue<AsyncLogRecord> queue;
357+
private final Logger logger;
358+
359+
private AsyncLogger() {
360+
super("LogFileManagerAsyncLogger");
361+
setDaemon(true);
362+
this.queue = new ConcurrentLinkedQueue<>();
363+
this.stop = new AtomicBoolean();
364+
this.logger = Logger.getLogger(LogFileManager.class.getName(), null);
365+
start();
366+
}
367+
368+
void logInfo(final String message) {
369+
trace(getClass(), message);
370+
queue.add(new AsyncLogRecord(INFO, message, null));
371+
}
372+
373+
void logError(final String message, final Exception exception) {
374+
GlassFishLoggingTracer.error(getClass(), message, exception);
375+
queue.add(new AsyncLogRecord(SEVERE, message, exception));
376+
}
377+
378+
@Override
379+
public void close() {
380+
this.stop.set(true);
381+
}
382+
383+
@Override
384+
public void run() {
385+
while(!stop.get()) {
386+
drainQueue();
387+
Thread.onSpinWait();
388+
}
389+
drainQueue();
390+
}
391+
392+
private void drainQueue() {
393+
while (true) {
394+
AsyncLogRecord record = queue.poll();
395+
if (record == null) {
396+
break;
397+
}
398+
logger.log(record);
399+
}
400+
}
401+
}
402+
403+
404+
private static class AsyncLogRecord extends GlassFishLogRecord {
405+
406+
private static final long serialVersionUID = -8159574547676058852L;
407+
408+
AsyncLogRecord(Level level, String message, Throwable error) {
409+
super(level, message, true);
410+
setThrown(error);
411+
}
368412
}
369413
}

nucleus/glassfish-jul-extension/src/test/java/org/glassfish/main/jul/cfg/LoggingPropertiesTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2023 Eclipse Foundation and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2025 Contributors to the Eclipse Foundation
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -38,7 +38,7 @@
3838
*/
3939
public class LoggingPropertiesTest {
4040

41-
public static final int PROPERTY_COUNT = 12;
41+
public static final int PROPERTY_COUNT = 13;
4242

4343
@Test
4444
void conversions() throws Exception {

nucleus/glassfish-jul-extension/src/test/java/org/glassfish/main/jul/handler/GlassFishLogHandlerTest.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation
2+
* Copyright (c) 2022, 2025 Contributors to the Eclipse Foundation
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -157,9 +157,10 @@ public void roll() throws Exception {
157157
() -> assertTrue(handler.getConfiguration().getLogFile().exists(), "file exists"),
158158
() -> assertThat("file content", Files.readAllLines(handler.getConfiguration().getLogFile().toPath()),
159159
contains(
160-
stringContainsInOrder("INFO", "Rolling the file ", ".log", "output was originally enabled: true"),
161-
stringContainsInOrder("INFO", "Archiving file ", ".log", " to ", ".log_")
162-
)
160+
stringContainsInOrder("INFO", "org.glassfish.main.jul.rotation.LogFileManager.roll",
161+
"Rolling the file ", ".log", "output was originally enabled: true"),
162+
stringContainsInOrder("INFO", "org.glassfish.main.jul.rotation.LogFileManager.moveFile",
163+
"Archiving file ", ".log", " to ", ".log_") )
163164
)
164165
);
165166
handler.publish(new GlassFishLogRecord(Level.SEVERE, "File two, line two", false));

nucleus/glassfish-jul-extension/src/test/java/org/glassfish/main/jul/handler/LoggingPrintStreamTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public void testPrintStacktrace() throws Exception {
9898
() -> assertEquals(1L, record.getThreadID()),
9999
() -> assertEquals(Thread.currentThread().getName(), record.getThreadName()),
100100
() -> assertSame(exception, record.getThrown()),
101-
() -> assertNull(record.getSourceMethodName())
101+
() -> assertEquals("testPrintStacktrace", record.getSourceMethodName())
102102
);
103103
}
104104

nucleus/glassfish-jul-extension/src/test/resources/logging.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ java.util.logging.ConsoleHandler.filter=org.glassfish.main.jul.test.TestFilter
55

66
org.glassfish.main.jul.handler.GlassFishLogHandler.formatter=org.glassfish.main.jul.formatter.OneLineFormatter
77
org.glassfish.main.jul.handler.GlassFishLogHandler.formatter.size.class=1
8+
org.glassfish.main.jul.handler.GlassFishLogHandler.formatter.printSource=true
89

910
.level=FINEST
1011
org.glassfish.main.jul.GlassFishLogManagerLifeCycleTest.level=FINE

0 commit comments

Comments
 (0)