mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-04-19 17:16:53 -04:00
refactored BufferedLog
* rename to EventualLogger * adhere to slf4j API * ensure single instance
This commit is contained in:
@@ -2,7 +2,6 @@ package org.cryptomator.launcher;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -37,7 +36,7 @@ import java.util.Set;
|
||||
*/
|
||||
class AdminPropertiesSetter {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AdminPropertiesSetter.class);
|
||||
private static final Logger LOG = EventualLogger.getInstance();
|
||||
|
||||
private static final String LINUX_DIR = "/etc/cryptomator";
|
||||
private static final String MAC_DIR = "/Library/Application Support/Cryptomator";
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package org.cryptomator.launcher;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class BufferedLog {
|
||||
|
||||
private final static List<Entry> logMessages = new ArrayList<>();
|
||||
|
||||
private BufferedLog() {
|
||||
}
|
||||
|
||||
record Entry(String className, String message, List<Object> messageInput) {}
|
||||
|
||||
synchronized static void log(String className, String message, List<Object> messageInput) {
|
||||
logMessages.add(new Entry(className, message, messageInput));
|
||||
}
|
||||
|
||||
synchronized static void flushTo(Logger log) {
|
||||
logMessages.forEach(e -> {
|
||||
var message = "PRE LOG INIT Event in %s: %s".formatted(e.className, e.message);
|
||||
log.info(message, e.messageInput.toArray());
|
||||
});
|
||||
logMessages.clear();
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ public class Cryptomator {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
BufferedLog.flushTo(LOG);
|
||||
EventualLogger.getInstance().drainTo(LOG);
|
||||
var printVersion = Optional.ofNullable(args) //
|
||||
.stream() //Streams either one element (the args-array) or zero elements
|
||||
.flatMap(Arrays::stream) //
|
||||
|
||||
115
src/main/java/org/cryptomator/launcher/EventualLogger.java
Normal file
115
src/main/java/org/cryptomator/launcher/EventualLogger.java
Normal file
@@ -0,0 +1,115 @@
|
||||
package org.cryptomator.launcher;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.Marker;
|
||||
import org.slf4j.event.DefaultLoggingEvent;
|
||||
import org.slf4j.event.Level;
|
||||
import org.slf4j.event.LoggingEvent;
|
||||
import org.slf4j.helpers.AbstractLogger;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Queue;
|
||||
|
||||
class EventualLogger extends AbstractLogger {
|
||||
|
||||
static EventualLogger getInstance() {
|
||||
return Wrapped.INSTANCE.get();
|
||||
}
|
||||
|
||||
|
||||
private final Queue<LoggingEvent> bufferedEvents = new ArrayDeque<>();
|
||||
|
||||
private EventualLogger() {
|
||||
}
|
||||
|
||||
synchronized void drainTo(Logger gutter) {
|
||||
for (var event : bufferedEvents) {
|
||||
gutter.atLevel(event.getLevel()).log(event.getMessage(), event.getArgumentArray());
|
||||
}
|
||||
bufferedEvents.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void handleNormalizedLoggingCall(Level level, Marker marker, String messagePattern, Object[] arguments, Throwable throwable) {
|
||||
var event = new DefaultLoggingEvent(level, this);
|
||||
if (marker != null) {
|
||||
event.addMarker(marker);
|
||||
}
|
||||
event.setMessage(messagePattern);
|
||||
for (var arg : arguments) {
|
||||
event.addArgument(arg);
|
||||
}
|
||||
bufferedEvents.add(event);
|
||||
}
|
||||
|
||||
//Unclear, unused and undocumented method of slf4j, see also https://github.com/qos-ch/slf4j/discussions/348
|
||||
@Override
|
||||
protected String getFullyQualifiedCallerName() {
|
||||
return getClass().getCanonicalName();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isTraceEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTraceEnabled(Marker marker) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebugEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebugEnabled(Marker marker) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInfoEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInfoEnabled(Marker marker) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWarnEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWarnEnabled(Marker marker) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isErrorEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isErrorEnabled(Marker marker) {
|
||||
return true;
|
||||
}
|
||||
|
||||
private enum Wrapped {
|
||||
INSTANCE;
|
||||
|
||||
EventualLogger actualInstance;
|
||||
|
||||
Wrapped() {
|
||||
actualInstance = new EventualLogger();
|
||||
}
|
||||
|
||||
public EventualLogger get() {
|
||||
return actualInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,98 +1,59 @@
|
||||
package org.cryptomator.launcher;
|
||||
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.Matchers.anEmptyMap;
|
||||
import static org.hamcrest.Matchers.hasEntry;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
|
||||
public class AdminPropertiesSetterTest {
|
||||
|
||||
private static final Map<String, Object> NO_STRING_CONFIG = Map.of("list", List.of("a", "b", "c"), //
|
||||
"map", Map.of("a", 1, "b", 2));
|
||||
|
||||
private static final Map<String, Object> CONFIG = Map.of("kack", "dudel", //
|
||||
"list", List.of("a", "b", "c"), //
|
||||
"map", Map.of("a", 1, "b", 2));
|
||||
private static final String PROPS = """
|
||||
fruit=banana
|
||||
vegetable:kärrot
|
||||
method=scan寧""";
|
||||
|
||||
@Test
|
||||
@DisplayName("Loading valid JSON")
|
||||
void loadValidJson(@TempDir Path path) throws IOException {
|
||||
try (var adminPropSetterMock = Mockito.mockStatic(AdminPropertiesSetter.class)) {
|
||||
adminPropSetterMock.when(() -> AdminPropertiesSetter.log(anyString(), any())).thenAnswer(Answers.RETURNS_DEFAULTS);
|
||||
adminPropSetterMock.when(() -> AdminPropertiesSetter.loadAdminProperties(any())).thenCallRealMethod();
|
||||
var configPath = path.resolve("config.json");
|
||||
setupValidJson(configPath);
|
||||
@DisplayName("UTF-8 is supported")
|
||||
void loadUTF8Properties(@TempDir Path path) throws IOException {
|
||||
var config = path.resolve("config.properties");
|
||||
setupValidProperties(config);
|
||||
|
||||
var result = AdminPropertiesSetter.loadAdminProperties(configPath);
|
||||
Assertions.assertAll(CONFIG.entrySet().stream().map((e) -> //
|
||||
() -> MatcherAssert.assertThat(result, hasEntry(e.getKey(), e.getValue()))));
|
||||
}
|
||||
var properties = AdminPropertiesSetter.loadAdminProperties(config);
|
||||
Assertions.assertAll(List.of( //
|
||||
() -> MatcherAssert.assertThat(properties, hasEntry("fruit", "banana")), //
|
||||
() -> MatcherAssert.assertThat(properties, hasEntry("vegetable", "kärrot")), //
|
||||
() -> MatcherAssert.assertThat(properties, hasEntry("method", "scan寧"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Loading not existing file")
|
||||
void loadNotExistingFile(@TempDir Path path) {
|
||||
try (var adminPropSetterMock = Mockito.mockStatic(AdminPropertiesSetter.class)) {
|
||||
adminPropSetterMock.when(() -> AdminPropertiesSetter.log(anyString(), any())).thenAnswer(Answers.RETURNS_DEFAULTS);
|
||||
adminPropSetterMock.when(() -> AdminPropertiesSetter.loadAdminProperties(any())).thenCallRealMethod();
|
||||
var configPath = path.resolve("config.json");
|
||||
|
||||
var result = AdminPropertiesSetter.loadAdminProperties(configPath);
|
||||
MatcherAssert.assertThat(result, anEmptyMap());
|
||||
adminPropSetterMock.verify(() -> AdminPropertiesSetter.log(anyString(), any()));
|
||||
}
|
||||
var config = path.resolve("config.properties");
|
||||
var properties = AdminPropertiesSetter.loadAdminProperties(config);
|
||||
MatcherAssert.assertThat(properties, anEmptyMap());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Loading empty file")
|
||||
@DisplayName("Loading invalid properties file")
|
||||
void loadEmptyFile(@TempDir Path path) throws IOException {
|
||||
try (var adminPropSetterMock = Mockito.mockStatic(AdminPropertiesSetter.class)) {
|
||||
adminPropSetterMock.when(() -> AdminPropertiesSetter.log(anyString(), any())).thenAnswer(Answers.RETURNS_DEFAULTS);
|
||||
adminPropSetterMock.when(() -> AdminPropertiesSetter.loadAdminProperties(any())).thenCallRealMethod();
|
||||
var configPath = path.resolve("config.json");
|
||||
Files.createFile(configPath);
|
||||
|
||||
var result = AdminPropertiesSetter.loadAdminProperties(configPath);
|
||||
MatcherAssert.assertThat(result, anEmptyMap());
|
||||
adminPropSetterMock.verify(() -> AdminPropertiesSetter.log(anyString(), any()));
|
||||
}
|
||||
}
|
||||
|
||||
void setupValidJson(Path p) throws IOException {
|
||||
var json = JsonMapper.builder().build();
|
||||
void setupValidProperties(Path p) throws IOException {
|
||||
try (var out = Files.newOutputStream(p, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) {
|
||||
json.writerWithDefaultPrettyPrinter().writeValue(out, CONFIG);
|
||||
var bytes = PROPS.getBytes(StandardCharsets.UTF_8);
|
||||
out.write(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Keys with non-String values are ignored")
|
||||
void ignoreValues(@TempDir Path path) {
|
||||
try (var adminPropSetterMock = Mockito.mockStatic(AdminPropertiesSetter.class)) {
|
||||
adminPropSetterMock.when(() -> AdminPropertiesSetter.log(anyString(), any())).thenAnswer(Answers.RETURNS_DEFAULTS);
|
||||
adminPropSetterMock.when(() -> AdminPropertiesSetter.loadAdminProperties(any())).thenReturn(NO_STRING_CONFIG);
|
||||
adminPropSetterMock.when(AdminPropertiesSetter::adjustSystemProperties).thenCallRealMethod();
|
||||
|
||||
AdminPropertiesSetter.adjustSystemProperties();
|
||||
adminPropSetterMock.verify(() -> AdminPropertiesSetter.log(anyString(), any()), Mockito.times(NO_STRING_CONFIG.size()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user