Added better logging to the system.

Signed-off-by: Cliff Hill <xlorep@darkhelm.org>
This commit is contained in:
2025-08-29 10:12:41 -04:00
parent dc5eb06ce1
commit 3ed8005679
9 changed files with 92 additions and 19 deletions
+1
View File
@@ -35,6 +35,7 @@ services:
service: frontend
environment:
REACT_APP_API_URL: http://localhost:8000
REACT_APP_LOG_LEVEL: debug
volumes:
- ./frontend/src:/app/src
- ./frontend/public:/app/public
+1
View File
@@ -24,6 +24,7 @@ services:
dockerfile: Dockerfile
environment:
REACT_APP_API_URL: http://localhost:8000
REACT_APP_LOG_LEVEL: warning
ports:
- "3000:3000"
depends_on:
+1
View File
@@ -21,6 +21,7 @@
"@types/react-router-dom": "^5.3.3",
"axios": "^1.11.0",
"date-fns": "^4.1.0",
"loglevel": "^1.9.2",
"react": "^19.1.1",
"react-big-calendar": "^1.19.4",
"react-dom": "^19.1.1",
+3
View File
@@ -1,9 +1,12 @@
import { logger } from "./utils/logger";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import LandingPage from "./pages/LandingPage";
import BookingPage from "./pages/BookingPage";
const App: React.FC = () => {
// Log when App mounts
logger.info("[App] Mounted");
return (
<Router>
<Routes>
+27 -4
View File
@@ -1,3 +1,4 @@
import { logger } from "../utils/logger";
// BookingForm.tsx
// Booking form for selecting room, date, time, title, and invitees
@@ -93,6 +94,9 @@ const BookingForm: React.FC<BookingFormProps> = ({
}
});
setErrors(newErrors);
if (Object.keys(newErrors).length > 0) {
logger.warn("[BookingForm] Validation failed", newErrors);
}
return Object.keys(newErrors).length === 0;
};
@@ -104,6 +108,12 @@ const BookingForm: React.FC<BookingFormProps> = ({
let bookingId = isEdit ? editBooking.id : undefined;
let bookingRes;
if (!isEdit) {
logger.info("[BookingForm] Creating new booking", {
roomId,
start,
end,
title,
});
bookingRes = await axios.post("/bookings/", {
room_id: roomId,
start,
@@ -111,14 +121,23 @@ const BookingForm: React.FC<BookingFormProps> = ({
title: title || undefined,
});
bookingId = bookingRes.data.id;
logger.info("[BookingForm] Booking created", bookingRes.data);
} else {
// Update booking details
logger.info("[BookingForm] Updating booking", {
id: editBooking.id,
roomId,
start,
end,
title,
});
await axios.put(`/bookings/${editBooking.id}`, {
room_id: roomId,
start,
end,
title: title || undefined,
});
logger.info("[BookingForm] Booking updated", { id: editBooking.id });
}
// For edit: remove all old invitees first
if (isEdit) {
@@ -128,6 +147,7 @@ const BookingForm: React.FC<BookingFormProps> = ({
data: { user_email: inv.user_email },
});
}
logger.debug("[BookingForm] Old invitees removed", oldInvitees.data);
}
// Add invitees
for (const email of invitees) {
@@ -136,6 +156,7 @@ const BookingForm: React.FC<BookingFormProps> = ({
user_email: email,
});
}
logger.info("[BookingForm] Invitees added", invitees);
alert(
isEdit
? "Booking updated successfully!"
@@ -156,8 +177,10 @@ const BookingForm: React.FC<BookingFormProps> = ({
setSubmitError(
"Booking times overlap with an existing booking for this room. Please choose a different time."
);
logger.warn("[BookingForm] Booking overlap error", detail);
} else {
setSubmitError("Failed to submit booking: " + detail);
logger.error("[BookingForm] Booking submit error", detail);
}
}
};
@@ -166,6 +189,7 @@ const BookingForm: React.FC<BookingFormProps> = ({
if (!isEdit) return;
if (!window.confirm("Delete this booking?")) return;
try {
logger.info("[BookingForm] Deleting booking", { id: editBooking.id });
await axios.delete(`/bookings/${editBooking.id}`);
alert("Booking deleted.");
if (onBookingSuccess) {
@@ -174,10 +198,9 @@ const BookingForm: React.FC<BookingFormProps> = ({
onClose();
}
} catch (err: any) {
alert(
"Failed to delete booking: " +
(err?.response?.data?.detail || err.message)
);
const detail = err?.response?.data?.detail || err.message;
alert("Failed to delete booking: " + detail);
logger.error("[BookingForm] Booking delete error", detail);
}
};
+8 -2
View File
@@ -1,3 +1,4 @@
import { logger } from "../utils/logger";
/**
* Displays a list of bookings for the current day in a Material-UI Card.
*
@@ -61,6 +62,7 @@ const BookingList: FC<BookingListProps> = ({
// Keep liveBookings in sync with prop changes
useEffect(() => {
setLiveBookings(bookings);
logger.debug("[BookingList] Bookings prop changed", bookings);
}, [bookings]);
useEffect(() => {
@@ -71,6 +73,7 @@ const BookingList: FC<BookingListProps> = ({
typeof window.EventSource !== "undefined" &&
(enableSSE || process.env.NODE_ENV !== "test");
if (!sseAllowed) {
logger.warn("[BookingList] SSE not enabled or supported");
return;
}
// Close any previous EventSource
@@ -92,15 +95,18 @@ const BookingList: FC<BookingListProps> = ({
);
}
setLiveBookings(filtered);
logger.debug("[BookingList] SSE update received", filtered);
} catch (e) {
// Ignore parse errors
logger.warn("[BookingList] Failed to parse SSE data", e);
}
};
es.onerror = () => {
es.onerror = (err) => {
es.close();
logger.error("[BookingList] SSE connection error", err);
};
return () => {
es.close();
logger.info("[BookingList] SSE connection closed");
};
}, [roomId, date, enableSSE]);
+29 -4
View File
@@ -1,3 +1,4 @@
import { logger } from "../utils/logger";
// BookingPage.tsx
// Booking page for conference room system
// See README for requirements
@@ -25,6 +26,11 @@ import {
* @component
*/
const BookingPage: React.FC = () => {
// Log mount
useEffect(() => {
logger.info("[BookingPage] Mounted");
return () => logger.info("[BookingPage] Unmounted");
}, []);
// Fetch all rooms
const {
data: rooms = [],
@@ -59,20 +65,30 @@ const BookingPage: React.FC = () => {
try {
const res = await axios.get(`/bookings/room/${room.id}`);
results[room.id] = res.data;
logger.debug(
`[BookingPage] Bookings fetched for room ${room.id}`,
res.data
);
} catch (e) {
results[room.id] = [];
logger.warn(
`[BookingPage] Failed to fetch bookings for room ${room.id}`
);
}
})
);
if (!cancelled) setBookingsByRoom(results);
logger.info("[BookingPage] All bookings fetched and set");
} catch (err) {
if (!cancelled) setBookingsError(err);
logger.error("[BookingPage] Error fetching bookings", err);
} finally {
if (!cancelled) setBookingsLoading(false);
}
}
if (rooms.length > 0) {
fetchBookingsAndSet();
logger.info("[BookingPage] Fetching bookings for all rooms");
// Fallback: If EventSource/SSE is not supported, use polling
const sseAllowed =
typeof window !== "undefined" &&
@@ -92,18 +108,22 @@ const BookingPage: React.FC = () => {
grouped[b.roomId].push(b);
});
setBookingsByRoom(grouped);
logger.debug("[BookingPage] SSE update received", grouped);
}
} catch (e) {
// Ignore parse errors
logger.warn("[BookingPage] Failed to parse SSE data", e);
}
};
es.onerror = () => {
es.onerror = (err) => {
es.close();
logger.error("[BookingPage] SSE connection error", err);
};
} else {
setUsingPollingFallback(true);
logger.warn("[BookingPage] SSE not supported, falling back to polling");
// Poll every 10 seconds if SSE not available
pollInterval = setInterval(() => {
logger.info("[BookingPage] Polling for bookings update");
fetchBookingsAndSet();
}, 10000);
}
@@ -139,23 +159,28 @@ const BookingPage: React.FC = () => {
const [selectedRoomId, setSelectedRoomId] = useState<any>(rooms[0]?.id || "");
const handleSelectSlot = (slotInfo: any) => {
logger.debug("[BookingPage] Slot selected", slotInfo);
setFormSlot(slotInfo);
setEditBooking(null);
setFormOpen(true);
};
const handleSelectEvent = (event: CalendarEvent) => {
// Open form in edit mode
logger.debug("[BookingPage] Event selected for edit", event);
setEditBooking(event);
setFormSlot(null);
setFormOpen(true);
};
const handleFormClose = (refresh = false) => {
logger.info("[BookingPage] Booking form closed", { refresh });
setFormOpen(false);
setFormSlot(null);
setEditBooking(null);
if (refresh) refetchBookings();
if (refresh) {
logger.info("[BookingPage] Refetching bookings after form close");
refetchBookings();
}
};
// (removed old handleSelectEvent)
+17 -9
View File
@@ -1,3 +1,4 @@
import { logger } from "../utils/logger";
/**
* Main entry point for the conference room booking system.
* Displays available rooms, today's bookings, and a navigation button to the booking page.
@@ -27,11 +28,11 @@ import BookingList, { Booking } from "../components/BookingList";
* @returns {JSX.Element} The landing page UI.
*/
const LandingPage: FC = () => {
// Debug: log when LandingPage mounts
// Log when LandingPage mounts/unmounts
useEffect(() => {
// ...removed debug log...
logger.info("[LandingPage] Mounted");
return () => {
// ...removed debug log...
logger.info("[LandingPage] Unmounted");
};
}, []);
@@ -54,8 +55,13 @@ const LandingPage: FC = () => {
refetch: refetchRooms,
} = useQuery({
queryKey: ["rooms"],
queryFn: () =>
axios.get("/rooms/").then((res) => res.data as ConferenceRoom[]),
queryFn: () => {
logger.info("[LandingPage] Fetching rooms");
return axios.get("/rooms/").then((res) => {
logger.debug("[LandingPage] Rooms fetched", res.data);
return res.data as ConferenceRoom[];
});
},
});
// Room selection state
@@ -80,15 +86,15 @@ const LandingPage: FC = () => {
queryKey: ["bookings", today, selectedRoomId],
enabled: !!selectedRoomId,
queryFn: () => {
// ...removed debug log...
logger.info("[LandingPage] Fetching bookings", { today, selectedRoomId });
return axios
.get(`/bookings/room/${selectedRoomId}?date=${today}`)
.then((res) => {
// ...removed debug log...
logger.debug("[LandingPage] Bookings fetched", res.data);
return res.data as Booking[];
})
.catch((err) => {
// ...removed debug error log...
logger.error("[LandingPage] Failed to fetch bookings", err);
throw err;
});
},
@@ -140,7 +146,9 @@ const LandingPage: FC = () => {
// Display error message with retry option
if (roomsError || bookingsError) {
// ...removed debug error log...
if (roomsError) logger.error("[LandingPage] Rooms error", roomsError);
if (bookingsError)
logger.error("[LandingPage] Bookings error", bookingsError);
return (
<div
className="landing-root"
+5
View File
@@ -7743,6 +7743,11 @@ lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
loglevel@^1.9.2:
version "1.9.2"
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.9.2.tgz#c2e028d6c757720107df4e64508530db6621ba08"
integrity sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==
loose-envify@^1.0.0, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"