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

Allow to dynamically change log filename using {dynamic name} segment #230

Merged
merged 1 commit into from
Dec 22, 2021
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
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,70 @@
/*
* 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 a dynamic text.
*/
public class DynamicNameSegment implements Segment {

private static String dynamicName;

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

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

/**
* Sets a new dynamic name.
*
* <p>When used together with {@link DynamicNamePolicy} and the dynamic name differs from the current one,
* a {@linkplain Policy#reset() reset} is triggered.</p>
*
* @param newDynamicName 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 "dynamic name".
*/
@Test
public void isRegistered() {
Policy policy = new ServiceLoader<>(Policy.class, String.class).create("dynamic name", (String) null);
assertThat(policy).isInstanceOf(DynamicNamePolicy.class);
}

}
Loading