In Week 11 you met the idea of a health check: your hosting platform can ping an endpoint on a schedule, and only send traffic to your app while that endpoint answers. But answering is not the same as being healthy.
Ask yourself: if your database goes down, what should a health check say? Your app is still running, but every real request fails. A check that only proves "the process is alive" is a smoke detector without a battery — it never goes off, no matter what is burning. What you really want is a check that tests whether the app can do its job: reach its database, its dependencies, everything a real request needs.
That is exactly what Spring Boot Actuator gives you, for free.
Actuator is Spring Boot's standard module for exactly the signals this week is about. One dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Restart, then open http://localhost:8080/actuator/health:
{"status":"UP"}
It returns the same kind of UP your health check wants — but this one is telling the truth. Actuator discovers what your app depends on and checks it: because you have a datasource, the health status includes a real database check. To see the parts behind the verdict locally, add:
management:
endpoint:
health:
show-details: always
Now the response breaks down into components — db, diskSpace, ping — each with its own status. (In production you would use show-details: when-authorized instead, since the breakdown reveals your dependencies.)
<aside> ⌨️
Hands on: Run your app with Docker Compose, open /actuator/health, then stop the database container (docker compose stop db). Refresh: {"status":"DOWN"}, with the db component reporting the failure. Start the database again and watch it recover. Your health check finally has a battery.
</aside>
Now finish the upgrade in production:
/actuator/health.HealthController back in Week 11, delete it now — Actuator replaces it.Your platform now asks your app "are you really OK?" on every check — and during deploys it will only switch traffic to a container whose database connection actually works.
Actuator brought a second gift. Logs record events; metrics record measurements over time — how many requests, how fast, how much memory. Expose the metrics endpoint alongside health:
management:
endpoints:
web:
exposure:
include: health,metrics
Open http://localhost:8080/actuator/metrics — a catalogue of everything being measured. Two are worth a look right now:
/actuator/metrics/http.server.requests — hit a few of your API endpoints first, then open it: request counts and timing, straight from your own traffic. Nobody wrote code for this; Spring Boot measures every request automatically./actuator/metrics/jvm.memory.used — how much memory your app is using. Free hosting tiers are usually memory-limited (often around 512 MB); this number is why a deploy can fail with an out-of-memory error.<aside> ⚠️
Exposure discipline. By default Actuator exposes only health — which is safe. Here we add metrics too. But never use include: * in production: it opens endpoints like env and heapdump that leak configuration and full memory contents (an attacker's heap dump is essentially a data breach). In a real job, Actuator sits behind authentication or on a separate management port the public cannot reach. For our training apps, health,metrics is fine.
</aside>
Reading numbers from JSON is not how anyone monitors a system — dashboards are. And you already ship everything needed for a genuinely useful first panel: your logs carry a level label, so Grafana can count log lines by level over time — a live overview of how much INFO, WARN and ERROR your app produces. Those counts are metrics, derived from your logs.
app = my-app (your app name).level. This gives you one line per log level instead of a single total.You now see INFO, WARN and ERROR as separate lines over time — a healthy app is mostly INFO with ERROR flat at zero. Trigger one error in your app and watch the ERROR line twitch.

<aside> 🎉
Step back and look at what you built this week: an app that reports on itself, logs that survive anything, a health check that tells the truth, and a dashboard watching for trouble. That is a production service — and a screenshot worth having in your portfolio.
</aside>
<aside> 💡
Curious what "more" looks like? The optional Appendix: Alerting chapter turns this exact panel into an email that finds you when errors appear — no staring at dashboards required.
</aside>
One signal from the first chapter remains: traces. Here is why they exist — and why we only talk about them.
Your whole application is one service: a request enters your controller and every answer is found inside the same container. When something is slow, your logs and metrics point at the culprit, because there is only one suspect.
Now picture the systems you will meet at work: placing one order touches an order service, a payment service, a stock service, an email service — separate applications on separate machines, each with its own logs. The order service logs "payment took 4 seconds"… and the payment service logs a normal day. Who do you believe? Which of the four systems' logs do you even open first?
A trace answers that by following one request across every service it touches, recording how long each hop took — one timeline instead of four log files. The open standard that makes services emit these signals in a common format is called OpenTelemetry; you will see the name in job descriptions, and now you can say what problem it solves. Next week, when you study how systems are split into services, you will recognise exactly the moment traces become necessary.
The HackYourFuture curriculum is licensed under CC BY-NC-SA 4.0 *https://hackyourfuture.net/*

Built with ❤️ by the HackYourFuture community · Thank you, contributors
Found a mistake or have a suggestion? Let us know in the feedback form.