Skip to content
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ build
.gradle
.idea
.pdf
.db
.db
logs/
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ SQLite storage and PDF ledger reports.
`src/main/java/openconsignment/di`)
- SQLite (via `sqlite-jdbc`) for storage
- JasperReports for PDF report generation
- SLF4J with Log4j 2 for asynchronous rolling-file logging
- Gradle (Kotlin DSL) with the Shadow plugin for a single runnable JAR
- JUnit 5, Mockito, and TestFX for testing; Spotless (`palantir-java-format`)
for formatting; JaCoCo for coverage
Expand Down Expand Up @@ -107,6 +108,34 @@ Keep the JAR and launch script together when moving the application.
- The report organization name is configured in
`src/main/java/openconsignment/common/Constants.java`.

## Logging

The application writes INFO and higher messages to stderr and to
`logs/openconsignment.log` in its working directory. Application logging uses
SLF4J directly with Log4j 2 as its backend, including exception stack traces.
Use SLF4J's `LoggerFactory` and parameterized messages in application code.

The file is rolled at startup, daily, or when it reaches 10 MB. Archives are
compressed; during rollover, archives older than 30 days or beyond 100 MB total
are deleted. The active file is separate from that archive limit. File writes use
a bounded asynchronous queue; if it fills, callers wait rather than dropping
messages. The queue is drained on normal JVM shutdown. An abrupt termination
may lose queued messages. Log4j reports file setup problems to stderr, where
console logging remains available.

The working directory is the repository root when using `./gradlew run` and
`build/libs` when using the supplied launch scripts. To use a different
directory or file size, pass JVM properties, for example:

```shell
java -Dopenconsignment.logDir=/path/to/logs -Dopenconsignment.logSize="20 MB" ...
```

Configure these options in the launch script when running the packaged JAR.
Use a separate log directory for each simultaneous application instance;
the rolling files are not coordinated across processes. Log files can contain
exception details, so restrict access and review them before sharing.

## Testing

