Structured logging has infinite loop and throws StackOverflowError when attempting to log a Path object.

Tested with spring-boot 3.4.3 and latest 3.5.0-SNAPSHOT.

Example code:

System.setProperty("logging.structured.format.console", "logstash");
SpringApplication.run(DemoApplication.class, args);
LoggerFactory.getLogger(DemoApplication.class).atInfo()
        .addKeyValue("foo", Path.of("bar"))
        .log("test");

Exception:

Exception in thread "main" java.lang.StackOverflowError
    ...
    at org.springframework.boot.json.JsonValueWriter.append(JsonValueWriter.java:282)
    at org.springframework.boot.json.JsonValueWriter.start(JsonValueWriter.java:144)
    at org.springframework.boot.json.JsonValueWriter.writeArray(JsonValueWriter.java:168)
    at org.springframework.boot.json.JsonValueWriter.write(JsonValueWriter.java:118)
    at org.springframework.boot.json.JsonValueWriter.writeElement(JsonValueWriter.java:190)

Comment From: gkdis6

Hi @nosan , I think

else if (value instanceof Path p) {
    write(p.toString());
}

would be a more natural approach here. Since Path.toString() already provides a platform-independent string representation of the path, logging it directly as a string seems more intuitive than converting it into a JSON array. This would also align with how other objects like File are typically logged.

Comment From: nosan

Hi @gkdis6,

Since Path.toString() already provides a platform-independent string representation of the path, logging it directly as a string seems more intuitive than converting it into a JSON array.

This is a good option as well.


  • Jackson: Serializes using Path.toUri().toString() (source).
  • Gson: Fails to serialize java.nio.file.Path (issue).
  • Yasson: Serializes using Path.toString() (source).

So I would say there is no "right or wrong" approach. For example, this proposal suggests handling Path as an Iterable (JSON Array). Your suggested option is to use toString(), while Jackson's choice is to use URI.

Additionally, it's always possible to explicitly use something like addKeyValue("directory", Path.of("...").toString()).

Comment From: nosan

I reviewed the JsonValueWriter.write(...) implementation again, and I believe the original intention was as follows:

  • If it’s a Map, serialize it as a JSON object ({}).
  • If it’s an Iterable or an array, serialize it as a JSON array ([]).
  • Otherwise, serialize it as a JSON string.

I don’t think anyone intended to serialize java.nio.file.Path as a JSON array. Most likely, the fact that Path extends Iterable<Path> was simply overlooked.

I also checked different java.nio.file.Path JSON serializations:

Map<String, Object> files = new LinkedHashMap<>();
files.put("files",
List.of(Paths.get(".").toAbsolutePath(), Paths.get("1.jar").toAbsolutePath(), Paths.get("/root")));
String json = JsonWriter.standard().write(files).toJsonString();

Iterable Path.forEach():

{
  "files": [
    [
      "Users",
      "dmytronosan",
      "IdeaProjects",
      "spring-boot",
      "spring-boot-project",
      "spring-boot",
      "."
    ],
    [
      "Users",
      "dmytronosan",
      "IdeaProjects",
      "spring-boot",
      "spring-boot-project",
      "spring-boot",
      "1.jar"
    ],
    [
      "root"
    ]
  ]
}

Path.toString():

{
  "files": [
    "/Users/dmytronosan/IdeaProjects/spring-boot/spring-boot-project/spring-boot/.",
    "/Users/dmytronosan/IdeaProjects/spring-boot/spring-boot-project/spring-boot/1.jar",
    "/root"
  ]
}

Path.toUri().toString():

{
  "files": [
    "file:///Users/dmytronosan/IdeaProjects/spring-boot/spring-boot-project/spring-boot/./",
    "file:///Users/dmytronosan/IdeaProjects/spring-boot/spring-boot-project/spring-boot/1.jar",
    "file:///root"
  ]
}

I think treating java.nio.file.Path as a JSON array (Iterable) is quite unusual and meaningless.

With that said, I will update gh-44507 to use Path.toString().

Comment From: gkdis6

When I said, “This would also align with how other objects like File are typically logged.” I actually meant to talk about how 'Path.toString()' case should be represented.

I really appreciate your time and feedback. Thanks for reviewing! 👍