Skip to content

Commit

Permalink
Save uncaught exceptions based on logviewer RC
Browse files Browse the repository at this point in the history
  • Loading branch information
tibagni committed May 1, 2024
1 parent 287da2a commit 4b8bc61
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 24 deletions.
25 changes: 24 additions & 1 deletion src/main/java/com/tibagni/logviewer/LogViewerApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,23 @@
import com.formdev.flatlaf.util.UIScale;
import com.tibagni.logviewer.logger.Logger;
import com.tibagni.logviewer.preferences.LogViewerPreferences;
import com.tibagni.logviewer.rc.CrashReportConfig;
import com.tibagni.logviewer.rc.LogLevelConfig;
import com.tibagni.logviewer.rc.RuntimeConfiguration;
import com.tibagni.logviewer.rc.UIScaleConfig;
import com.tibagni.logviewer.theme.LogViewerThemeManager;
import com.tibagni.logviewer.updates.ReleaseInfo;
import com.tibagni.logviewer.updates.UpdateAvailableDialog;
import com.tibagni.logviewer.updates.UpdateManager;
import com.tibagni.logviewer.util.CommonUtils;
import com.tibagni.logviewer.util.StringUtils;
import com.tibagni.logviewer.util.scaling.UIScaleUtils;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import java.io.File;
import java.io.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
Expand All @@ -30,6 +34,13 @@ public static void main(String[] args) {
UIScaleUtils.initialize(RuntimeConfiguration.getConfig(RuntimeConfiguration.UI_SCALE, UIScaleConfig.class));
Logger.initialize(RuntimeConfiguration.getConfig(RuntimeConfiguration.LOG_LEVEL, LogLevelConfig.class));

CrashReportConfig crashReportConfig = RuntimeConfiguration.getConfig(RuntimeConfiguration.CRASH_REPORT,
CrashReportConfig.class);
if (crashReportConfig != null && crashReportConfig.getConfigValue()) {
Logger.info("Enabling crash report...");
configureUncaughtExceptionHandler();
}

Set<File> initialLogFiles = Arrays
.stream(args)
.map(File::new)
Expand All @@ -40,6 +51,18 @@ public static void main(String[] args) {
application.start(initialLogFiles);
}

private static void configureUncaughtExceptionHandler() {
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
String fileName = "CRASH-logviewer_" + CommonUtils.calculateStackTraceHash(e) + ".txt";
Path filePath = Paths.get(System.getProperty("user.home"), fileName);
try (PrintWriter pw = new PrintWriter(new FileWriter(filePath.toFile()))) {
e.printStackTrace(pw);
} catch (IOException ex) {
ex.printStackTrace();
}
});
}

