Skip to content

Commit

Permalink
Allow to dynamically change log filename
Browse files Browse the repository at this point in the history
  • Loading branch information
simon04 committed Dec 21, 2021
1 parent 57a450b commit 4c3acd3
Show file tree
Hide file tree
Showing 9 changed files with 357 additions and 0 deletions.
6 changes: 6 additions & 0 deletions configuration/spotbugs-filters.xml
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,10 @@
<!-- Performance of inner anonymous class is the purpose of a benchmark -->
<Bug pattern="SIC_INNER_SHOULD_BE_STATIC_ANON" />
</Match>
<Match>
<!-- Dynamic Name Policy -->
<Class name="org.tinylog.policies.DynamicNamePolicy" />
<!-- Allow writing to static field org.tinylog.policies.DynamicNamePolicy.reset from instance method org.tinylog.policies.DynamicNamePolicy.reset -->
<Bug pattern="ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD" />
</Match>
</FindBugsFilter>
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright 2021 Martin Winandy
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/

package org.tinylog.path;

import org.tinylog.policies.DynamicNamePolicy;
import org.tinylog.policies.Policy;
import org.tinylog.runtime.Timestamp;

/**
* Path segment that represents the log filename from {@link DynamicNamePolicy}.
*/
public class DynamicNameSegment implements Segment {

private static String dynamicName;

DynamicNameSegment(final String defaultValue) {
setDynamicName(defaultValue);
}

/**
* Returns the current dynamic name.
*
* @return the current dynamic name
*/
public static String getDynamicName() {
return dynamicName;
}

/**
* Sets a new dynamic name.
* When used together with {@link DynamicNamePolicy} and the dynamic name differs from the current one,
* a {@linkplain Policy#reset() reset} is triggered.
*
* @param newDynamicName the dynamic name to set
*/
public static void setDynamicName(final String newDynamicName) {
if (dynamicName != null && dynamicName.equals(newDynamicName)) {
return;
}
dynamicName = newDynamicName;
DynamicNamePolicy.setReset();
}

@Override
public String getStaticText() {
return getDynamicName();
}

@Override
public boolean validateToken(final String token) {
return dynamicName != null && dynamicName.equals(token);
}

@Override
public String createToken(final String prefix, final Timestamp timestamp) {
return getDynamicName();
}
}
3 changes: 3 additions & 0 deletions tinylog-impl/src/main/java/org/tinylog/path/DynamicPath.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
public final class DynamicPath {

private static final String DEFAULT_DATE_FORMAT_PATTERN = "yyyy-MM-dd_HH-mm-ss";
private static final String DEFAULT_LOG_FILENAME = "log";

private final List<Segment> segments;
private final File folder;
Expand Down Expand Up @@ -280,6 +281,8 @@ private static Segment parseSegment(final String path, final String token) {
return new CountSegment();
} else if ("pid".equals(name) && parameter == null) {
return new ProcessIdSegment();
} else if ("dynamic name".equals(name)) {
return new DynamicNameSegment(parameter == null ? DEFAULT_LOG_FILENAME : parameter);
} else {
throw new IllegalArgumentException("Invalid token '" + token + "' in '" + path + "'");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2021 Martin Winandy
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/

package org.tinylog.policies;

import org.tinylog.Level;
import org.tinylog.path.DynamicNameSegment;
import org.tinylog.provider.InternalLogger;

/**
* Policy for triggering a manual rollover by calling {@link #setReset()}.
* Might be used together with {@link DynamicNameSegment}.
*/
public final class DynamicNamePolicy implements Policy {

private static boolean reset;

/**
*
*/
public DynamicNamePolicy() {
this(null);
}

/**
* @param argument Should be always {@code null} as dynamic name policy does not support arguments
*/
public DynamicNamePolicy(final String argument) {
if (argument != null) {
InternalLogger.log(Level.WARN, "Dynamic name policy does not support arguments");
}
}

@Override
public boolean continueExistingFile(final String path) {
return !reset;
}

@Override
public boolean continueCurrentFile(final byte[] entry) {
return !reset;
}

@Override
public void reset() {
reset = false;
}

/**
* Sets the reset flag to trigger a {@linkplain #reset() reset}.
*/
public static void setReset() {
reset = true;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
org.tinylog.policies.DailyPolicy
org.tinylog.policies.DynamicNamePolicy
org.tinylog.policies.MonthlyPolicy
org.tinylog.policies.StartupPolicy
org.tinylog.policies.SizePolicy
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 2021 Martin Winandy
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/

package org.tinylog.path;

import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Tests for {@link DynamicNameSegment}.
*/
public final class DynamicNameSegmentTest {

/**
* Verifies that the passed initial dynamic name text as well as the dynamic name set via
* {@link DynamicNameSegment#setDynamicName(String)} will be returned as static text.
*/
@Test
public void doesHaveStaticText() {
DynamicNameSegment segment = new DynamicNameSegment("test");
assertThat(segment.getStaticText()).isEqualTo("test");
DynamicNameSegment.setDynamicName("foo");
assertThat(segment.getStaticText()).isEqualTo("foo");
DynamicNameSegment.setDynamicName("bar");
assertThat(segment.getStaticText()).isEqualTo("bar");
}

/**
* Verifies that the passed initial dynamic name text as well as the dynamic name set via
* {@link DynamicNameSegment#setDynamicName(String)} will be returned as generated token.
*/
@Test
public void createToken() {
DynamicNameSegment segment = new DynamicNameSegment("test");
assertThat(segment.createToken(null, null)).isEqualTo("test");
DynamicNameSegment.setDynamicName("foo");
assertThat(segment.getStaticText()).isEqualTo("foo");
DynamicNameSegment.setDynamicName("bar");
assertThat(segment.getStaticText()).isEqualTo("bar");
}

/**
* Verifies that the dynamic name text will be accepted as valid token.
*/
@Test
public void validateValidToken() {
DynamicNameSegment segment = new DynamicNameSegment("test");
assertThat(segment.validateToken("test")).isTrue();
assertThat(segment.validateToken("foo")).isFalse();
DynamicNameSegment.setDynamicName("foo");
assertThat(segment.validateToken("test")).isFalse();
assertThat(segment.validateToken("foo")).isTrue();
}

}
26 changes: 26 additions & 0 deletions tinylog-impl/src/test/java/org/tinylog/path/DynamicPathTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,32 @@ public void processIdToken() {
assertThat(path.resolve()).isEqualTo(folder.getRoot() + File.separator + RuntimeProvider.getProcessId() + ".log");
}

/**
* Verifies that a path with a log filename pattern can be resolved.
*/
@Test
public void dynamicName() {
String pattern = new File(folder.getRoot(), "{dynamic name}.log").getAbsolutePath();
DynamicPath path = new DynamicPath(pattern);
assertThat(path.resolve()).isEqualTo(folder.getRoot() + File.separator + "log.log");
DynamicNameSegment.setDynamicName("foo");
assertThat(path.resolve()).isEqualTo(folder.getRoot() + File.separator + "foo.log");
DynamicNameSegment.setDynamicName("bar");
assertThat(path.resolve()).isEqualTo(folder.getRoot() + File.separator + "bar.log");
}

/**
* Verifies that a path with a log filename pattern including initial value can be resolved.
*/
@Test
public void dynamicNameParameter() {
String pattern = new File(folder.getRoot(), "{dynamic name: foobar}.log").getAbsolutePath();
DynamicPath path = new DynamicPath(pattern);
assertThat(path.resolve()).isEqualTo(folder.getRoot() + File.separator + "foobar.log");
DynamicNameSegment.setDynamicName("baz");
assertThat(path.resolve()).isEqualTo(folder.getRoot() + File.separator + "baz.log");
}

/**
* Verifies that a path with an unknown pattern will be rejected.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright 2021 Martin Winandy
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/

package org.tinylog.policies;

import java.io.IOException;

import org.junit.Rule;
import org.junit.Test;
import org.tinylog.configuration.ServiceLoader;
import org.tinylog.rules.SystemStreamCollector;
import org.tinylog.util.FileSystem;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Tests for {@link DynamicNamePolicy}.
*/
public final class DynamicNamePolicyTest {

/**
* Redirects and collects system output streams.
*/
@Rule
public final SystemStreamCollector systemStream = new SystemStreamCollector(true);

/**
* Verifies that an existing log file will be never continued.
*
* @throws IOException
* Failed creating temporary file
*/
@Test
public void discontinueExistingFile() throws IOException {
String file = FileSystem.createTemporaryFile();
final DynamicNamePolicy policy = new DynamicNamePolicy(null);
assertThat(policy.continueExistingFile(file)).isTrue();
DynamicNamePolicy.setReset();
assertThat(policy.continueExistingFile(file)).isFalse();
policy.reset();
assertThat(policy.continueExistingFile(file)).isTrue();
}

/**
* Verifies that the current log file will be always continued.
*/
@Test
public void continueCurrentFile() {
final DynamicNamePolicy policy = new DynamicNamePolicy(null);
assertThat(policy.continueCurrentFile(new byte[0])).isTrue();
DynamicNamePolicy.setReset();
assertThat(policy.continueCurrentFile(new byte[0])).isFalse();
policy.reset();
assertThat(policy.continueCurrentFile(new byte[0])).isTrue();
}

/**
* Verifies that the reset() method can be executed without throwing any exception.
*/
@Test
public void resetIsCallable() {
new DynamicNamePolicy(null).reset();
}

/**
* Verifies that a warning will be output, if an argument is passed.
*/
@Test
public void warnIfArgumentIsSet() {
new DynamicNamePolicy("test");
assertThat(systemStream.consumeErrorOutput()).containsOnlyOnce("WARN").containsOnlyOnce("argument");
}

/**
* Verifies that policy is registered as service under the name "startup".
*/
@Test
public void isRegistered() {
Policy policy = new ServiceLoader<>(Policy.class, String.class).create("dynamic name", (String) null);
assertThat(policy).isInstanceOf(DynamicNamePolicy.class);
}

}
Loading

0 comments on commit 4c3acd3

Please sign in to comment.