feat(client/logging): client-side info logging

This commit is contained in:
Zhongheng Liu 2026-01-08 16:32:59 +01:00
commit e91d6355f0
Signed by: steven
GPG key ID: F69B980899C1C09D
8 changed files with 53 additions and 16 deletions

View file

@ -15,8 +15,17 @@
*/ */
package client; package client;
import org.slf4j.bridge.SLF4JBridgeHandler;
public class Main { public class Main {
static {
// Choose SLF4J Logger (Spring Boot) for JavaFX.
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
}
public static void main(String[] args){ public static void main(String[] args){
UI.launch(UI.class, args); UI.launch(UI.class, args);
} }
} }

View file

@ -6,6 +6,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.logging.Logger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import client.exception.InvalidModificationException; import client.exception.InvalidModificationException;
@ -45,7 +46,7 @@ public class FoodpalApplicationCtrl implements LocaleAware {
private final WebSocketUtils webSocketUtils; private final WebSocketUtils webSocketUtils;
private final LocaleManager localeManager; private final LocaleManager localeManager;
private final WebSocketDataService<Long, Recipe> dataService; private final WebSocketDataService<Long, Recipe> dataService;
private final Logger logger = Logger.getLogger(FoodpalApplicationCtrl.class.getName());
@FXML @FXML
private RecipeDetailCtrl recipeDetailController; private RecipeDetailCtrl recipeDetailController;
@ -93,24 +94,31 @@ public class FoodpalApplicationCtrl implements LocaleAware {
this.configService = configService; this.configService = configService;
this.dataService = recipeDataService; this.dataService = recipeDataService;
setupDataService(); setupDataService();
logger.info("WebSocket processor initialized.");
initializeWebSocket(); initializeWebSocket();
logger.info("WebSocket connection handler initialized.");
logger.info("Main application controller initialized.");
} }
private void setupDataService() { private void setupDataService() {
dataService.setMessageParser((msg) -> switch (msg) { dataService.setMessageParser((msg) -> switch (msg) {
case CreateRecipeMessage _ -> (m) -> { case CreateRecipeMessage _ -> (m) -> {
CreateRecipeMessage crm = (CreateRecipeMessage) m; CreateRecipeMessage crm = (CreateRecipeMessage) m;
logger.info("Server informs us of creation of recipe: " + crm.getRecipe());
return handleCreateRecipeMessage(crm); return handleCreateRecipeMessage(crm);
}; };
case UpdateRecipeMessage _ -> (m) -> { case UpdateRecipeMessage _ -> (m) -> {
UpdateRecipeMessage urm = (UpdateRecipeMessage) m; UpdateRecipeMessage urm = (UpdateRecipeMessage) m;
logger.info("Server informs us of update for recipe: " + urm.getRecipe());
return handleUpdateRecipeMessage(urm); return handleUpdateRecipeMessage(urm);
}; };
case DeleteRecipeMessage _ -> (m) -> { case DeleteRecipeMessage _ -> (m) -> {
DeleteRecipeMessage drm = (DeleteRecipeMessage) m; DeleteRecipeMessage drm = (DeleteRecipeMessage) m;
logger.info("Server informs us of the deletion of recipe with ID: " + drm.getRecipeId());
return handleDeleteRecipeMessage(drm); return handleDeleteRecipeMessage(drm);
}; };
case FavouriteRecipeMessage _ -> (m) -> { case FavouriteRecipeMessage _ -> (m) -> {
FavouriteRecipeMessage frm = (FavouriteRecipeMessage) m; FavouriteRecipeMessage frm = (FavouriteRecipeMessage) m;
logger.info("Server informs us of a favourite recipe being deleted: " + frm.getRecipeId());
return handleFavouriteRecipeMessage(frm); return handleFavouriteRecipeMessage(frm);
}; };
default -> throw new IllegalStateException("Unexpected value: " + msg); default -> throw new IllegalStateException("Unexpected value: " + msg);
@ -185,7 +193,7 @@ public class FoodpalApplicationCtrl implements LocaleAware {
this.recipeList.getItems().setAll(recipes); this.recipeList.getItems().setAll(recipes);
System.out.println("Search returned " + recipes.size() + " recipes."); logger.info("Search returned " + recipes.size() + " recipes.");
// Restore selection, if possible // Restore selection, if possible
if (newIndex != -1) { if (newIndex != -1) {
@ -263,7 +271,9 @@ public class FoodpalApplicationCtrl implements LocaleAware {
recipes = server.getRecipesFiltered(searchBarController.getFilter()); recipes = server.getRecipesFiltered(searchBarController.getFilter());
} catch (IOException | InterruptedException e) { } catch (IOException | InterruptedException e) {
recipes = Collections.emptyList(); recipes = Collections.emptyList();
System.err.println("Failed to load recipes: " + e.getMessage()); String msg = "Failed to load recipes: " + e.getMessage();
logger.severe(msg);
printError(msg);
} }
allRecipes = new ArrayList<>(recipes); allRecipes = new ArrayList<>(recipes);
@ -295,7 +305,10 @@ public class FoodpalApplicationCtrl implements LocaleAware {
dataService.add(newRecipe.getId(), recipe -> { dataService.add(newRecipe.getId(), recipe -> {
this.recipeList.getSelectionModel().select(recipe); this.recipeList.getSelectionModel().select(recipe);
openSelectedRecipe(); openSelectedRecipe();
Platform.runLater(() -> this.recipeDetailController.editRecipeTitle()); Platform.runLater(() -> {
logger.info("Focused recipe title edit box.");
this.recipeDetailController.editRecipeTitle();
});
}); });
recipeList.refresh(); recipeList.refresh();
} catch (IOException | InterruptedException e) { } catch (IOException | InterruptedException e) {

View file

@ -15,6 +15,7 @@ import javafx.util.StringConverter;
import java.io.InputStream; import java.io.InputStream;
import java.util.Locale; import java.util.Locale;
import java.util.logging.Logger;
/** /**
* The language selection menu controller. * The language selection menu controller.
@ -22,6 +23,7 @@ import java.util.Locale;
* <code>getLocaleString(String)</code> function is available. * <code>getLocaleString(String)</code> function is available.
*/ */
public class LangSelectMenuCtrl implements LocaleAware { public class LangSelectMenuCtrl implements LocaleAware {
private final Logger logger = Logger.getLogger(LangSelectMenuCtrl.class.getName());
public ComboBox<String> langSelectMenu; public ComboBox<String> langSelectMenu;
private final LocaleManager manager; private final LocaleManager manager;
@ -37,6 +39,7 @@ public class LangSelectMenuCtrl implements LocaleAware {
@FXML @FXML
private void switchLocale(ActionEvent event) { private void switchLocale(ActionEvent event) {
String lang = langSelectMenu.getSelectionModel().getSelectedItem(); String lang = langSelectMenu.getSelectionModel().getSelectedItem();
logger.info("Switching locale to " + lang);
manager.setLocale(Locale.of(lang)); manager.setLocale(Locale.of(lang));
} }

View file

@ -207,7 +207,7 @@ public class RecipeDetailCtrl implements LocaleAware {
try { try {
server.updateRecipe(this.recipe); server.updateRecipe(this.recipe);
this.refresh(); // this.refresh();
} catch (IOException | InterruptedException e) { } catch (IOException | InterruptedException e) {
// throw a nice blanket UpdateException // throw a nice blanket UpdateException
throw new UpdateException("Error occurred when updating recipe name!"); throw new UpdateException("Error occurred when updating recipe name!");

View file

@ -7,10 +7,12 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import java.nio.file.Path; import java.nio.file.Path;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.logging.Logger;
public class ConfigService { public class ConfigService {
private final Path configPath; private final Path configPath;
private final ObjectMapper mapper = new ObjectMapper(); private final ObjectMapper mapper = new ObjectMapper();
private final Logger logger = Logger.getLogger(ConfigService.class.getName());
private Config config; private Config config;
@ -70,9 +72,10 @@ public class ConfigService {
try { try {
File file = configPath.toFile(); // file is the config file here File file = configPath.toFile(); // file is the config file here
mapper.writeValue(file, config); // here we edit the value of the file using config mapper.writeValue(file, config); // here we edit the value of the file using config
logger.info("Config saved to " + file.getAbsolutePath());
} }
catch (Exception e) { catch (Exception e) {
logger.severe("Something bad happened while saving the config: " + e.getMessage());
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }

View file

@ -18,12 +18,14 @@ import java.net.http.HttpRequest;
import java.net.http.HttpResponse; import java.net.http.HttpResponse;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.logging.Logger;
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
public class ServerUtils { public class ServerUtils {
private static final String SERVER = "http://localhost:8080/api"; private static final String SERVER = "http://localhost:8080/api";
private Logger logger = Logger.getLogger(ServerUtils.class.getName());
private final HttpClient client; private final HttpClient client;
private final ObjectMapper objectMapper = new ObjectMapper(); private final ObjectMapper objectMapper = new ObjectMapper();
private final int statusOK = 200; private final int statusOK = 200;
@ -48,9 +50,10 @@ public class ServerUtils {
throw new IOException("No recipe to get. Server responds with " + response.body()); throw new IOException("No recipe to get. Server responds with " + response.body());
} }
List<Recipe> list = objectMapper.readValue(response.body(), new TypeReference<List<Recipe>>() {
return objectMapper.readValue(response.body(), new TypeReference<List<Recipe>>() { });
});// JSON string-> List<Recipe> (Jackson) logger.info("Received response from server: " + list);
return list; // JSON string-> List<Recipe> (Jackson)
} }
public List<Recipe> getRecipesFiltered(String filter) throws IOException, InterruptedException { public List<Recipe> getRecipesFiltered(String filter) throws IOException, InterruptedException {

View file

@ -10,8 +10,10 @@ import java.util.Map;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.logging.Logger;
public class WebSocketDataService<ID, Value> { public class WebSocketDataService<ID, Value> {
private Logger logger = Logger.getLogger(WebSocketDataService.class.getName());
public WebSocketDataService() { public WebSocketDataService() {
} }
@ -49,7 +51,11 @@ public class WebSocketDataService<ID, Value> {
*/ */
public boolean add(ID id, Consumer<Value> onComplete) { public boolean add(ID id, Consumer<Value> onComplete) {
CompletableFuture<Value> future = new CompletableFuture<>(); CompletableFuture<Value> future = new CompletableFuture<>();
future.thenAccept(onComplete.andThen(_ -> pendingRegister.remove(id))); future.thenAccept(onComplete.andThen(_ -> {
logger.info("Item " + id + " resolved. Removing from pending register.");
pendingRegister.remove(id);
}));
logger.info("Item " + id + " pending propagation. Adding to pending register.");
return pendingRegister.putIfAbsent(id, future) == null; return pendingRegister.putIfAbsent(id, future) == null;
} }
public boolean add(ID id) { public boolean add(ID id) {

View file

@ -14,11 +14,13 @@ import java.lang.reflect.Type;
import java.util.function.Consumer; import java.util.function.Consumer;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;
public class WebSocketUtils { public class WebSocketUtils {
private static final String WS_URL = "ws://localhost:8080/updates"; private static final String WS_URL = "ws://localhost:8080/updates";
private WebSocketStompClient stompClient; private WebSocketStompClient stompClient;
private StompSession stompSession; private StompSession stompSession;
private Logger logger = Logger.getLogger(WebSocketUtils.class.getName());
/** /**
* Connect to the websocket server. * Connect to the websocket server.
@ -36,21 +38,19 @@ public class WebSocketUtils {
@Override @Override
public void afterConnected(StompSession session, StompHeaders connectedHeaders) { public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
stompSession = session; stompSession = session;
System.out.println("WebSocket connected: " + session.getSessionId()); logger.info("WebSocket connected with session ID: " + session.getSessionId());
} }
@Override @Override
public void handleException(StompSession session, @Nullable StompCommand command, public void handleException(StompSession session, @Nullable StompCommand command,
StompHeaders headers, byte[] payload, StompHeaders headers, byte[] payload,
Throwable exception) { Throwable exception) {
System.err.println("STOMP error: " + exception.getMessage()); logger.severe("STOMP error: " + exception.getMessage());
exception.printStackTrace();
} }
@Override @Override
public void handleTransportError(StompSession session, Throwable exception) { public void handleTransportError(StompSession session, Throwable exception) {
System.err.println("STOMP transport error: " + exception.getMessage()); logger.severe("STOMP transport error: " + exception.getMessage());
exception.printStackTrace();
} }
} }
); );