private void start(Set<File> initialLogFiles) {
startCheckingForUpdates();
initLookAndFeel();
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/tibagni/logviewer/rc/CrashReportConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.tibagni.logviewer.rc

class CrashReportConfig(configValue: String) : Config<Boolean> {
private val state: Boolean
init {
state = configValue.lowercase() == "on"
}

override fun getConfigValue() = state
}
25 changes: 14 additions & 11 deletions src/main/java/com/tibagni/logviewer/rc/RuntimeConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,17 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.stream.Stream;

public class RuntimeConfiguration {
private static final Path RC_FILE_PATH = Paths.get(System.getProperty("user.home"), ".logviewer");
private static RuntimeConfiguration instance;

private HashMap<String, Config> runtimeConfigs = new HashMap<>();
private final HashMap<String, Config<?>> runtimeConfigs = new HashMap<>();

public static final String UI_SCALE = "uiscale";
public static final String LOG_LEVEL = "loglevel";
public static final String CRASH_REPORT = "crashreport";

@NotNull
static RuntimeConfiguration initializeForTest() {
Expand All @@ -35,16 +37,16 @@ public static void initialize() {

public static <T> T getConfig(String configName, Class<T> type) {
if (instance.runtimeConfigs.containsKey(configName)) {
return (T) instance.runtimeConfigs.get(configName);
Object config = instance.runtimeConfigs.get(configName);
if (type.isInstance(config)) {
//noinspection unchecked
return (T) config;
}
}

return null;
}

public static Config getConfig(String configName) {
return getConfig(configName, Config.class);
}

// For test only
private RuntimeConfiguration() { }

Expand All @@ -54,10 +56,8 @@ private RuntimeConfiguration(Path rcFilePath) {
return;
}

try {
Files.lines(rcFilePath)
.filter(StringUtils::isNotEmpty)
.forEach(this::parseConfig);
try (Stream<String> lines = Files.lines(rcFilePath)) {
lines.filter(StringUtils::isNotEmpty).forEach(this::parseConfig);
} catch (IOException e) {
Logger.error("Could not read rc file", e);
}
Expand All @@ -74,14 +74,17 @@ void parseConfig(@NotNull String configLine) {

String configName = configParts[0].toLowerCase();
String configValue = configParts[1].toLowerCase();
Config config = null;
Config<?> config = null;
switch (configName) {
case UI_SCALE:
config = new UIScaleConfig(configValue);
break;
case LOG_LEVEL:
config = new LogLevelConfig(configValue);
break;
case CRASH_REPORT:
config = new CrashReportConfig(configValue);
break;
default:
Logger.error("Invalid config: " + configName);
break;
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/com/tibagni/logviewer/util/CommonUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.tibagni.logviewer.logger.Logger;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
Expand Down Expand Up @@ -31,4 +33,25 @@ <T extends Comparable<? super T>> List<T> asSortedList(Collection<T> c) {
Collections.sort(list);
return list;
}

public static String calculateStackTraceHash(Throwable throwable) {
StringBuilder stackTrace = new StringBuilder();
for (StackTraceElement element : throwable.getStackTrace()) {
stackTrace.append(element.toString()).append("\n");
}

byte[] bytes = stackTrace.toString().getBytes();

try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hashBytes = digest.digest(bytes);
StringBuilder hexString = new StringBuilder();
for (byte b : hashBytes) {
hexString.append(String.format("%02x", b));
}
return hexString.toString();
} catch (NoSuchAlgorithmException ignored) { }

return "empty";
}
}
40 changes: 28 additions & 12 deletions src/test/java/com/tibagni/logviewer/rc/RuntimeConfigurationTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,40 +17,55 @@ class RuntimeConfigurationTests {
fun testUiScaleConfig() {
testRcConfig.parseConfig("uiscale=2")

assertNotNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.UI_SCALE))
assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.LOG_LEVEL))
assertNotNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.UI_SCALE, UIScaleConfig::class.java))
assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.LOG_LEVEL, LogLevelConfig::class.java))
assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.CRASH_REPORT, CrashReportConfig::class.java))
}

@Test
fun testLogLevelConfig() {
testRcConfig.parseConfig("loglevel=v")

assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.UI_SCALE))
assertNotNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.LOG_LEVEL))
assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.UI_SCALE, UIScaleConfig::class.java))
assertNotNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.LOG_LEVEL, LogLevelConfig::class.java))
assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.CRASH_REPORT, CrashReportConfig::class.java))
}

@Test
fun testCrashReportConfig() {
testRcConfig.parseConfig("crashreport=on")

assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.UI_SCALE, UIScaleConfig::class.java))
assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.LOG_LEVEL, LogLevelConfig::class.java))
assertNotNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.CRASH_REPORT, CrashReportConfig::class.java))
}

@Test
fun testAllConfig() {
testRcConfig.parseConfig("loglevel=verbose")
testRcConfig.parseConfig("uiscale=5")
testRcConfig.parseConfig("crashreport=ON")

assertNotNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.UI_SCALE))
assertNotNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.LOG_LEVEL))
assertNotNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.UI_SCALE, UIScaleConfig::class.java))
assertNotNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.LOG_LEVEL, LogLevelConfig::class.java))
assertNotNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.CRASH_REPORT, CrashReportConfig::class.java))
}

@Test
fun testNoConfig() {
assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.UI_SCALE))
assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.LOG_LEVEL))
assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.UI_SCALE, UIScaleConfig::class.java))
assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.LOG_LEVEL, LogLevelConfig::class.java))
assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.CRASH_REPORT, CrashReportConfig::class.java))
}

@Test
fun testOnlyInvalidConfig() {
testRcConfig.parseConfig("invalidkey=dddddd")
testRcConfig.parseConfig("nonexistentconfig=eeee")

assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.UI_SCALE))
assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.LOG_LEVEL))
assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.UI_SCALE, UIScaleConfig::class.java))
assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.LOG_LEVEL, LogLevelConfig::class.java))
assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.CRASH_REPORT, CrashReportConfig::class.java))
}

@Test
Expand All @@ -60,7 +75,8 @@ class RuntimeConfigurationTests {
testRcConfig.parseConfig("")
testRcConfig.parseConfig(" ")

assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.UI_SCALE))
assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.LOG_LEVEL))
assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.UI_SCALE, UIScaleConfig::class.java))
assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.LOG_LEVEL, LogLevelConfig::class.java))
assertNull(RuntimeConfiguration.getConfig(RuntimeConfiguration.CRASH_REPORT, CrashReportConfig::class.java))
}
}

0 comments on commit 4b8bc61

Please sign in to comment.