hectoday
DocsCoursesChangelog GitHub
DocsCoursesChangelog GitHub

Access Required

Enter your access code to view courses.

Invalid code

← All courses Logging and Observability with @hectoday/http

Why Logging

  • console.log Is Not Enough
  • What to Log
  • Project Setup

Structured Logging

  • JSON Logs
  • Log Levels
  • Building a Logger

Request Logging

  • Request IDs
  • Request-Response Logging
  • Error Logging

Context and Correlation

  • Log Context
  • Child Loggers
  • Correlating Across Services

What to Observe

  • Business Event Logging
  • Performance Logging
  • Health and Metrics

Production

  • Log Output and Transport
  • Sensitive Data
  • Checklist and Capstone

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?

← Correlating Across Services Performance Logging →

© 2026 hectoday. All rights reserved.