Start learning free

LESSON 142 OF 173 · BACKEND: TESTS AND RUNNING IT

A Request ID in Java

Practice

When many customers order at the same time, their log lines get mixed together. When one of them writes that their order went wrong, you can't tell which lines are theirs. The fix is a request ID: a random name made for each request, sent back with the answer in a header and written in every log line about that request. The customer gives you the id, and you search the log for it.

Spring can run code around every request with a filter: a class that extends OncePerRequestFilter, marked @Component. Its doFilterInternal gets the request and the answer, and chain.doFilter(request, response) passes the request on to the route. Values that belong to the request being handled go in a request attribute: each request has its own. For example, to remember when each request started:

@Component
class TimingFilter extends OncePerRequestFilter {
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        request.setAttribute("started", System.currentTimeMillis());
        chain.doFilter(request, response);
    }
}

A route reads it with request.getAttribute("started") until that answer is sent. For the id itself, UUID.randomUUID().toString() makes 36 random characters of 0–9, a–f and dashes, practically never the same twice. A test can't know a random id in advance, so it checks a part: assertTrue(text.contains(part)) passes when part is found inside text.

The check sends four requests. The ids are random, so it shows them as <id 1>, <id 2> and so on:

POST /orders {"product_id": 1, "quantity": 2}  →  201 | X-Request-Id: <id 1>
  log: INFO shop.ShopController - [<id 1>] Order placed: 2 x Mug, total 24
POST /orders {"product_id": 3, "quantity": 1}  →  201 | X-Request-Id: <id 2>
  log: INFO shop.ShopController - [<id 2>] Order placed: 1 x Lamp, total 29
POST /orders {"product_id": 99, "quantity": 1}  →  404 | X-Request-Id: <id 3>
GET /about  →  404 | X-Request-Id: <id 4>

Your task: write a filter that makes an id for every request, keeps it on the request and sends it back in the X-Request-Id header, and write the id in square brackets at the start of the order's log line. Then write testLogHasRequestId, which checks that the header's id is in the log line. The check runs it on the shop, where it must pass, and on two broken copies, one that leaves the id out of the log and one that makes a second id for the answer, where it must fail.


Assignment
Follow the Coding Tutorial to give every request an id, put it in the answer's header and in the order's log line, and test that the two match.


Hint
Keep the id with request.setAttribute("requestId", …), so placeOrder can read it with request.getAttribute. Set the header before chain.doFilter: once the route has answered, it is too late for headers. In the test, logs.list holds the lines logged while it runs.


STEP 1 OF 12
    Ctrl + Enter to check

    Log Request Id in Java

    Keep going with Java1,600+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free