Debug Around Timeouts with Logpoints

Other languages: EspañolFrançaisDeutsch日本語한국어Português中文

Modern development tools, IntelliJ IDEA especially, have so many debugging features that it takes a while to even remember what is available. For virtually any niche use case, there is a tool for the job.

In this article, I want to approach debugging from the other end and look at the basics. If you are new to debugging tools and want the biggest return on your learning investment, the one feature to start with is logpoints.

Logpoints are my favorite because even though they’re as simple as debugging with plain println statements (and arguably simpler), they vastly broaden the range of issues that you can debug: for some issues, logpoints are the only practical approach. And for the rest, logpoints provide a convenience that saves a lot of time and effort.

Also, in IntelliJ IDEA 2026.2, logpoints got some very cool improvements, so it’s a perfect time for a recap.

Problem statement

Here’s a mini client/server that uses gRPC for communication. The server has a bug that makes it return incorrect discount values for some tenants.

So we’ll follow the usual debugging flow: reproduce the problem, set up visibility into the inner workings of the server, send a problematic request, and observe exactly how it produces the wrong result.

Reproduce the bug

To simulate running in another environment, the project bundles a Dockerfile with the listening and debug ports exposed. You can launch it using the supplied GrpcQuoteServer in Docker run configuration or directly from the command line:

docker build -t grpc-timeout .
docker run --rm -p 50051:50051 -p 5005:5005 grpc-timeout

Then for a problematic request, use the GrpcQuoteClientLoop run configuration that will periodically query the server. This lets us forget about sending requests manually and focus more on what happens on the server.

When both the server and the client loop are running, the console shows the following:

tenant='JetBrains' region='EMEA' status=OK symbol=IDEA price=100.00 USD source=live detail=region=emea, discount_bps=0

… instead of the expected:

tenant='JetBrains' region='EMEA' status=OK symbol=IDEA price=80.00 USD source=live detail=region=emea, discount_bps=2000

Attach to the server

The server is not launched from a local IntelliJ IDEA debug session, but it listens for debugger connections, so we can still attach to it using the provided GrpcQuoteServer attach run configuration.

One thing that a lot of people get wrong, and that is worth mentioning here: for the debugger, there is no difference whether the process runs locally, in a separate environment, or on a remote host. Either way, the communication happens over a socket, so our exercise is valid for debugging any Java process regardless of locality.

Logpoints

Logpoints are similar to debugging with println statements in that they don’t suspend the program but only log the necessary details to the console. Unlike println statements, they can be changed without rebuilding or redeploying the application.

You might already know how to set a logpoint, but since IntelliJ IDEA 2026.2, there’s been a new, faster way. Click in the gutter in between any two executable lines and enter the expression you want to log. As the starting point, we can use the beginning of the query handling method ( QuoteEndpoint:12 ):

IntelliJ IDEA editor showing an inline Log field for a logpoint expression between two Java statements
Info icon

Beware of heavy computations in hot paths. They are executed in the same VM and are not magically free. Since 2026.2, IntelliJ IDEA removes the debugger-introduced overhead through instrumentation, but heavy logging expressions may still take time to execute.

For every incoming request, the console now prints:

EMEA JetBrains

Now, with the request loop running, we can progressively change and add logpoints until the output points to the bug. Just add more logpoints or update the existing ones and watch for new messages in the console as new requests come in.

After following the call chain and ruling out our initial suspicions, we arrive at the discountBpsFor() method:

IntelliJ IDEA editor showing logpoints inside the discountBpsFor method

The console points at the tenant name not being properly normalized:

tenant = JetBrains expected: jetbrains

Also, the absence of discount applied tells us that the block with the correct discount is never entered. Normalizing the tenant name should fix the bug.

Info icon

Pro tip: when in doubt what produced specific console output, click the line in the console, and IntelliJ IDEA will take you to the relevant piece of code or logpoint:

IntelliJ IDEA debug console showing logpoint output with an Open popup that navigates back to the code

Even if you are using printlns for logging, the navigation will work for you as long as you are running the process with IntelliJ IDEA’s debugger.

Test the fix

Let’s test the fix while we’re here. Logpoints are meant for logging, not for modifying the program, but nothing really prevents us from testing how a particular fix would behave:

IntelliJ IDEA editor showing a logpoint that normalizes the tenant name before the discount check

Works as expected:

