Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add linktolatest option for RollingFileWriter #150

Merged
merged 4 commits into from
Jun 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion tinylog-impl/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,12 @@
<groupId>org.tinylog</groupId>
<artifactId>tinylog-api</artifactId>
</dependency>
</dependencies>
<dependency>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-annotations</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>

<build>
<plugins>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
import org.tinylog.Level;
import org.tinylog.configuration.ServiceLoader;
import org.tinylog.core.LogEntry;
Expand All @@ -43,6 +47,7 @@ public final class RollingFileWriter extends AbstractFormatPatternWriter {
private final int backups;
private final boolean buffered;
private final boolean writingThread;
private final DynamicPath linkToLatest;
private final Charset charset;

private ByteArrayWriter writer;
Expand Down Expand Up @@ -72,8 +77,9 @@ public RollingFileWriter(final Map<String, String> properties) throws FileNotFou
path = new DynamicPath(getFileName(properties));
policies = createPolicies(properties.get("policies"));
backups = properties.containsKey("backups") ? Integer.parseInt(properties.get("backups")) : -1;
linkToLatest = properties.containsKey("latest") ? new DynamicPath(properties.get("latest")) : null;

List<File> files = path.getAllFiles();
List<File> files = filterOutSymlinks(path.getAllFiles());

String fileName;
boolean append;
Expand All @@ -96,7 +102,43 @@ public RollingFileWriter(final Map<String, String> properties) throws FileNotFou
charset = getCharset(properties);
buffered = Boolean.parseBoolean(properties.get("buffered"));
writingThread = Boolean.parseBoolean(properties.get("writingthread"));
writer = createByteArrayWriter(fileName, append, buffered, false, false);
writer = createByteArrayWriterAndLinkLatest(fileName, append, buffered, false, false);
}

@IgnoreJRERequirement
private static List<File> filterOutSymlinks(final List<File> files) {
pabl0rg marked this conversation as resolved.
Show resolved Hide resolved
if (!RuntimeProvider.isAndroid()) {
List<File> symlinks = new ArrayList<File>();
for (File file : files) {
if (Files.isSymbolicLink(file.toPath())) {
symlinks.add(file);
}
}
files.removeAll(symlinks);
}
return files;
}

@IgnoreJRERequirement
private ByteArrayWriter createByteArrayWriterAndLinkLatest(final String fileName, final boolean append, final boolean buffered,
pabl0rg marked this conversation as resolved.
Show resolved Hide resolved
final boolean threadSafe, final boolean shared) throws FileNotFoundException {
ByteArrayWriter writer = createByteArrayWriter(fileName, append, buffered, threadSafe, shared);
if (linkToLatest != null) {
File logFile = new File(fileName);
File linkFile = new File(linkToLatest.resolve());
if (!RuntimeProvider.isAndroid()) {
pabl0rg marked this conversation as resolved.
Show resolved Hide resolved
try {
Path linkPath = linkFile.toPath();
Files.delete(linkPath);
Files.createSymbolicLink(linkPath, logFile.toPath());
} catch (IOException exception) {
InternalLogger.log(Level.ERROR, exception, "Failed to create symlink '" + linkFile + "'");
}
} else {
InternalLogger.log(Level.WARN, "Cannot create symlink to latest log segment on Android");
}
}
return writer;
}

@Override
Expand Down Expand Up @@ -145,9 +187,11 @@ private void internalWrite(final byte[] data) throws IOException {
if (!canBeContinued(data, policies)) {
writer.close();

List<File> existingFiles = filterOutSymlinks(path.getAllFiles());
deleteBackups(existingFiles, backups);

String fileName = path.resolve();
deleteBackups(path.getAllFiles(), backups);
writer = createByteArrayWriter(fileName, false, buffered, false, false);
writer = createByteArrayWriterAndLinkLatest(fileName, false, buffered, false, false);

for (Policy policy : policies) {
policy.reset();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -481,4 +481,53 @@ public void isRegistered() throws IOException {
assertThat(writer).isInstanceOf(RollingFileWriter.class);
}

/**
* Verifies that obsolete backup files will be deleted when rolling log file.
*
* @throws IOException
* Failed access to temporary folder or files
*/
@Test
public void updateSymlinkToLatestAtRollOver() throws IOException {
File file1 = folder.newFile("0");
File file2 = folder.newFile("1");
File file3 = folder.newFile("2");
File file4 = folder.newFile("3");
File file5 = folder.newFile("4");
File linkToLatest = folder.newFile("latest");

file1.setLastModified(0);
file2.setLastModified(1000);
file3.setLastModified(2000);
file4.setLastModified(3000);
file5.delete();

Map<String, String> properties = new HashMap<>();
properties.put("file", new File(folder.getRoot(), "{count}").getAbsolutePath());
properties.put("latest", linkToLatest.getAbsolutePath());
properties.put("format", "{message}");
properties.put("policies", "size: 10");
properties.put("backups", "3");

RollingFileWriter writer = new RollingFileWriter(properties);

assertThat(file1).exists();
assertThat(file2).exists();
assertThat(file3).exists();
assertThat(file4).exists();
assertThat(file5).doesNotExist();

writer.write(LogEntryBuilder.empty().message("First").create());
writer.write(LogEntryBuilder.empty().message("Second").create());

assertThat(file1).doesNotExist();
assertThat(file2).exists();
assertThat(file3).exists();
assertThat(file4).hasContent("First" + NEW_LINE);
assertThat(file5).hasContent("Second" + NEW_LINE);

assertThat(linkToLatest.getCanonicalPath()).isEqualTo(file5.getCanonicalPath());

writer.close();
}
}