From d9694b7000907b26a84b2be0e0bae4eb2e4bcf2c Mon Sep 17 00:00:00 2001 From: Jakub Date: Fri, 21 Nov 2025 17:35:05 +0100 Subject: [PATCH 1/5] Fixed security button and powershell command, fixed search bar (problem with unexpected details popup's) --- HowToRun.md | 40 +++++++++++++ HowToRun_PL.md | 40 +++++++++++++ pom.xml | 13 ++++- .../SystemLogAnalyzerApp.java | 40 ++++++++++++- .../controller/MainWindowFXController.java | 5 ++ .../controller/WelcomeViewFXController.java | 27 +++++++-- .../io/WindowsEventExporter.java | 28 ++++++++-- .../system/WindowsElevationManager.java | 56 +++++++++++++++++++ 8 files changed, 234 insertions(+), 15 deletions(-) create mode 100644 HowToRun.md create mode 100644 HowToRun_PL.md create mode 100644 src/main/java/com/project/system_log_analyzer/system/WindowsElevationManager.java diff --git a/HowToRun.md b/HowToRun.md new file mode 100644 index 0000000..b33b618 --- /dev/null +++ b/HowToRun.md @@ -0,0 +1,40 @@ +## 🖥️ Installation (2 clicks — for recruiters) + +The application is distributed as a **standalone EXE** built with `jpackage`. + +### ✔ Requirements +Nothing except: +- Windows 10/11 +- Local user profile +- Windows PowerShell (built-in on Windows 10/11) + +> **Java is bundled inside the EXE**. +> You do NOT need to install Java. + +### ✔ Running the app +1. Download `SystemLogAnalyzer.rar` +2. unpack it with winrar anywhere. +3. Double-click on SystemLogAnalyzer.exe +4. (Optional) If Security logs are selected → confirm Windows UAC popup + +That's all. + +## 📦 How it works + +### 1. Choose: +- directory for storing exported CSVs +- directory for saving reports +- log types (Application / System / Security) + +### 2. The app: +- runs PowerShell → exports CSV +- parses records +- loads them into a JavaFX table + +### 3. You can: +- filter +- search +- inspect details +- refresh logs anytime + +All without touching Event Viewer manually. \ No newline at end of file diff --git a/HowToRun_PL.md b/HowToRun_PL.md new file mode 100644 index 0000000..45c5d5b --- /dev/null +++ b/HowToRun_PL.md @@ -0,0 +1,40 @@ +## 🖥️ Instalacja (2 kliknięcia — dla rekruterów) + +Aplikacja jest dystrybuowana jako **samodzielny plik EXE** zbudowany przy użyciu `jpackage`. + +### ✔ Wymagania +Nic poza: +- Windows 10/11 +- Lokalnym profilem użytkownika +- Windows PowerShell (built-in on Windows 10/11) + +> **Java jest dołączona do pliku EXE**. +> NIE musisz instalować Javy. + +### ✔ Uruchamianie aplikacji +1. Pobierz `SystemLogAnalyzer.exe` +2. Wypakuj program w dowolne miejsce +3. Kliknij go dwukrotnie na SystemLogAnalyzer.exe +4. (Opcjonalnie) Jeśli wybrano dzienniki zabezpieczeń → potwierdź wyskakujące okienko UAC systemu Windows + +To wszystko. + +## 📦 Jak to działa + +### 1. Wybierz: +- katalog do przechowywania wyeksportowanych plików CSV +- katalog do zapisywania raportów +- typy logów (Aplikacja/System/Zabezpieczenia) + +### 2. Aplikacja: +- uruchamia program PowerShell → eksportuje plik CSV +- analizuje rekordy +- ładuje je do tabeli JavaFX + +### 3. Możesz: +- filtrować +- wyszukiwać +- sprawdzać szczegóły +- odświeżać logi w dowolnym momencie + +Wszystko to bez ręcznego uruchamiania Podglądu zdarzeń. \ No newline at end of file diff --git a/pom.xml b/pom.xml index 04fd624..842a6f4 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ - + org.springframework spring-context @@ -46,12 +46,19 @@ 25-ea+1 + + + net.java.dev.jna + jna + 5.13.0 + + - + org.apache.maven.plugins maven-compiler-plugin @@ -71,7 +78,7 @@ - + org.apache.maven.plugins maven-surefire-plugin diff --git a/src/main/java/com/project/system_log_analyzer/SystemLogAnalyzerApp.java b/src/main/java/com/project/system_log_analyzer/SystemLogAnalyzerApp.java index 47c9a4a..98c5172 100644 --- a/src/main/java/com/project/system_log_analyzer/SystemLogAnalyzerApp.java +++ b/src/main/java/com/project/system_log_analyzer/SystemLogAnalyzerApp.java @@ -1,6 +1,7 @@ package com.project.system_log_analyzer; import com.project.system_log_analyzer.config.SpringConfig; +import com.project.system_log_analyzer.system.WindowsElevationManager; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; @@ -8,13 +9,29 @@ import javafx.stage.Stage; import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.PrintStream; + public class SystemLogAnalyzerApp extends Application { private AnnotationConfigApplicationContext springContext; + private static Boolean elevatedFlag = false; // Admin permissions + @Override public void init() { springContext = new AnnotationConfigApplicationContext(SpringConfig.class); + + // debug + PrintStream out = null; + try { + out = new PrintStream(new FileOutputStream("app.log", true), true); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + System.setOut(out); + System.setErr(out); } @Override @@ -22,8 +39,25 @@ public void start(Stage primaryStage) throws Exception { FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/WelcomeView.fxml")); loader.setControllerFactory(springContext::getBean); - Parent root = loader.load(); + boolean elevated = getParameters().getRaw().contains("--elevated"); + elevatedFlag = elevated; // Admin profile checker + + System.out.println("ARGS = " + getParameters().getRaw()); // Admin permission check to debug file + + com.project.system_log_analyzer.config.appConfig cfg = springContext.getBean(com.project.system_log_analyzer.config.appConfig.class); + if (elevated) { + cfg.setCsvSecurity(true); + } + + try { + cfg.setCsvSecurity(elevated); + IO.println("Elevated mode: " + elevated); + } catch (Exception e) { + System.err.println("Could not set elevated flag in appConfig: " + e.getMessage()); + } + + Parent root = loader.load(); SpringConfig.APP_READY = true; Scene scene = new Scene(root); @@ -37,6 +71,10 @@ public void stop() { springContext.close(); } + public static boolean isElevated() { + return Boolean.TRUE.equals(elevatedFlag); + } + public static void main(String[] args) { launch(args); } diff --git a/src/main/java/com/project/system_log_analyzer/controller/MainWindowFXController.java b/src/main/java/com/project/system_log_analyzer/controller/MainWindowFXController.java index 3d38742..104d2e7 100644 --- a/src/main/java/com/project/system_log_analyzer/controller/MainWindowFXController.java +++ b/src/main/java/com/project/system_log_analyzer/controller/MainWindowFXController.java @@ -175,6 +175,9 @@ public void onRefreshClick() { refreshButton.setDisable(true); logTable.setDisable(true); loadingLabel.setText("Refreshing logs… Please wait. (Time of loading depends on number of logs)"); + logTable.getSelectionModel().clearSelection(); + logTable.getFocusModel().focus(-1); // Details unexpected pop-up || Fixed + onClearFilters(); // Clear all filters Task> task = new Task>() { @Override @@ -222,6 +225,8 @@ protected List call() throws Exception { } @FXML public void onSearchChanged() { + logTable.getSelectionModel().clearSelection(); + logTable.getFocusModel().focus(-1); // Details unexpected pop-up || Fixed applyFilters(); } diff --git a/src/main/java/com/project/system_log_analyzer/controller/WelcomeViewFXController.java b/src/main/java/com/project/system_log_analyzer/controller/WelcomeViewFXController.java index 61982cb..45687d5 100644 --- a/src/main/java/com/project/system_log_analyzer/controller/WelcomeViewFXController.java +++ b/src/main/java/com/project/system_log_analyzer/controller/WelcomeViewFXController.java @@ -1,7 +1,10 @@ package com.project.system_log_analyzer.controller; +import com.project.system_log_analyzer.SystemLogAnalyzerApp; import com.project.system_log_analyzer.config.ApplicationContextProvider; import com.project.system_log_analyzer.config.appConfig; +import com.project.system_log_analyzer.system.WindowsElevationManager; +import javafx.application.Platform; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; @@ -34,6 +37,11 @@ public class WelcomeViewFXController { @FXML public void initialize() { System.out.println("Controller initialized, appConfig = " + appConfig); + + if (appConfig.isCsvSecurity()) { + securityButton.setSelected(true); + securityLabel.setText("Admin permission granted!"); + } } @@ -57,7 +65,7 @@ private void scan(ActionEvent event) throws IOException { ApplicationContext springContext = ApplicationContextProvider.getApplicationContext(); FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/LoadingScreen.fxml")); - loader.setControllerFactory(springContext::getBean); // Spring Boot starter by JavaFX !!IMPORTANT!! + loader.setControllerFactory(springContext::getBean); // Spring starter by JavaFX !!IMPORTANT!! Parent root = loader.load(); Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow(); @@ -103,17 +111,26 @@ private void systemButtonON(ActionEvent event) throws IOException { @FXML private void securityButtonON(ActionEvent event) throws IOException { + + if (SystemLogAnalyzerApp.isElevated()) { // Admin ckeck for Security Logs + securityLabel.setText("Admin permission granted!"); + appConfig.setCsvSecurity(securityButton.isSelected()); + return; + } + if (securityButton.isSelected()) { boolean proceed = askForSecurityPermission(); if (!proceed) { securityButton.setSelected(false); - securityLabel.setText("Admin permission required!"); appConfig.setCsvSecurity(false); + securityLabel.setText("Admin permission required!"); return; - } else { - securityLabel.setText("Admin permission granted!"); - appConfig.setCsvSecurity(true); } + + boolean relaunchStarted = WindowsElevationManager.relaunchAsAdmin("--elevated"); + + if (relaunchStarted) Platform.exit(); + } else { appConfig.setCsvSecurity(false); securityLabel.setText("(Requires Admin Permission)"); diff --git a/src/main/java/com/project/system_log_analyzer/io/WindowsEventExporter.java b/src/main/java/com/project/system_log_analyzer/io/WindowsEventExporter.java index eda0b19..9e893ca 100644 --- a/src/main/java/com/project/system_log_analyzer/io/WindowsEventExporter.java +++ b/src/main/java/com/project/system_log_analyzer/io/WindowsEventExporter.java @@ -78,7 +78,7 @@ public Path exportToCsv(LogType type) { // Method responsible for exporting logs } } - public Path exportSecurityLogsAsAdmin() { // Method responsible for exporting Security logs with admin permissions + public Path exportSecurityLogsAsAdmin() { try { String baseDir = config.getLogsDir() != null && !config.getLogsDir().isEmpty() ? config.getLogsDir() : "logs/exported"; @@ -89,15 +89,30 @@ public Path exportSecurityLogsAsAdmin() { // Method responsible for exporting S String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")); Path outputFile = Path.of(dir.getAbsolutePath(), "Security_" + timestamp + ".csv"); - String powershellCommand = - "Start-Process powershell -Verb RunAs -ArgumentList " + - "\"Get-WinEvent -LogName Security | " + + String ps = + "Get-WinEvent -LogName Security -MaxEvents 15000 | " + "Select-Object TimeCreated, Id, LevelDisplayName, ProviderName, Message | " + - "Export-Csv -Path '" + outputFile.toAbsolutePath() + "' -NoTypeInformation -Encoding UTF8\""; + "Export-Csv -Path '" + outputFile.toAbsolutePath() + + "' -NoTypeInformation -Encoding UTF8"; - ProcessBuilder pb = new ProcessBuilder("Powershell.exe", "-Command", powershellCommand); + + + List cmd = new ArrayList<>(); + cmd.add("powershell.exe"); + cmd.add("-NoProfile"); + cmd.add("-ExecutionPolicy"); + cmd.add("Bypass"); + cmd.add("-Command"); + cmd.add(ps); + + ProcessBuilder pb = new ProcessBuilder(cmd); + pb.redirectErrorStream(true); Process process = pb.start(); + try (BufferedReader out = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + out.lines().forEach(System.out::println); + } + int exit = process.waitFor(); if (exit == 0) { @@ -114,6 +129,7 @@ public Path exportSecurityLogsAsAdmin() { // Method responsible for exporting S return null; } + public List exportSelected() { List paths = new ArrayList<>(); diff --git a/src/main/java/com/project/system_log_analyzer/system/WindowsElevationManager.java b/src/main/java/com/project/system_log_analyzer/system/WindowsElevationManager.java new file mode 100644 index 0000000..fee16ff --- /dev/null +++ b/src/main/java/com/project/system_log_analyzer/system/WindowsElevationManager.java @@ -0,0 +1,56 @@ +package com.project.system_log_analyzer.system; + +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.win32.W32APIOptions; + +import java.io.File; + +public class WindowsElevationManager { + + public interface Shell32 extends com.sun.jna.Library { + Shell32 INSTANCE = Native.load("shell32", Shell32.class, W32APIOptions.DEFAULT_OPTIONS); + + Pointer ShellExecuteW( + Pointer hwnd, + WString lpOperation, + WString lpFile, + WString lpParameters, + WString lpDirectory, + int nShowCmd + ); + } + + public static boolean relaunchAsAdmin(String params) { + try { + String exePath = new File(System.getProperty("user.dir"), + "System_Log_Analyzer.exe").getAbsolutePath(); + + Pointer p = Shell32.INSTANCE.ShellExecuteW( + null, + new WString("runas"), + new WString(exePath), + params == null ? null : new WString(params), + null, + 1 + ); + + long result = Pointer.nativeValue(p); + return result > 32; + + } catch (Throwable t) { + t.printStackTrace(); + return false; + } + } + + public static boolean isCurrentUserAdmin() { + try { + new java.io.File("C:\\Windows\\System32\\config\\systemprofile").list(); + return true; + } catch (Throwable e) { + return false; + } + } +} From 2c7757c38a3f2a63999ee460cb5a440c8886acea Mon Sep 17 00:00:00 2001 From: Jakub Date: Sun, 23 Nov 2025 16:03:25 +0100 Subject: [PATCH 2/5] Added noLogs feature, imporved exporter path location --- app.log | 33 +++++++ .../system_log_analyzer/config/appConfig.java | 18 ++++ .../controller/WelcomeViewFXController.java | 86 ++++++++++++++++++- .../core/FileLoggerService.java | 16 +++- .../io/WindowsEventExporter.java | 28 +++--- .../system/AppShutdownHandler.java | 39 ++++++++- src/main/resources/view/WelcomeView.fxml | 9 ++ 7 files changed, 212 insertions(+), 17 deletions(-) create mode 100644 app.log diff --git a/app.log b/app.log new file mode 100644 index 0000000..31b4bc3 --- /dev/null +++ b/app.log @@ -0,0 +1,33 @@ + +ARGS = [] +Elevated mode: false +Controller initialized, appConfig = com.project.system_log_analyzer.config.appConfig@1156ebe3 +Application logs export : true +LoadingBarFXController::initialize +exported injected - exporting logs from backend +Export successful: C:\Users\jakub\IdeaProjects\System Log Analyzer\logs\exported\exported\Application_20251123_154710.csv +Logs list exported +FileLoggerService: logsDir not set yet — skipping log. +Exception in thread "JavaFX Application Thread" java.lang.NullPointerException: Cannot invoke "java.io.File.length()" because "this.logFile" is null + at com.project.system_log_analyzer.core.FileLoggerService.rotateLogsIfNeeded(FileLoggerService.java:192) + at com.project.system_log_analyzer.core.FileLoggerService.saveParsedLogs(FileLoggerService.java:299) + at com.project.system_log_analyzer.controller.MainWindowFXController.setData(MainWindowFXController.java:104) + at com.project.system_log_analyzer.controller.LoadingBarFXController.lambda$initialize$0(LoadingBarFXController.java:79) + at javafx.base@25-ea/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86) + at javafx.base@25-ea/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:232) + at javafx.base@25-ea/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:189) + at javafx.base@25-ea/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58) + at javafx.base@25-ea/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) + at javafx.base@25-ea/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74) + at javafx.base@25-ea/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54) + at javafx.base@25-ea/javafx.event.Event.fireEvent(Event.java:199) + at javafx.graphics@25-ea/javafx.concurrent.EventHelper.fireEvent(EventHelper.java:219) + at javafx.graphics@25-ea/javafx.concurrent.Task.fireEvent(Task.java:1321) + at javafx.graphics@25-ea/javafx.concurrent.Task.setState(Task.java:724) + at javafx.graphics@25-ea/javafx.concurrent.Task$TaskCallable.lambda$call$1(Task.java:1399) + at javafx.graphics@25-ea/com.sun.javafx.application.PlatformImpl.lambda$runLater$4(PlatformImpl.java:419) + at javafx.graphics@25-ea/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95) + at javafx.graphics@25-ea/com.sun.glass.ui.win.WinApplication._runLoop(Native Method) + at javafx.graphics@25-ea/com.sun.glass.ui.win.WinApplication.lambda$runLoop$0(WinApplication.java:168) + at java.base/java.lang.Thread.run(Thread.java:1474) +NoLogs = true → temporary directory removed. diff --git a/src/main/java/com/project/system_log_analyzer/config/appConfig.java b/src/main/java/com/project/system_log_analyzer/config/appConfig.java index 4629dab..4c33335 100644 --- a/src/main/java/com/project/system_log_analyzer/config/appConfig.java +++ b/src/main/java/com/project/system_log_analyzer/config/appConfig.java @@ -9,6 +9,24 @@ public class appConfig { private boolean csvApplication; private boolean csvSystem; private boolean csvSecurity; + private boolean noLogs; + private boolean saveInExeDir; + + public boolean isNoLogs() { + return noLogs; + } + + public void setNoLogs(boolean noLogs) { + this.noLogs = noLogs; + } + + public boolean isSaveInExeDir() { + return saveInExeDir; + } + + public void setSaveInExeDir(boolean saveInExeDir) { + this.saveInExeDir = saveInExeDir; + } public boolean isCsvSecurity() { return csvSecurity; diff --git a/src/main/java/com/project/system_log_analyzer/controller/WelcomeViewFXController.java b/src/main/java/com/project/system_log_analyzer/controller/WelcomeViewFXController.java index 45687d5..89094da 100644 --- a/src/main/java/com/project/system_log_analyzer/controller/WelcomeViewFXController.java +++ b/src/main/java/com/project/system_log_analyzer/controller/WelcomeViewFXController.java @@ -31,6 +31,12 @@ public class WelcomeViewFXController { @FXML private CheckBox systemButton; @FXML private CheckBox securityButton; @FXML private Label securityLabel; + @FXML private Button logFilesDirButton; + @FXML private Button reportDirButton; + + @FXML private CheckBox noLogsBox; + @FXML private CheckBox saveInAppDirectoryBox; + @Autowired public appConfig appConfig; @@ -50,9 +56,11 @@ private void scan(ActionEvent event) throws IOException { String logDir = logFilesDirField.getText(); String reportDir = reportDirField.getText(); - if (logDir.isEmpty() || reportDir.isEmpty()) { - informationLabel.setText("Please select both directories."); - return; + if (!noLogsBox.isSelected()) { + if (logDir.isEmpty() || reportDir.isEmpty()) { + informationLabel.setText("Please select both directories."); + return; + } } if (!appButton.isSelected() && !systemButton.isSelected() && !securityButton.isSelected()) { @@ -146,4 +154,76 @@ private boolean askForSecurityPermission() { return alert.showAndWait().filter(btn -> btn == ButtonType.OK).isPresent(); } + @FXML + private void noLogsBoxOn(ActionEvent event) throws IOException { + if (noLogsBox.isSelected()) { + appConfig.setNoLogs(true); + + appConfig.setSaveInExeDir(false); + saveInAppDirectoryBox.setSelected(false); + saveInAppDirectoryBox.setDisable(true); + + logFilesDirButton.setDisable(true); + reportDirButton.setDisable(true); + + logFilesDirField.clear(); + reportDirField.clear(); + + appConfig.setLogsDir(null); + appConfig.setReportDir(null); + + } else { + appConfig.setNoLogs(false); + saveInAppDirectoryBox.setDisable(false); + + if (appConfig.isSaveInExeDir()) { + String baseDir = System.getProperty("user.dir"); + String logs = baseDir + "/logs"; + String reports = baseDir + "/reports"; + + appConfig.setSaveInExeDir(true); + appConfig.setLogsDir(logs); + appConfig.setReportDir(reports); + + logFilesDirField.setText(logs); + reportDirField.setText(reports); + + logFilesDirButton.setDisable(true); + reportDirButton.setDisable(true); + + } else { + logFilesDirButton.setDisable(false); + reportDirButton.setDisable(false); + } + } + } + @FXML + private void saveInAppDirectoryBoxOn(ActionEvent event) throws IOException { + if (saveInAppDirectoryBox.isSelected()) { + appConfig.setSaveInExeDir(true); + + String baseDir = System.getProperty("user.dir"); + String logs = baseDir + "/logs"; + String reports = baseDir + "/reports"; + + logFilesDirField.setText(logs); + reportDirField.setText(reports); + + logFilesDirButton.setDisable(true); + reportDirButton.setDisable(true); + + new File(logs).mkdirs(); + new File(reports).mkdirs(); + + appConfig.setLogsDir(logs); + appConfig.setReportDir(reports); + + } else { + appConfig.setSaveInExeDir(false); + + logFilesDirButton.setDisable(false); + reportDirButton.setDisable(false); + + } + } } diff --git a/src/main/java/com/project/system_log_analyzer/core/FileLoggerService.java b/src/main/java/com/project/system_log_analyzer/core/FileLoggerService.java index 778712a..bb5c76d 100644 --- a/src/main/java/com/project/system_log_analyzer/core/FileLoggerService.java +++ b/src/main/java/com/project/system_log_analyzer/core/FileLoggerService.java @@ -57,13 +57,23 @@ private void ensureLogFile() { if (logsDir == null) { String path = config.getLogsDir(); - if (path == null) { - System.out.println("FileLoggerService: logsDir not set yet — skipping log."); - return; // + + if (path == null || path.isBlank()) { + + String baseDir = System.getProperty("user.dir"); + path = baseDir + "/temp_"; + + new File(path).mkdirs(); + + config.setLogsDir(path); + config.setReportDir(path); + + System.out.println("FileLoggerService: Using TEMP directory for logs: " + path); } logsDir = new File(path); if (!logsDir.exists()) logsDir.mkdirs(); + logFile = new File(logsDir, "log_" + nOfLogPart + ".log"); createLogFileIfMissing(); } diff --git a/src/main/java/com/project/system_log_analyzer/io/WindowsEventExporter.java b/src/main/java/com/project/system_log_analyzer/io/WindowsEventExporter.java index 9e893ca..cb4abb5 100644 --- a/src/main/java/com/project/system_log_analyzer/io/WindowsEventExporter.java +++ b/src/main/java/com/project/system_log_analyzer/io/WindowsEventExporter.java @@ -42,14 +42,18 @@ public WindowsEventExporter(appConfig config) { public Path exportToCsv(LogType type) { // Method responsible for exporting logs from windows try { - String baseDir = config.getLogsDir() != null && !config.getLogsDir().isEmpty() - ? config.getLogsDir() : "logs/exported"; + String baseDir; + if (config.getLogsDir() == null || config.getLogsDir().isBlank()) { + baseDir = System.getProperty("user.dir") + "/temp_"; + } else { + baseDir = config.getLogsDir(); + } - File dir = new File(baseDir, "exported"); - if (!dir.exists()) dir.mkdirs(); + File exportedDir = new File(baseDir, "exported"); + if (!exportedDir.exists()) exportedDir.mkdirs(); String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")); - Path outputFile = Path.of(dir.getAbsolutePath(), type.getLogName() + "_" + timestamp + ".csv"); + Path outputFile = Path.of(exportedDir.getAbsolutePath(), "Security_" + timestamp + ".csv"); // Powershell command String command = String.format( @@ -80,14 +84,18 @@ public Path exportToCsv(LogType type) { // Method responsible for exporting logs public Path exportSecurityLogsAsAdmin() { try { - String baseDir = config.getLogsDir() != null && !config.getLogsDir().isEmpty() - ? config.getLogsDir() : "logs/exported"; + String baseDir; + if (config.getLogsDir() == null || config.getLogsDir().isBlank()) { + baseDir = System.getProperty("user.dir") + "/temp_"; + } else { + baseDir = config.getLogsDir(); + } - File dir = new File(baseDir, "exported"); - if (!dir.exists()) dir.mkdirs(); + File exportedDir = new File(baseDir, "exported"); + if (!exportedDir.exists()) exportedDir.mkdirs(); String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")); - Path outputFile = Path.of(dir.getAbsolutePath(), "Security_" + timestamp + ".csv"); + Path outputFile = Path.of(exportedDir.getAbsolutePath(), "Security_" + timestamp + ".csv"); String ps = "Get-WinEvent -LogName Security -MaxEvents 15000 | " + diff --git a/src/main/java/com/project/system_log_analyzer/system/AppShutdownHandler.java b/src/main/java/com/project/system_log_analyzer/system/AppShutdownHandler.java index 8a5cba4..c78920e 100644 --- a/src/main/java/com/project/system_log_analyzer/system/AppShutdownHandler.java +++ b/src/main/java/com/project/system_log_analyzer/system/AppShutdownHandler.java @@ -1,6 +1,7 @@ package com.project.system_log_analyzer.system; import com.project.system_log_analyzer.config.SpringConfig; +import com.project.system_log_analyzer.config.appConfig; import com.project.system_log_analyzer.core.FileLoggerService; import com.project.system_log_analyzer.core.FileReportExporter; import org.springframework.beans.factory.annotation.Autowired; @@ -8,16 +9,25 @@ import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; + @Component public class AppShutdownHandler { private FileLoggerService fileLoggerService; private FileReportExporter reportExporter; + private appConfig appConfig; @Autowired - public AppShutdownHandler(FileLoggerService fileLoggerService, FileReportExporter reportExporter) { + public AppShutdownHandler(FileLoggerService fileLoggerService, FileReportExporter reportExporter, appConfig config) { this.fileLoggerService = fileLoggerService; this.reportExporter = reportExporter; + this.appConfig = config; } @EventListener(ContextClosedEvent.class) @@ -27,6 +37,21 @@ public void onShutdown() { return; } + if (appConfig.isNoLogs()) { + Path dir = Paths.get(appConfig.getLogsDir()); + + try { + if (Files.exists(dir)) { + deleteDirectoryRecursively(dir); + System.out.println("NoLogs = true → temporary directory removed."); + } + } catch (Exception e) { + System.err.println("Failed to delete temp directory: " + e.getMessage()); + } + + return; + } + try { System.out.println("AppShutdownHandler - Application is shutting down! Flushing logs and exporting report..."); fileLoggerService.flushLogToMainFile(); @@ -36,5 +61,17 @@ public void onShutdown() { e.printStackTrace(); } } + // Method for correct deletion of temp files when noLogs is selected + private void deleteDirectoryRecursively(Path path) throws IOException { + if (!Files.exists(path)) return; + + Files.walk(path) + .sorted(Comparator.reverseOrder()) + .forEach(p -> { + try { + Files.deleteIfExists(p); + } catch (IOException ignored) {} + }); + } } diff --git a/src/main/resources/view/WelcomeView.fxml b/src/main/resources/view/WelcomeView.fxml index 04c532f..966ade8 100644 --- a/src/main/resources/view/WelcomeView.fxml +++ b/src/main/resources/view/WelcomeView.fxml @@ -38,6 +38,15 @@ + + + From d97a7fe33035c53ef1490589e6633d6917f50ff3 Mon Sep 17 00:00:00 2001 From: Jakub Date: Sun, 23 Nov 2025 16:11:58 +0100 Subject: [PATCH 3/5] Fixed bug with filters and details popup --- .gitignore | 1 + app.log | 33 ------------------- .../controller/MainWindowFXController.java | 3 ++ 3 files changed, 4 insertions(+), 33 deletions(-) delete mode 100644 app.log diff --git a/.gitignore b/.gitignore index b4dc8d0..a65e01e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ TO-DO.md dist target/ +app.log .mvn/wrapper/maven-wrapper.jar !**/src/main/**/target/ !**/src/test/**/target/ diff --git a/app.log b/app.log deleted file mode 100644 index 31b4bc3..0000000 --- a/app.log +++ /dev/null @@ -1,33 +0,0 @@ - -ARGS = [] -Elevated mode: false -Controller initialized, appConfig = com.project.system_log_analyzer.config.appConfig@1156ebe3 -Application logs export : true -LoadingBarFXController::initialize -exported injected - exporting logs from backend -Export successful: C:\Users\jakub\IdeaProjects\System Log Analyzer\logs\exported\exported\Application_20251123_154710.csv -Logs list exported -FileLoggerService: logsDir not set yet — skipping log. -Exception in thread "JavaFX Application Thread" java.lang.NullPointerException: Cannot invoke "java.io.File.length()" because "this.logFile" is null - at com.project.system_log_analyzer.core.FileLoggerService.rotateLogsIfNeeded(FileLoggerService.java:192) - at com.project.system_log_analyzer.core.FileLoggerService.saveParsedLogs(FileLoggerService.java:299) - at com.project.system_log_analyzer.controller.MainWindowFXController.setData(MainWindowFXController.java:104) - at com.project.system_log_analyzer.controller.LoadingBarFXController.lambda$initialize$0(LoadingBarFXController.java:79) - at javafx.base@25-ea/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86) - at javafx.base@25-ea/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:232) - at javafx.base@25-ea/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:189) - at javafx.base@25-ea/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58) - at javafx.base@25-ea/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) - at javafx.base@25-ea/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74) - at javafx.base@25-ea/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54) - at javafx.base@25-ea/javafx.event.Event.fireEvent(Event.java:199) - at javafx.graphics@25-ea/javafx.concurrent.EventHelper.fireEvent(EventHelper.java:219) - at javafx.graphics@25-ea/javafx.concurrent.Task.fireEvent(Task.java:1321) - at javafx.graphics@25-ea/javafx.concurrent.Task.setState(Task.java:724) - at javafx.graphics@25-ea/javafx.concurrent.Task$TaskCallable.lambda$call$1(Task.java:1399) - at javafx.graphics@25-ea/com.sun.javafx.application.PlatformImpl.lambda$runLater$4(PlatformImpl.java:419) - at javafx.graphics@25-ea/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95) - at javafx.graphics@25-ea/com.sun.glass.ui.win.WinApplication._runLoop(Native Method) - at javafx.graphics@25-ea/com.sun.glass.ui.win.WinApplication.lambda$runLoop$0(WinApplication.java:168) - at java.base/java.lang.Thread.run(Thread.java:1474) -NoLogs = true → temporary directory removed. diff --git a/src/main/java/com/project/system_log_analyzer/controller/MainWindowFXController.java b/src/main/java/com/project/system_log_analyzer/controller/MainWindowFXController.java index 104d2e7..1f2807b 100644 --- a/src/main/java/com/project/system_log_analyzer/controller/MainWindowFXController.java +++ b/src/main/java/com/project/system_log_analyzer/controller/MainWindowFXController.java @@ -249,6 +249,9 @@ public void onFilterChanged() { private void applyFilters() { if (logs == null) return; + logTable.getSelectionModel().clearSelection(); + logTable.getFocusModel().focus(-1); + String input = searchField.getText().toLowerCase().trim(); // input correctness check From 276696e5b208b1cbf855c33b95c0b8781bc11695 Mon Sep 17 00:00:00 2001 From: Jakub Date: Sun, 23 Nov 2025 18:09:34 +0100 Subject: [PATCH 4/5] Fixed creation of temp file and permissions requirements for admin --- HowToRun.md | 2 +- .../SystemLogAnalyzerApp.java | 23 +++++++++++------- .../config/SpringConfig.java | 2 ++ .../system_log_analyzer/config/appConfig.java | 9 +++++++ .../controller/WelcomeViewFXController.java | 14 ++++++++--- .../io/WindowsEventExporter.java | 4 ++-- .../system/AppShutdownHandler.java | 24 ++++++++++++------- 7 files changed, 54 insertions(+), 24 deletions(-) diff --git a/HowToRun.md b/HowToRun.md index b33b618..856f92a 100644 --- a/HowToRun.md +++ b/HowToRun.md @@ -4,7 +4,7 @@ The application is distributed as a **standalone EXE** built with `jpackage`. ### ✔ Requirements Nothing except: -- Windows 10/11 +- Windows 10/11 (Currently on win 10 System Logs are not working - TODO) - Local user profile - Windows PowerShell (built-in on Windows 10/11) diff --git a/src/main/java/com/project/system_log_analyzer/SystemLogAnalyzerApp.java b/src/main/java/com/project/system_log_analyzer/SystemLogAnalyzerApp.java index 98c5172..f5869ec 100644 --- a/src/main/java/com/project/system_log_analyzer/SystemLogAnalyzerApp.java +++ b/src/main/java/com/project/system_log_analyzer/SystemLogAnalyzerApp.java @@ -1,7 +1,6 @@ package com.project.system_log_analyzer; import com.project.system_log_analyzer.config.SpringConfig; -import com.project.system_log_analyzer.system.WindowsElevationManager; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; @@ -15,6 +14,8 @@ public class SystemLogAnalyzerApp extends Application { + private boolean app_Log = false; + private AnnotationConfigApplicationContext springContext; private static Boolean elevatedFlag = false; // Admin permissions @@ -23,15 +24,19 @@ public class SystemLogAnalyzerApp extends Application { public void init() { springContext = new AnnotationConfigApplicationContext(SpringConfig.class); - // debug - PrintStream out = null; - try { - out = new PrintStream(new FileOutputStream("app.log", true), true); - } catch (FileNotFoundException e) { - throw new RuntimeException(e); + if (app_Log) { // debug app.log in main directory + PrintStream out = null; + try { + out = new PrintStream(new FileOutputStream("app.log", true), true); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + System.setOut(out); + System.setErr(out); } - System.setOut(out); - System.setErr(out); + + + } @Override diff --git a/src/main/java/com/project/system_log_analyzer/config/SpringConfig.java b/src/main/java/com/project/system_log_analyzer/config/SpringConfig.java index 8aecbdc..a9185d5 100644 --- a/src/main/java/com/project/system_log_analyzer/config/SpringConfig.java +++ b/src/main/java/com/project/system_log_analyzer/config/SpringConfig.java @@ -1,6 +1,8 @@ package com.project.system_log_analyzer.config; +import com.project.system_log_analyzer.SystemLogAnalyzerApp; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; diff --git a/src/main/java/com/project/system_log_analyzer/config/appConfig.java b/src/main/java/com/project/system_log_analyzer/config/appConfig.java index 4c33335..09bd5f4 100644 --- a/src/main/java/com/project/system_log_analyzer/config/appConfig.java +++ b/src/main/java/com/project/system_log_analyzer/config/appConfig.java @@ -11,6 +11,15 @@ public class appConfig { private boolean csvSecurity; private boolean noLogs; private boolean saveInExeDir; + private boolean relaunch; + + public boolean isRelaunch() { + return relaunch; + } + + public void setRelaunch(boolean relaunch) { + this.relaunch = relaunch; + } public boolean isNoLogs() { return noLogs; diff --git a/src/main/java/com/project/system_log_analyzer/controller/WelcomeViewFXController.java b/src/main/java/com/project/system_log_analyzer/controller/WelcomeViewFXController.java index 89094da..fbb1112 100644 --- a/src/main/java/com/project/system_log_analyzer/controller/WelcomeViewFXController.java +++ b/src/main/java/com/project/system_log_analyzer/controller/WelcomeViewFXController.java @@ -137,10 +137,18 @@ private void securityButtonON(ActionEvent event) throws IOException { boolean relaunchStarted = WindowsElevationManager.relaunchAsAdmin("--elevated"); - if (relaunchStarted) Platform.exit(); + if (relaunchStarted) { + appConfig.setRelaunch(true); + Platform.exit(); + } else { + appConfig.setCsvSecurity(false); + securityButton.setSelected(false); + securityLabel.setText("(Requires Admin Permission)"); + } } else { appConfig.setCsvSecurity(false); + securityButton.setSelected(false); securityLabel.setText("(Requires Admin Permission)"); } } @@ -212,8 +220,8 @@ private void saveInAppDirectoryBoxOn(ActionEvent event) throws IOException { logFilesDirButton.setDisable(true); reportDirButton.setDisable(true); - new File(logs).mkdirs(); - new File(reports).mkdirs(); + //new File(logs).mkdirs(); + //new File(reports).mkdirs(); appConfig.setLogsDir(logs); appConfig.setReportDir(reports); diff --git a/src/main/java/com/project/system_log_analyzer/io/WindowsEventExporter.java b/src/main/java/com/project/system_log_analyzer/io/WindowsEventExporter.java index cb4abb5..5938ef6 100644 --- a/src/main/java/com/project/system_log_analyzer/io/WindowsEventExporter.java +++ b/src/main/java/com/project/system_log_analyzer/io/WindowsEventExporter.java @@ -21,7 +21,7 @@ public class WindowsEventExporter { public enum LogType { APPLICATION("Application"), SYSTEM("System"), - SECURITY("Security"); // TODO - Test in real environment (.exe/.jar) + SECURITY("Security"); private final String logName; LogType(String logName) { @@ -53,7 +53,7 @@ public Path exportToCsv(LogType type) { // Method responsible for exporting logs if (!exportedDir.exists()) exportedDir.mkdirs(); String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")); - Path outputFile = Path.of(exportedDir.getAbsolutePath(), "Security_" + timestamp + ".csv"); + Path outputFile = Path.of(exportedDir.getAbsolutePath(), type.getLogName() + "_" + timestamp + ".csv"); // Powershell command String command = String.format( diff --git a/src/main/java/com/project/system_log_analyzer/system/AppShutdownHandler.java b/src/main/java/com/project/system_log_analyzer/system/AppShutdownHandler.java index c78920e..198a3ab 100644 --- a/src/main/java/com/project/system_log_analyzer/system/AppShutdownHandler.java +++ b/src/main/java/com/project/system_log_analyzer/system/AppShutdownHandler.java @@ -9,7 +9,6 @@ import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -22,6 +21,7 @@ public class AppShutdownHandler { private FileLoggerService fileLoggerService; private FileReportExporter reportExporter; private appConfig appConfig; + private Boolean elevatedFlag; @Autowired public AppShutdownHandler(FileLoggerService fileLoggerService, FileReportExporter reportExporter, appConfig config) { @@ -48,18 +48,24 @@ public void onShutdown() { } catch (Exception e) { System.err.println("Failed to delete temp directory: " + e.getMessage()); } - return; } - try { - System.out.println("AppShutdownHandler - Application is shutting down! Flushing logs and exporting report..."); - fileLoggerService.flushLogToMainFile(); - System.out.println("AppShutdownHandler - Shutdown tasks completed successfully."); - } catch (Exception e) { - System.err.println("AppShutdownHandler - Error during shutdown tasks: " + e.getMessage()); - e.printStackTrace(); + if (appConfig.isRelaunch() || appConfig.getLogsDir() == null || appConfig.getLogsDir().isBlank()) { + System.out.println("Skipping flush — elevated admin relaunch or before Spring Injection"); + return; + } else { + try { + System.out.println("AppShutdownHandler - Application is shutting down! Flushing logs and exporting report..."); + fileLoggerService.flushLogToMainFile(); + System.out.println("AppShutdownHandler - Shutdown tasks completed successfully."); + } catch (Exception e) { + System.err.println("AppShutdownHandler - Error during shutdown tasks: " + e.getMessage()); + e.printStackTrace(); + } } + + } // Method for correct deletion of temp files when noLogs is selected private void deleteDirectoryRecursively(Path path) throws IOException { From 982970b2b9c39357fd51789514355ce534d9ddcf Mon Sep 17 00:00:00 2001 From: Jakub Date: Sun, 23 Nov 2025 18:22:26 +0100 Subject: [PATCH 5/5] Readme version change. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 72888d8..ac1a073 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# System Log Analyzer (Work in progress — v0.1 MVP) +# System Log Analyzer (Work in progress — v0.2) **System Log Analyzer** A standalone Windows desktop application for IT professionals to export, parse and analyze Windows Event Logs with a fast, clean and modern UI.