Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixed issues around rolling the server.log file
- When rolling, several log records were lost - doesn't happen any more - Refactoring: separated features to own classes, which resolved some locking and reduced need of sharing method handles. Signed-off-by: David Matějček <david.matejcek@omnifish.ee>
- Loading branch information
Showing
6 changed files
with
394 additions
and
250 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
152 changes: 152 additions & 0 deletions
152
...lassfish-jul-extension/src/main/java/org/glassfish/main/jul/rotation/LogFileArchiver.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
/* | ||
* Copyright (c) 2024 Contributors to the Eclipse Foundation | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Eclipse Public License v. 2.0, which is available at | ||
* http://www.eclipse.org/legal/epl-2.0. | ||
* | ||
* This Source Code may also be made available under the following Secondary | ||
* Licenses when the conditions for such availability set forth in the | ||
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License, | ||
* version 2 with the GNU Classpath Exception, which is available at | ||
* https://www.gnu.org/software/classpath/license.html. | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 | ||
*/ | ||
|
||
package org.glassfish.main.jul.rotation; | ||
|
||
import java.io.File; | ||
import java.io.FileFilter; | ||
import java.io.FileInputStream; | ||
import java.io.FileOutputStream; | ||
import java.io.IOException; | ||
import java.lang.System.Logger; | ||
import java.util.Arrays; | ||
import java.util.Comparator; | ||
import java.util.zip.GZIPOutputStream; | ||
|
||
import org.glassfish.main.jul.tracing.GlassFishLoggingTracer; | ||
|
||
import static java.lang.System.Logger.Level.ERROR; | ||
import static java.lang.System.Logger.Level.INFO; | ||
|
||
/** | ||
* LogFileArchiver manages history of log files, compresses them into gz files, removes old files. | ||
*/ | ||
class LogFileArchiver { | ||
private static final Logger LOG = System.getLogger(LogFileArchiver.class.getName()); | ||
private static final String GZIP_EXTENSION = ".gz"; | ||
|
||
private final File mainLogFile; | ||
private final boolean compressOldLogFiles; | ||
private final int maxCountOfOldLogFiles; | ||
|
||
|
||
LogFileArchiver(File mainLogFile, boolean compressOldLogFiles, final int maxCountOfOldLogFiles) { | ||
this.mainLogFile = mainLogFile; | ||
this.compressOldLogFiles = compressOldLogFiles; | ||
this.maxCountOfOldLogFiles = maxCountOfOldLogFiles; | ||
} | ||
|
||
|
||
/** | ||
* @return file with a name ie. server.log_2024-01-13T11-45-37.gz | ||
*/ | ||
File getGzArchiveFile(final File rotatedFile) { | ||
return new File(rotatedFile.getParentFile(), rotatedFile.getName() + GZIP_EXTENSION); | ||
} | ||
|
||
|
||
/** | ||
* There is no need to block processing of new log records with this time consuming action, | ||
* so this starts a parallel thread. | ||
* However the cleanup is done in synchronized method to avoid collisions in a case when | ||
* there would be more parallel slow threads. | ||
* | ||
* @param archivedFile | ||
*/ | ||
void archive(File archivedFile) { | ||
final Runnable cleanup = () -> cleanUpHistoryLogFiles(archivedFile); | ||
new Thread(cleanup, "old-log-files-cleanup-" + mainLogFile.getName()).start(); | ||
} | ||
|
||
|
||
private synchronized void cleanUpHistoryLogFiles(final File rotatedFile) { | ||
if (this.compressOldLogFiles) { | ||
compressFile(rotatedFile); | ||
} | ||
deleteOldLogFiles(); | ||
} | ||
|
||
|
||
private void compressFile(final File rotatedFile) { | ||
final long start = System.currentTimeMillis(); | ||
final File outFile = getGzArchiveFile(rotatedFile); | ||
final boolean compressed = gzipFile(rotatedFile, outFile); | ||
if (!compressed) { | ||
logError("Could not compress log file: " + rotatedFile.getAbsolutePath()); | ||
return; | ||
} | ||
final long time = System.currentTimeMillis() - start; | ||
LOG.log(INFO, "File {0} of size {1} has been archived to file {2} of size {3} in {4} ms", | ||
rotatedFile, rotatedFile.length(), outFile, outFile.length(), time); | ||
final boolean deleted = rotatedFile.delete(); | ||
if (!deleted) { | ||
logError("Could not delete uncompressed log file: " + rotatedFile.getAbsolutePath()); | ||
} | ||
} | ||
|
||
|
||
private boolean gzipFile(final File inputFile, final File outputFile) { | ||
try ( | ||
FileInputStream fis = new FileInputStream(inputFile); | ||
FileOutputStream fos = new FileOutputStream(outputFile); | ||
GZIPOutputStream gzos = new GZIPOutputStream(fos) | ||
) { | ||
byte[] buffer = new byte[1024]; | ||
int len; | ||
while ((len = fis.read(buffer)) != -1) { | ||
gzos.write(buffer, 0, len); | ||
} | ||
gzos.finish(); | ||
return true; | ||
} catch (IOException e) { | ||
final String message = "Error gzipping log file " + inputFile; | ||
GlassFishLoggingTracer.error(getClass(), message, e); | ||
LOG.log(ERROR, message, e); | ||
return false; | ||
} | ||
} | ||
|
||
|
||
private void deleteOldLogFiles() { | ||
if (this.maxCountOfOldLogFiles == 0) { | ||
return; | ||
} | ||
|
||
final File dir = this.mainLogFile.getParentFile(); | ||
final String logFileName = this.mainLogFile.getName(); | ||
if (dir == null) { | ||
return; | ||
} | ||
final FileFilter filter = f -> f.isFile() && !f.getName().equals(logFileName) | ||
&& f.getName().startsWith(logFileName); | ||
Arrays.stream(dir.listFiles(filter)).sorted(Comparator.comparing(File::getName).reversed()) | ||
.skip(this.maxCountOfOldLogFiles).forEach(this::deleteFile); | ||
} | ||
|
||
|
||
private void deleteFile(final File file) { | ||
final boolean delFile = file.delete(); | ||
if (!delFile) { | ||
logError("Could not delete the log file: " + file); | ||
} | ||
} | ||
|
||
|
||
private void logError(final String message) { | ||
GlassFishLoggingTracer.error(getClass(), message); | ||
LOG.log(ERROR, message); | ||
} | ||
} |
Oops, something went wrong.