EMEA JetBrains
jetbrains
discount applied

Why not printlns

You’re probably thinking logpoints look a lot like nicer println statements. This is correct, in a sense, because the core technique is the same: add probes in a simple way that doesn’t affect how the program runs.

There are several reasons why logpoints can be a better choice:

At that point, logpoints stop feeling like printlns and start feeling like a professional debugging tool.

Why not regular breakpoints

When using the debugger, most developers will reach for breakpoints. But this particular scenario is exactly where logpoints are a better fit, and this is not just a matter of preference.

Let’s see what happens if we use regular breakpoints. After attaching to the server, set a line breakpoint at GrpcQuoteServer.java:55 . The next request from the loop suspends the server:

IntelliJ IDEA debugger paused at a breakpoint in the getQuote method of GrpcQuoteServer.java

But after looking at the program state and stepping a couple of times, we find ourselves in the cancellation path:

IntelliJ IDEA debugger paused on a Status.CANCELLED exception while the remaining discount calculation code is greyed out

Once we’re there, the code has taken a different execution path. You can see that IntelliJ IDEA greyed out parts of the code that are not going to be executed. To bring the problematic state back, we have to send requests one after another and fit our debugging work into the timeout window.

This happens because our client sets a deadline for the remote call. Unlike a typical HTTP/REST client timeout, where the timeout only signals failure on the client, gRPC can propagate the client’s deadline to the server. As a result, the client can actually cancel the server-side work, not just stop waiting for a response.

On the other hand, logpoints give us the same information we would get in the debugger UI, except that we observe it in the console. Importantly, this doesn’t suspend the server, so we can extract the information we need without triggering the timeout.

Bonus: remove the timeout

If you’d prefer an alternative approach to this scenario, here’s another way to debug it. For our gRPC example, the problematic bit was the timeout, and we can remove it at runtime using… logpoints!

As you’ve just seen, logpoint expressions can modify the running program through side effects. Here, we can use this technique to adjust the incoming request.

First, find the library method that sets the timeout. There are several places where we can do that. One of them is io.grpc.internal.ServerImpl.createContext :

IntelliJ IDEA Search Everywhere dialog showing the io.grpc.internal.ServerImpl.createContext symbol

In that method, we can rewrite the value of the timeoutNanos local variable right after it has been assigned:

IntelliJ IDEA editor showing a logpoint that changes timeoutNanos in ServerImpl.createContext

With this logpoint in place, every time the gRPC timeout value is read from the request headers, it is immediately replaced with a five-minute deadline. This means we can suspend the server again.

If you want to only extend the timeout for the reproducer requests and keep the server functioning as usual, for example, if it runs on a shared staging instance, you can use multiline logic inside a logpoint:

IntelliJ IDEA editor showing a multiline logpoint that extends timeoutNanos only for requests with a Debug header

Here’s the code to copy to the logpoint:

Metadata.Key<String> DEBUG_HEADER =
        Metadata.Key.of("Debug", Metadata.ASCII_STRING_MARSHALLER);

String debugHeader = headers.get(DEBUG_HEADER);

if ("Debug".equals(debugHeader)) {
    timeoutNanos = java.util.concurrent.TimeUnit.MINUTES.toNanos(5L);
    return "Timeout reset";
}

The multiline expression parses the request headers and extends the timeout only for requests with the Debug header (which our test clients add). Other requests keep the normal deadline. The if branch returns "Timeout reset", confirming when the branch was visited.


Info icon

You can combine logpoints with more advanced IntelliJ IDEA features, such as Mark Object. If that sounds interesting, you might want to look at this article.

Of course, this method requires familiarity with the library or time to explore it. If you have neither and just want to change the runtime behavior quickly, you can delegate the task to an AI agent using the bundled AI agent skill:

OpenAI Codex terminal showing a prompt to add a logpoint that resets incoming request timeouts to five minutes IntelliJ IDEA AI Agents terminal showing a non-suspending logpoint added to GrpcQuoteServer.java

Conclusion

In this article, we looked at a use case where logpoints offer a simpler and more elegant alternative to breakpoints or println logging. We:

I hope you learned something new and now have a better option for the next time println statements or breakpoints get in the way. In the next post of the series, we’ll look at how debugger instrumentation works, which is the underlying mechanism that makes the new logpoints so fast.

Happy debugging!

all posts ->