```shell
Expand Down
20 changes: 13 additions & 7 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ dependencies {
implementation(libs.controlsfx)
implementation(libs.jasperreports)
implementation(libs.sqlite)
implementation(libs.olap4j)
implementation(libs.olap4j)
implementation(libs.slf4j.api)
runtimeOnly(libs.log4j.slf4j2.impl)
runtimeOnly(libs.log4j.core)
// implementation(libs.itext)

implementation(libs.dagger)
Expand Down Expand Up @@ -50,9 +53,11 @@ javafx {
modules = listOf("javafx.controls", "javafx.fxml", "javafx.graphics", "javafx.base")
}

tasks.test {
useJUnitPlatform()
finalizedBy(tasks.jacocoTestReport)
tasks.test {
useJUnitPlatform()
finalizedBy(tasks.jacocoTestReport)
systemProperty("logging.test.classpath", sourceSets.test.get().runtimeClasspath.asPath)
providers.gradleProperty("loggingTestJar").orNull?.let { systemProperty("logging.test.jar", file(it).absolutePath) }

// Keep UI tests independent of desktop focus and native mouse permissions.
// Use -PheadlessTests=false when debugging tests with visible windows.
Expand Down Expand Up @@ -106,9 +111,10 @@ tasks.named<JavaExec>("run") {
)
}

tasks.named<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("shadowJar") {
archiveClassifier.set("")
}
tasks.named<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("shadowJar") {
archiveClassifier.set("")
mergeServiceFiles()
}

tasks.named("distZip") { enabled = false }
tasks.named("distTar") { enabled = false }
Expand Down
11 changes: 8 additions & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ monocle = "17.0.10"
hamcrest = "2.2"
#junit = "5.10.1"
junitbom = "5.10.2"
mockito = "5.14.2"
mockito = "5.14.2"
slf4j = "2.0.17"
log4j = "2.24.3"

spotless = "7.0.2"
shadow = "7.1.2"
Expand All @@ -23,7 +25,10 @@ javafxPlugin = "0.1.0"
controlsfx = { module = "org.controlsfx:controlsfx", version.ref = "controlsfx" }
jasperreports = { module = "net.sf.jasperreports:jasperreports", version.ref = "jasperreports" }
sqlite = { module = "org.xerial:sqlite-jdbc", version.ref = "sqlite" }
olap4j = { module = "org.olap4j:olap4j", version.ref = "olap4j" }
olap4j = { module = "org.olap4j:olap4j", version.ref = "olap4j" }
slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
log4j-slf4j2-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version.ref = "log4j" }
log4j-core = { module = "org.apache.logging.log4j:log4j-core", version.ref = "log4j" }
#itext = { module = "com.lowagie:itext", version.ref = "itext" }

javafx-controls = { module = "org.openjfx:javafx-controls", version.ref = "javafx" }
Expand Down Expand Up @@ -55,4 +60,4 @@ mockito-junit-jupiter = { module = "org.mockito:mockito-junit-jupiter", version.
javafx = { id = "org.openjfx.javafxplugin", version.ref = "javafxPlugin" }
spotless = { id = "com.diffplug.spotless", version.ref = "spotless" }
#shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }
shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" }
shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" }
8 changes: 5 additions & 3 deletions src/main/java/openconsignment/Launcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import java.io.InputStream;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
Expand All @@ -13,8 +11,11 @@
import openconsignment.common.Utils;
import openconsignment.di.AppComponent;
import openconsignment.di.DaggerAppComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Launcher extends Application {
private static final Logger logger = LoggerFactory.getLogger(Launcher.class);

public static void main(String[] args) {
launch(args);
Expand All @@ -41,9 +42,10 @@ public void start(Stage stage) {
stage.getIcons().add(new Image(resourceAsStream));
}
stage.setTitle("OpenConsignment");
logger.info("OpenConsignment started at {}", java.time.LocalDateTime.now());
} catch (Exception ex) {
Utils.showAlert(ex.toString());
Logger.getLogger(Launcher.class.getName()).log(Level.SEVERE, ex.toString(), ex);
logger.error(ex.toString(), ex);
}
}
}
38 changes: 10 additions & 28 deletions src/main/java/openconsignment/common/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,41 +11,23 @@
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Utils {

private static final Logger logger = Logger.getLogger(Utils.class.getName());
private static final Logger logger = LoggerFactory.getLogger(Utils.class);

public static void showAlert(String message) {
Alert alert = new Alert(AlertType.ERROR);
alert.setContentText(message);
alert.show();
}

private static AlertType getAlertTypeFromCustomValue(int choice) {
switch (choice) {
case 0:
return AlertType.ERROR;
case 1:
return AlertType.INFORMATION;
case 2:
return AlertType.WARNING;
case 3:
return AlertType.CONFIRMATION;
default:
showAlert("Programming Error. Select 1->error 2->info 3->warn 4->confirm");
}
return AlertType.NONE;
showAlert(message, AlertType.ERROR);
}

public static void showAlert(String message, int choice) {
Alert alert = new Alert(getAlertTypeFromCustomValue(choice), message);
public static void showAlert(String message, AlertType alertType) {
Alert alert = new Alert(alertType, message);
alert.show();
}

Expand All @@ -64,7 +46,7 @@ public static void launchPdf(String filename) throws IOException {
try {
Desktop.getDesktop().open(pdfFile);
} catch (IOException e) {
logger.log(Level.WARNING, "Error opening file: " + pdfFile.getAbsolutePath(), e);
logger.warn("Error opening file: {}", pdfFile.getAbsolutePath(), e);
}
});
}
Expand All @@ -78,7 +60,7 @@ public static void launchBrowser(String url) throws IOException {
try {
Desktop.getDesktop().browse(new URI(url));
} catch (IOException | URISyntaxException e) {
logger.log(Level.WARNING, "Error opening browser link " + url, e);
logger.warn("Error opening browser link {}", url, e);
}
});
}
Expand All @@ -95,7 +77,7 @@ public static String formatDate(String dateString) {
LocalDate.parse(dateString, outputFormatter);
return dateString;
} catch (DateTimeParseException ex2) {
logger.warning(ex2.toString());
logger.warn(ex2.toString());
throw ex2;
}
}
Expand All @@ -118,7 +100,7 @@ public static void generatePdf(String jrxmlFileName, Map<String, Object> payload
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, payload, dataSource);
JasperExportManager.exportReportToPdfFile(jasperPrint, Constants.REPORT_FILE_NAME);
} catch (JRException ex) {
Logger.getLogger(Utils.class.getName()).log(Level.SEVERE, ex.toString(), ex);
logger.error(ex.toString(), ex);
throw ex;
}
}
Expand Down
11 changes: 5 additions & 6 deletions src/main/java/openconsignment/common/ViewLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@

import java.io.IOException;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.fxml.FXMLLoader;
import javafx.scene.Group;
import javafx.scene.Parent;
import javafx.scene.layout.BorderPane;
import javafx.util.Callback;
import javax.inject.Inject;
import openconsignment.controller.MainController;
import openconsignment.controller.entry.EntryController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ViewLoader {
private static final Logger logger = LoggerFactory.getLogger(ViewLoader.class);

private final Callback<Class<?>, Object> controllerFactory;

Expand All @@ -30,7 +29,7 @@ public void loadRoot(String resourcePath, Group root) {
root.getChildren().setAll(parent);
} catch (IOException ex) {
Utils.showAlert(ex.toString());
Logger.getLogger(EntryController.class.getName()).log(Level.SEVERE, ex.toString(), ex);
logger.error(ex.toString(), ex);
}
}

Expand All @@ -42,7 +41,7 @@ public void loadCenter(String resourcePath, BorderPane center) {
center.setCenter(parent);
} catch (IOException ex) {
Utils.showAlert(ex.toString());
Logger.getLogger(MainController.class.getName()).log(Level.SEVERE, ex.toString(), ex);
logger.error(ex.toString(), ex);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
Expand All @@ -13,8 +11,11 @@
import javax.inject.Inject;
import openconsignment.common.Constants;
import openconsignment.common.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class WelcomeScreenController implements Initializable {
private static final Logger logger = LoggerFactory.getLogger(WelcomeScreenController.class);

@FXML
private Text organization_name;
Expand All @@ -31,7 +32,7 @@ private void handleOpenGithub(ActionEvent event) {
Utils.launchBrowser(url);
} catch (IOException ex) {
Utils.showAlert(ex.toString());
Logger.getLogger(WelcomeScreenController.class.getName()).log(Level.SEVERE, ex.toString(), ex);
logger.error(ex.toString(), ex);
}
}

Expand Down
Loading
Loading