Business Event Logging
Logs that answer business questions
Request logs tell you: “GET /v2/books returned 200 in 12ms.” Business event logs tell you: “User alice registered via email at 2:30 PM.” “Order #99 was placed for $29.99 with 3 items.” “Book ‘Kindred’ received a 5-star review.”
These answer questions that request logs cannot: How many users registered today? What is the average order value this week? Which books are most reviewed?
The business event pattern
A business event log has a consistent structure:
function logEvent(log: Logger, event: string, data: Record<string, unknown>): void {
log.info(event, { event, ...data });
} The event field is both the message and a searchable tag. Search for event: "order.placed" to find all orders.
Events in the book catalog
// User registered
logEvent(log, "user.registered", {
userId: user.id,
method: "email", // or "github", "google"
});
// Book created
logEvent(log, "book.created", {
bookId: book.id,
title: book.title,
genre: book.genre,
authorId: book.authorId,
});
// Review submitted
logEvent(log, "review.submitted", {
reviewId: review.id,
bookId: review.bookId,
userId: review.userId,
rating: review.rating,
});
// Book deleted
logEvent(log, "book.deleted", {
bookId: book.id,
deletedBy: userId,
reason: "admin action",
}); Each event includes the entities involved (IDs), the relevant data (title, rating, amount), and who triggered it (userId).
Using events in route handlers
route.post("/v2/books/:bookId/reviews", {
request: { body: CreateReviewSchema },
resolve: (c) => {
if (!c.input.ok) {
return Response.json({ error: "Validation failed" }, { status: 400 });
}
const review = createReview({
bookId: c.input.params.bookId,
userId: c.locals.userId,
rating: c.input.body.rating,
body: c.input.body.body,
});
logEvent(c.locals.log, "review.submitted", {
reviewId: review.id,
bookId: review.bookId,
rating: review.rating,
});
return Response.json(review, { status: 201 });
},
}); The request log says: POST /v2/books/book-1/reviews → 201 in 35ms. The business event says: review.submitted, bookId=book-1, rating=5, userId=user-42. Both are valuable. Together they tell the complete story.
Event naming conventions
Use a consistent naming scheme: entity.action.
user.registered user.deleted user.password_changed
book.created book.updated book.deleted
review.submitted review.deleted
order.placed order.cancelled order.refunded Dot-separated, lowercase, past tense for the action. This makes events searchable by entity (user.*) or by action (*.deleted).
[!NOTE] The REST API Design course established resource naming conventions. Business event names follow the same pattern — consistent, predictable, and discoverable.
Exercises
Exercise 1: Add business event logging to three endpoints: create book, submit review, delete book. Use the logEvent helper.
Exercise 2: Search the logs for all review.submitted events. Count the total reviews and average rating.
Exercise 3: Design event names for a new feature (e.g., wishlists: wishlist.created, wishlist.item_added, wishlist.shared).
Why log business events separately from request logs?