Skip to content

JSON writer incorrectly escapes forward slash which can cause structure logging issues #45972

@alexey-lapin

Description

@alexey-lapin

The new structured logging support is great and provides all the necessary building blocks to create custom JSON log formats via JsonWriterStructuredLogFormatter.

However, JsonValueWriter.writeString() escapes / as \/ in JSON, which causes issues when targeting platforms like Google Cloud Logging. GCP expects specific root-level keys such as:

{
"logging.googleapis.com/spanId": "abc",
"logging.googleapis.com/trace": "projects/my-project/traces/123"
}

But the current implementation renders them as:

{
"logging.googleapis.com\/spanId": "abc",
"logging.googleapis.com\/trace": "projects\/my-project\/traces\/123"
}

example impl

public class GCPStructuredLogFormatter extends JsonWriterStructuredLogFormatter<ILoggingEvent> {

    GCPStructuredLogFormatter(StackTracePrinter stackTracePrinter,
                              ContextPairs contextPairs,
                              ThrowableProxyConverter throwableProxyConverter,
                              StructuredLoggingJsonMembersCustomizer<?> customizer) {
        super(GCPStructuredLogFormatter::jsonMembers, customizer);
    }

    private static void jsonMembers(JsonWriter.Members<ILoggingEvent> members) {
        members.add("timestamp", ILoggingEvent::getInstant).as(GCPStructuredLogFormatter::asTimestamp);
        members.add("message", ILoggingEvent::getFormattedMessage);
        members.add("logger_name", ILoggingEvent::getLoggerName);
        members.add("thread_name", ILoggingEvent::getThreadName);
        members.add("level", ILoggingEvent::getLevel);
        members.add("logging.googleapis.com/spanId", (e) -> "abc");
        members.add("logging.googleapis.com/trace", (e) -> "projects/my-project/traces/123");
    }

    private static String asTimestamp(Instant instant) {
        OffsetDateTime offsetDateTime = OffsetDateTime.ofInstant(instant, ZoneId.systemDefault());
        return DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(offsetDateTime);
    }

}

This prevents GCP from recognizing the structured fields.

Request: Add a way to opt out of escaping / in JSON keys when using structured logging.

This would avoid having to reimplement JSON rendering logic entirely and allow users to fully benefit from the structured logging infrastructure provided by Spring Boot.

Activity

mhalbritter

mhalbritter commented on Jun 16, 2025

@mhalbritter
Contributor

I wonder why we escape / in the first place. The RFC reads:

An object structure is represented as a pair of curly brackets surrounding zero or more name/value pairs (or members). A name is a string.

And a string is:

All Unicode characters may be placed within the quotation marks, except for the characters that MUST be escaped: quotation mark, reverse solidus, and the control characters (U+0000 through U+001F).

nosan

nosan commented on Jun 16, 2025

@nosan
Contributor
Map<String, String> values = new LinkedHashMap<>();
values.put("/", "/");
values.put("�NUL", "NUL�");  \u0000
values.put("'", "'");
values.put("\"", "\"");
values.put("\n", "\n");
values.put("\\", "\\");
values.put("\b", "\b");
values.put("\f", "\f");
values.put("\r", "\r");
values.put("\t", "\t");



Jackson
{"/":"/","\u0000":"\u0000","'":"'","\"":"\"","\n":"\n","\\":"\\","\b":"\b","\f":"\f","\r":"\r","\t":"\t"}

Gson
{"/":"/","\u0000":"\u0000","'":"'","\"":"\"","\n":"\n","\\":"\\","\b":"\b","\f":"\f","\r":"\r","\t":"\t"}

JSON API (Jakarta)
{"/":"/","\u0000":"\u0000","'":"'","\"":"\"","\n":"\n","\\":"\\","\b":"\b","\f":"\f","\r":"\r","\t":"\t"}

Spring Boot
{"\/":"\/","\u0000":"\u0000","'":"'","\"":"\"","\n":"\n","\\":"\\","\b":"\b","\f":"\f","\r":"\r","\t":"\t"}

Looks like only Spring Boot escapes / , other JSON libraries don't do that.

mhalbritter

mhalbritter commented on Jun 16, 2025

@mhalbritter
Contributor

Thanks for testing, @nosan. I think we stop escaping the /. @philwebb do you agree?

changed the title [-]Allow unescaped forward slash in JSON keys for structured logging[/-] [+]Allow unescaped forward slash in JSON for structured logging[/+] on Jun 17, 2025
philwebb

philwebb commented on Jun 17, 2025

@philwebb
Member

Yes, I think this is a bug if we don't align with the other libraries.

added this to the 3.4.x milestone on Jun 17, 2025
changed the title [-]Allow unescaped forward slash in JSON for structured logging[/-] [+]JSON writer incorrectly escapes forward slash which can cause structure logging issues[/+] on Jun 17, 2025
self-assigned this
on Jun 17, 2025
added a commit that references this issue on Jun 17, 2025
7178458
modified the milestones: 3.4.x, 3.4.7 on Jun 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Relationships

None yet

    Development

    No branches or pull requests

      Participants

      @philwebb@nosan@alexey-lapin@spring-projects-issues@mhalbritter

      Issue actions

        JSON writer incorrectly escapes forward slash which can cause structure logging issues · Issue #45972 · spring-projects/spring-boot