CodingNic

Logging & Error Monitoring

Exercises

Logging & Error Monitoring 25 min read

Exercises

Objectives

By the end of this lesson, you should be able to:

  • Combine request logging, operational-error logging, and rate-limit logging on real auth routes
  • Distinguish, from log output alone, a validation failure, a bad login, and a blocked attack

⚠️ A note on verification: every command and every output in this lesson was actually run.

Exercise: Logging a Complete Auth Flow

a) Build the app, register and login, both logged, plus a rate limiter with its own logging:

javascript
const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 3,
  standardHeaders: true,
  legacyHeaders: false,
  handler: (req, res) => {
    req.log.warn({ status: 429 }, 'Rate limit exceeded on login');
    res.status(429).json({ error: 'TooManyRequests', message: 'Too many login attempts, try again later' });
  }
});

app.post('/api/v1/auth/register', async (req, res, next) => {
  const { email, password } = req.body;
  if (!email || !password) {
    return next(new AppError('email and password are required', 400));
  }
  const passwordHash = await bcrypt.hash(password, 10);
  users.push({ id: users.length + 1, email, passwordHash });
  req.log.info({ userId: users.length }, 'User registered');
  res.status(201).json({ email });
});

app.post('/api/v1/auth/login', loginLimiter, async (req, res, next) => {
  const { email, password } = req.body;
  const user = users.find(u => u.email === email);
  if (!user || !(await bcrypt.compare(password, user.passwordHash))) {
    return next(new AppError('Incorrect email or password', 401));
  }
  req.log.info({ userId: user.id }, 'User logged in');
  res.json({ success: true });
});

rateLimit’s handler option runs instead of the default response when a client is blocked, used here to log the block explicitly, at warn, exactly like any other operational error, being rate-limited is expected, routine behavior for the system, not a bug.

b) Run a sequence: register successfully, register with a missing password, then send four wrong-password login attempts in a row (max: 3 blocks the fourth):

text
User registered                                       level: info (30)
Operational error handled   status:400  "email and password are required"    level: warn (40)
Operational error handled   status:401  "Incorrect email or password"        level: warn (40)
Operational error handled   status:401  "Incorrect email or password"        level: warn (40)
Operational error handled   status:401  "Incorrect email or password"        level: warn (40)
Rate limit exceeded on login   status:429                                    level: warn (40)

Every line here is info or warn, nothing hits error, correctly, nothing in this sequence is an actual bug, a missing field, a wrong password, and a blocked attack are all expected, handled outcomes.

c) Confirm what error-level logging would look like. Trigger a genuinely unexpected error (an unmarked Error, not an AppError, thrown from inside a route), and confirm it logs at error (level: 50), with the full error object attached, distinct from every line in the sequence above.

d) Reflect. A team monitoring this application would reasonably configure alerts to fire only on error-level logs. Looking at the six log lines above, explain, in one or two sentences, why none of them should have paged anyone, even the rate-limited attack attempt.

Recap

This module replaced console.log with structured, leveled logging: Pino’s JSON output, pino-http tying logs to individual requests, and a consistent operational-versus-programmer distinction carried all the way from Node.js & Express Foundations into what gets logged at warn versus error. Combined with Module 7 and 8’s automated tests, this course’s API now has both correctness checks and production visibility, not just code that happens to work when run by hand.

Next module: the capstone, bringing every module in this course, hashing, sessions or JWTs, roles, validation, hardening, testing, and logging, together into one complete, secured, tested API.