Week 13

Observability introduction

Logging in Spring Boot

Structured Logging

Shipping Logs to the Cloud

Debugging with Logs

Health & Metrics

Appendix: Alerting (optional)

Practice

Assignment

Backend Track

The detective method

Something is wrong in production. You cannot step through the code, but you have something a debugger never gives you: a recorded history of everything the app said. Working through that history is a method, not a talent, and it is the same four moves every time:

  1. Start from the symptom. What exactly did the user see, and roughly when? "Checkout gives an error since this afternoon" gives you a time window and a place to look.
  2. Narrow the window. In Grafana Explore, set the time range around the symptom and filter to your app. Fewer lines, more signal.
  3. Find the loudest evidence. Filter on level = ERROR. In a healthy app, errors are rare โ€” whatever you find now is almost certainly related.
  4. Read outward from the error. The error line tells you what happened. The lines just before it โ€” the INFO and WARN story โ€” tell you what the app was doing on the way there. Cause lives upstream of effect.

<aside> ๐Ÿ’ก

In Explore you need only three controls: the time range picker (top right), the label filters (app, env, level), and line contains for searching text inside messages โ€” an order ID, an endpoint path, a word from the error. That is enough to solve real incidents.

</aside>

Reading a stack trace

Most ERROR lines come with a stack trace โ€” and juniors often scroll past it because it looks like noise. It is the opposite: it is the machine telling you the exact file and line where things went wrong. Here is one, straight from a shipped log:

ERROR c.h.taskapp.TaskService - Failed to load task 42
java.util.NoSuchElementException: No value present
    at java.base/java.util.Optional.get(Optional.java:143)
    at com.hackyourfuture.taskapp.TaskService.getTask(TaskService.java:31)
    at com.hackyourfuture.taskapp.TaskController.getTask(TaskController.java:24)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(...)
    ... 47 more lines of framework code ...

How to read it:

So: TaskService line 31 calls Optional.get() on a task that does not exist โ€” someone requested task 42, and it is not in the database. You found this in a running production system without touching a debugger.

Let AI help โ€” the right way

A stack trace is a perfect thing to hand to an AI assistant: it is precise, self-contained, and contains no personal data (check before pasting). The assistant is genuinely good at decoding exception types and suggesting likely causes.

But notice what the assistant is missing: it can see the trace, not your production system. It might reply with three plausible causes โ€” "the ID does not exist", "the row was deleted", "a transaction issue". Which one is true here? Only the logs can say. So the professional loop is:

  1. Hypothesis: paste the trace (and the surrounding log lines), ask what could cause this.
  2. Verify: go back to Grafana and look for evidence. Is there an earlier line showing task 42 being deleted? Did the request come in with a strange ID? Do other task IDs work fine?
  3. Only then, fix. A fix based on an unverified guess is how you "fix" the wrong thing and ship a second incident.

<aside> โš ๏ธ

The assistant sounds equally confident when it is right and when it is wrong โ€” you learned this in the core program as hallucination. In debugging, the logs are your ground truth. AI proposes, evidence decides.

</aside>

Common logging mistakes

Now that you read logs under pressure, you will recognise these โ€” and stop writing them:

Practise the method

๐Ÿ’ฌ A user reports: "I got an error saving my task around 14:30." Here is what level = ERROR plus the minutes before show in Grafana. What is the root cause โ€” and which single log line proves it?

14:29:41 INFO  c.h.taskapp.TaskController - POST /api/tasks received
14:29:41 INFO  c.h.taskapp.TaskService    - Creating task with title=Weekly groceries for user 7
14:29:41 INFO  c.h.taskapp.TaskService    - Task 118 created by user 7
14:30:02 INFO  c.h.taskapp.TaskController - POST /api/tasks received
14:30:02 WARN  com.zaxxer.hikari.HikariPool - Connection is not available, request timed out after 30001ms
14:30:02 ERROR c.h.taskapp.TaskService    - Failed to save task for user 7
org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection
    at com.hackyourfuture.taskapp.TaskRepository.save(TaskRepository.java:27)
    ...

Extra resources


The HackYourFuture curriculum is licensed underย CC BY-NC-SA 4.0 *https://hackyourfuture.net/*

CC BY-NC-SA 4.0 Icons

Built with โค๏ธ by the HackYourFuture community ยท Thank you, contributors

Found a mistake or have a suggestion? Let us know in the feedback form.