Skip to content

Instantly share code, notes, and snippets.

@benhubert
Last active January 18, 2021 15:51
Show Gist options
  • Save benhubert/c1c99670e73ba10ad23a5bcf96ca7dc5 to your computer and use it in GitHub Desktop.
Save benhubert/c1c99670e73ba10ad23a5bcf96ca7dc5 to your computer and use it in GitHub Desktop.
Be careful with members of members

How a simple log line can stop your business

Let's end this week with log output and some java basics and talk about the following toString() method:

@Override
public String toString() {
    return "Car{" +
            "id=" + id +
            ", name='" + name + '\'' +
            ", owner=" + owner.getId() +
            '}';
}

Well, do you see a problem with it? Not so far, you might say. So, when will this method be called? I'd say that, in most business applications out there, you won't really need your toString() implementation. Except, when debugging your application.

For this case, you've added the following line to your code:

LOG.debug("Processing {}", car);

Still see no problem? Well, there is one, and I'd say it's a big issue. As soon as you enable DEBUG log for your application, your code might cause a NullPointerException. Namely when your car does not have an owner.

Let's assume our owner is null. This might never be a problem for the rest of your business logic, and it might even be intended. But at some point you could run into a (possibly completely unrelated) issue and want to debug that on your production machine.

You enable DEBUG output, and suddenly your logging framework (slf4j, in my particular case) starts using your toString() implementation. But recall what we did there, especially think about the following line:

            ", owner=" + owner.getId() +

But as said, our car owner does not exist, it is null. So, we run straight into the mentioned NullPointerException.

Would you've expected that, when you'd added the simple LOG.debug(...) line from above? Would you've even seen that it could be risky for your business logic to add this debug statement in your code?

Be careful when calling members of member variables.

Activating log output should never, ever affect your business logic. So, what do we learn from this? I'd suggest, in methods like toString() always add your member variables directly, without any reference to their members. For this particular implementation, just remove .getId().

@Override
public String toString() {
    return "Car{" +
            "id=" + id +
            ", name='" + name + '\'' +
            ", owner=" + owner +
            '}';
}

And if you really care about a short output and really want the toString() method to only write out the ID of the owner, you must add a null-check. You could use Optional.ofNullable(owner)... or just this simple solution:

@Override
public String toString() {
    return "Car{" +
            "id=" + id +
            ", name='" + name + '\'' +
            ", owner=" + owner != null ? owner.getId() : "null" +
            '}';
}

However, this will bring complexity to your toString() method, and complexity brings the risk for other mistakes, bugs, and problems.

So, keep your code simple and always be cautious when accessing members of member variables. So much for that  — have a relaxing weekend without any downtime!

@Override
public String toString() {
return "Car{" +
"id=" + id +
", name='" + name + '\'' +
", owner=" + owner.getId() +
'}';
}
LOG.debug("Processing {}", car);
@Override
public String toString() {
return "Car{" +
"id=" + id +
", name='" + name + '\'' +
", owner=" + owner +
'}';
}
@Override
public String toString() {
return "Car{" +
"id=" + id +
", name='" + name + '\'' +
", owner=" + owner != null ? owner.getId() : "null" +
'}';
}
", owner=" + owner.getId() +
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment