diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..1dc8b04 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,119 @@ +name: Bug Report +description: Report a problem with KST4Contest / Ein Problem mit KST4Contest melden +title: "[BUG] " +labels: ["bug"] + +body: + - type: markdown + attributes: + value: | + **DE:** Bitte fülle alle Felder so vollständig wie möglich aus. Das hilft, den Fehler schneller zu finden. + **EN:** Please fill in all fields as completely as possible. This helps to find the bug faster. + + - type: textarea + id: description + attributes: + label: Description / Beschreibung + description: | + EN: A clear and concise description of the bug. + DE: Eine klare und präzise Beschreibung des Problems. + placeholder: "EN: When I click on ... / DE: Wenn ich auf ... klicke ..." + validations: + required: true + + - type: textarea + id: steps + attributes: + label: Steps to reproduce / Schritte zum Reproduzieren + description: | + EN: Step-by-step instructions to reproduce the bug. + DE: Schritt-für-Schritt-Anleitung, um den Fehler zu reproduzieren. + placeholder: | + 1. + 2. + 3. + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected behaviour / Erwartetes Verhalten + description: | + EN: What did you expect to happen? + DE: Was hätte passieren sollen? + validations: + required: true + + - type: textarea + id: actual + attributes: + label: Actual behaviour / Tatsächliches Verhalten + description: | + EN: What actually happened? + DE: Was ist stattdessen passiert? + validations: + required: true + + - type: textarea + id: logs + attributes: + label: Log file content / Inhalt der Logdatei + description: | + **EN:** Please paste the content of the error log file here. It is written automatically and contains error messages only — no personal data. + **DE:** Bitte füge hier den Inhalt der Fehler-Logdatei ein. Sie wird automatisch geschrieben und enthält nur Fehlermeldungen — keine persönlichen Daten. + + | OS | Path / Pfad | + |----|-------------| + | Linux / macOS | `~/.praktiKST/kst4contest-errors.log` | + | Windows | `C:\Users\\.praktiKST\kst4contest-errors.log` | + placeholder: Paste log content here / Loginhalt hier einfügen + render: text + validations: + required: true + + - type: input + id: version + attributes: + label: KST4Contest version / Version + description: EN: Found in the title bar or About dialog. / DE: Steht in der Titelleiste oder im About-Dialog. + placeholder: "e.g. 1.41.0" + validations: + required: true + + - type: input + id: java + attributes: + label: Java version / Java-Version + description: "`java -version`" + placeholder: "e.g. 17.0.9" + validations: + required: true + + - type: dropdown + id: os + attributes: + label: Operating system / Betriebssystem + options: + - Linux + - Windows + - macOS + validations: + required: true + + - type: input + id: logging-software + attributes: + label: Logging software / Logprogramm + description: "EN: If applicable. / DE: Falls relevant." + placeholder: "e.g. UCXLog, N1MM+, WinTest, QARTest, DXLog.net" + + - type: checkboxes + id: checklist + attributes: + label: Checklist / Checkliste + options: + - label: I have attached the log file / Ich habe die Logdatei angehängt + required: true + - label: I have checked that this issue has not been reported before / Ich habe geprüft, dass dieses Problem noch nicht gemeldet wurde + required: true diff --git a/github_docs/de-Home.md b/github_docs/de-Home.md index a383643..d7c6fd0 100644 --- a/github_docs/de-Home.md +++ b/github_docs/de-Home.md @@ -44,6 +44,17 @@ Der ON4KST-Chat ist der De-facto-Standard für Skeds auf den 144-MHz-und-höher- - **GitHub**: https://github.com/praktimarc/kst4contest - **Download**: https://github.com/praktimarc/kst4contest/releases/latest +### Fehler melden (Issue erstellen) + +Beim Melden eines Fehlers bitte **immer die Logdatei anhängen**. KST4Contest schreibt automatisch eine Fehler-Logdatei (nur Fehlermeldungen, keine persönlichen Daten): + +| Betriebssystem | Pfad zur Logdatei | +|---|---| +| Linux / macOS | `~/.praktiKST/kst4contest-errors.log` | +| Windows | `C:\Users\\.praktiKST\kst4contest-errors.log` | + +Beim Erstellen eines Issues auf GitHub steht eine Vorlage bereit, die alle wichtigen Felder abfragt. + --- ## Danksagungen diff --git a/github_docs/en-Home.md b/github_docs/en-Home.md index e1c908c..f264797 100644 --- a/github_docs/en-Home.md +++ b/github_docs/en-Home.md @@ -44,6 +44,17 @@ The ON4KST Chat is the de-facto standard for skeds on the 144 MHz and higher ban - **GitHub**: https://github.com/praktimarc/kst4contest - **Download**: https://github.com/praktimarc/kst4contest/releases/latest +### Reporting a bug (creating an issue) + +When reporting a bug, please **always attach the log file**. KST4Contest automatically writes an error log (error messages only, no personal data): + +| Operating system | Log file location | +|---|---| +| Linux / macOS | `~/.praktiKST/kst4contest-errors.log` | +| Windows | `C:\Users\\.praktiKST\kst4contest-errors.log` | + +When opening an issue on GitHub, a template is provided that guides you through all relevant fields. + --- ## Acknowledgements diff --git a/src/main/java/kst4contest/controller/AirScoutPeriodicalAPReflectionInquirerTask.java b/src/main/java/kst4contest/controller/AirScoutPeriodicalAPReflectionInquirerTask.java index 64b1d58..00fa248 100644 --- a/src/main/java/kst4contest/controller/AirScoutPeriodicalAPReflectionInquirerTask.java +++ b/src/main/java/kst4contest/controller/AirScoutPeriodicalAPReflectionInquirerTask.java @@ -9,6 +9,8 @@ import java.net.NoRouteToHostException; import java.net.SocketException; import java.net.UnknownHostException; import java.util.TimerTask; +import java.util.logging.Level; +import java.util.logging.Logger; import javafx.collections.ObservableList; import kst4contest.locatorUtils.Location; @@ -17,6 +19,7 @@ import kst4contest.model.ChatMember; public class AirScoutPeriodicalAPReflectionInquirerTask extends TimerTask { + private static final Logger LOGGER = Logger.getLogger(AirScoutPeriodicalAPReflectionInquirerTask.class.getName()); private ChatController client; public AirScoutPeriodicalAPReflectionInquirerTask(ChatController client) { @@ -55,7 +58,7 @@ public class AirScoutPeriodicalAPReflectionInquirerTask extends TimerTask { ownCallSign = this.client.getChatPreferences().getStn_loginCallSign(); } } catch (Exception e) { - System.out.println("[ASPERIODICAL, Error]: " + e.getMessage()); + LOGGER.log(Level.SEVERE, "[ASPERIODICAL] Error parsing callsign", e); } String myCallAndMyLocString = ownCallSign + "," + this.client.getChatPreferences().getStn_loginLocatorMainCat(); //bugfix, Airscout do not process 9A1W-2 but 9A1W like formatted calls @@ -105,13 +108,13 @@ public class AirScoutPeriodicalAPReflectionInquirerTask extends TimerTask { dsocket.send(packet); dsocket.close(); } catch (UnknownHostException e1) { - e1.printStackTrace(); + LOGGER.log(Level.SEVERE, "[ASPERIODICAL] Unknown host", e1); } catch (NoRouteToHostException e) { - e.printStackTrace(); + LOGGER.log(Level.SEVERE, "[ASPERIODICAL] No route to host", e); } catch (SocketException e) { - e.printStackTrace(); + LOGGER.log(Level.SEVERE, "[ASPERIODICAL] Socket error", e); } catch (IOException e) { - e.printStackTrace(); + LOGGER.log(Level.SEVERE, "[ASPERIODICAL] IO error sending query", e); } // System.out.println("[ASUDPTask, info:] sent query " + queryStringToAirScout); @@ -136,9 +139,9 @@ public class AirScoutPeriodicalAPReflectionInquirerTask extends TimerTask { dsocket.send(packet); dsocket.close(); } catch (IOException e) { - e.printStackTrace(); + LOGGER.log(Level.SEVERE, "[ASPERIODICAL] IO error sending watchlist", e); } - + // System.out.println("[ASUDPTask, info:] set watchlist: " + asWatchListStringSuffix); diff --git a/src/main/java/kst4contest/controller/DXClusterThreadPooledServer.java b/src/main/java/kst4contest/controller/DXClusterThreadPooledServer.java index 7b60164..d43e89b 100644 --- a/src/main/java/kst4contest/controller/DXClusterThreadPooledServer.java +++ b/src/main/java/kst4contest/controller/DXClusterThreadPooledServer.java @@ -10,9 +10,12 @@ import java.net.Socket; import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.logging.Level; +import java.util.logging.Logger; public class DXClusterThreadPooledServer implements Runnable{ + private static final Logger LOGGER = Logger.getLogger(DXClusterThreadPooledServer.class.getName()); private List clientSockets = Collections.synchronizedList(new ArrayList<>()); //list of all connected clients private ThreadStatusCallback callBackToController; @@ -111,8 +114,7 @@ public class DXClusterThreadPooledServer implements Runnable{ System.out.println("-------------> ORIGINALEE VAL: " + aChatMember.getFrequency().getValue()); System.out.println("-------------> NORMALIZED VAL: " + Utils4KST.normalizeFrequencyString(aChatMember.getFrequency().getValue(), chatController.getChatPreferences().getNotify_optionalFrequencyPrefix()) + " "); } catch (Exception e) { - System.out.println("DXCThPooledServer: Error accessing value in chatmember object: " + e.getMessage()); -// e.printStackTrace(); + LOGGER.log(Level.SEVERE, "DXCThPooledServer: Error accessing value in chatmember object", e); } for (Socket socket : clientSockets) { @@ -146,8 +148,7 @@ public class DXClusterThreadPooledServer implements Runnable{ callBackToController.onThreadStatus(ThreadNickName,threadStateMessage); } catch (IOException e) { - e.printStackTrace(); - System.out.println("[DXClusterSrvr, Error:] broadcasting DXC-message to clients went wrong!"); + LOGGER.log(Level.SEVERE, "[DXClusterSrvr] broadcasting DXC-message to clients went wrong", e); return false; } } @@ -159,6 +160,7 @@ public class DXClusterThreadPooledServer implements Runnable{ class DXClusterServerWorkerRunnable implements Runnable{ + private static final Logger LOGGER = Logger.getLogger(DXClusterServerWorkerRunnable.class.getName()); protected Socket clientSocket = null; protected String serverText = null; private ChatController client = null; @@ -197,14 +199,13 @@ class DXClusterServerWorkerRunnable implements Runnable{ output.write(("\r\n").getBytes()); } catch (IOException e) { - e.printStackTrace(); - System.out.println("[DXClusterSrvr, Error:] broadcasting DXC-message to clients went wrong!"); + LOGGER.log(Level.SEVERE, "[DXClusterSrvr] keep-alive broadcast to client failed", e); dXCkeepAliveTimer.purge(); try { socket.close(); } catch (IOException ex) { - ex.printStackTrace(); + LOGGER.log(Level.SEVERE, "[DXClusterSrvr] error closing client socket", ex); } finally { this.cancel(); @@ -224,7 +225,7 @@ class DXClusterServerWorkerRunnable implements Runnable{ System.out.println("[DXClusterThreadPooledServer, Info:] New cluster client connected! "); //TODO: maybe integrate non blocking reader for client identification } catch (IOException e) { - e.printStackTrace(); + LOGGER.log(Level.SEVERE, "[DXClusterSrvr] error in worker runnable", e); } finally { synchronized(dxClusterClientSocketsConnectedList) { dxClusterClientSocketsConnectedList.remove(clientSocket); // Entferne den Client nach Verarbeitung diff --git a/src/main/java/kst4contest/controller/InputReaderThread.java b/src/main/java/kst4contest/controller/InputReaderThread.java index 5282a07..9b8e986 100644 --- a/src/main/java/kst4contest/controller/InputReaderThread.java +++ b/src/main/java/kst4contest/controller/InputReaderThread.java @@ -2,6 +2,8 @@ package kst4contest.controller; import java.io.*; import java.net.*; +import java.util.logging.Level; +import java.util.logging.Logger; import kst4contest.model.ChatMessage; @@ -13,6 +15,7 @@ import kst4contest.model.ChatMessage; * No need for it as it´s not longer a console application */ public class InputReaderThread extends Thread { + private static final Logger LOGGER = Logger.getLogger(InputReaderThread.class.getName()); private PrintWriter writer; private Socket socket; private ChatController client; @@ -39,8 +42,7 @@ public class InputReaderThread extends Thread { try { sendThisMessage23001 = reader.readLine(); } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + LOGGER.log(Level.SEVERE, "Error reading from stdin", e); } ownMSG.setMessageText("MSG|" + this.client.getChatCategoryMain().getCategoryNumber() + "|0|" + sendThisMessage23001 + "|0|"); @@ -53,8 +55,8 @@ public class InputReaderThread extends Thread { try { this.sleep(500); } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + LOGGER.log(Level.SEVERE, "InputReaderThread interrupted", e); + Thread.currentThread().interrupt(); } } diff --git a/src/main/java/kst4contest/controller/ReadThread.java b/src/main/java/kst4contest/controller/ReadThread.java index 2d9439c..f26dd2d 100644 --- a/src/main/java/kst4contest/controller/ReadThread.java +++ b/src/main/java/kst4contest/controller/ReadThread.java @@ -3,6 +3,8 @@ package kst4contest.controller; import java.io.*; import java.net.*; import java.nio.charset.StandardCharsets; +import java.util.logging.Level; +import java.util.logging.Logger; import kst4contest.model.ChatMessage; @@ -14,6 +16,7 @@ import kst4contest.model.ChatMessage; * @author www.codejava.net */ public class ReadThread extends Thread { + private static final Logger LOGGER = Logger.getLogger(ReadThread.class.getName()); private BufferedReader reader; private Socket socket; private ChatController client; @@ -43,8 +46,7 @@ public class ReadThread extends Thread { reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)); } catch (IOException ex) { - System.out.println("Error getting input stream: " + ex.getMessage()); - ex.printStackTrace(); + LOGGER.log(Level.SEVERE, "Error getting input stream", ex); } } @@ -82,15 +84,14 @@ public class ReadThread extends Thread { } catch (Exception sexc) { - System.out.println("[ReadThread, CRITICAL: ] Socket geschlossen: " + sexc.getMessage()); + LOGGER.log(Level.SEVERE, "[ReadThread] Socket closed unexpectedly", sexc); try { this.client.getSocket().close(); this.interrupt(); break; - + } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + LOGGER.log(Level.SEVERE, "[ReadThread] Error closing socket", e); } } diff --git a/src/main/java/kst4contest/utils/ApplicationFileUtils.java b/src/main/java/kst4contest/utils/ApplicationFileUtils.java index 3f457cd..1d6da20 100644 --- a/src/main/java/kst4contest/utils/ApplicationFileUtils.java +++ b/src/main/java/kst4contest/utils/ApplicationFileUtils.java @@ -7,12 +7,16 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.Path; +import java.util.logging.Level; +import java.util.logging.Logger; /** * This class has utility methods to handle application files inside the home directory. */ public class ApplicationFileUtils { + private static final Logger LOGGER = Logger.getLogger(ApplicationFileUtils.class.getName()); + /** * Gets the path of a file inside the home directory of the user. * @param applicationName Name off the application which is used for the hidden directory @@ -61,8 +65,7 @@ public class ApplicationFileUtils { resourceStream.transferTo(fileOutputStream); } catch (IOException ex) { - System.err.println("Exception when copying Application file: " + ex.getMessage()); - ex.printStackTrace(System.err); + LOGGER.log(Level.SEVERE, "Exception when copying Application file: " + ex.getMessage(), ex); } } diff --git a/src/main/java/kst4contest/view/Kst4ContestApplication.java b/src/main/java/kst4contest/view/Kst4ContestApplication.java index ca518f5..180d4bd 100644 --- a/src/main/java/kst4contest/view/Kst4ContestApplication.java +++ b/src/main/java/kst4contest/view/Kst4ContestApplication.java @@ -3,7 +3,12 @@ package kst4contest.view; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; +import java.nio.file.Path; import java.util.*; +import java.util.logging.FileHandler; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.logging.SimpleFormatter; import java.util.function.Consumer; import java.util.function.Predicate; @@ -8504,9 +8509,24 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL public static void main(String[] args) { + setupFileLogging(); launch(args); } + private static void setupFileLogging() { + try { + String logDir = Path.of(System.getProperty("user.home"), ".praktiKST").toString(); + new File(logDir).mkdirs(); + FileHandler fileHandler = new FileHandler(logDir + "/kst4contest-errors.log", true); + fileHandler.setLevel(Level.SEVERE); + fileHandler.setFormatter(new SimpleFormatter()); + Logger rootLogger = Logger.getLogger(""); + rootLogger.addHandler(fileHandler); + } catch (IOException e) { + System.err.println("Could not set up file logging: " + e.getMessage()); + } + } + @Override public void onThreadStatusChanged(String key, ThreadStateMessage threadStateMessage) { diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 11ffaf3..5382c37 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -5,6 +5,7 @@ module praktiKST { requires jdk.xml.dom; requires java.sql; requires javafx.media; + requires java.logging; exports kst4contest.controller.interfaces; exports kst4contest.controller; exports kst4contest.locatorUtils;