-
Notifications
You must be signed in to change notification settings - Fork 61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add custom logging provider to support structured logging for SLF4J 1.x + Logback 1.2.x #3693
Open
JoeWang1127
wants to merge
65
commits into
main
Choose a base branch
from
feat/sdk-logging-helper
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 49 commits
Commits
Show all changes
65 commits
Select commit
Hold shift + click to select a range
60ec4e6
feat: add sdk logging helpers
JoeWang1127 88097e8
refactor
JoeWang1127 bd30ffa
add test deps
JoeWang1127 b32e11b
add test
JoeWang1127 4181927
add formatter
JoeWang1127 98e4130
Merge branch 'main' into feat/sdk-logging-helper
JoeWang1127 209768b
format
JoeWang1127 b0310a4
change version def
JoeWang1127 64acdfb
update test
JoeWang1127 82ff79d
add profile
JoeWang1127 e0f8981
skip normal release
JoeWang1127 204fb8f
add test
JoeWang1127 925f04c
update test
JoeWang1127 469c348
update test
JoeWang1127 25a6cb3
format
JoeWang1127 d1698a1
add test
JoeWang1127 b4effd9
remove slf4j deps
JoeWang1127 fb42331
add parent
JoeWang1127 d0712cc
update pom
JoeWang1127 e8eda21
add skip release in parent pom
JoeWang1127 b6819d8
Merge branch 'main' into feat/sdk-logging-helper
JoeWang1127 f35a134
Merge branch 'main' into feat/sdk-logging-helper
JoeWang1127 25705f1
Merge branch 'main' into feat/sdk-logging-helper
JoeWang1127 0bc9c94
add a bom
JoeWang1127 710705b
add a empty class for test prep
JoeWang1127 d56cf81
Merge branch 'main' into feat/sdk-logging-helper
JoeWang1127 310e546
chore: generate libraries at Fri Mar 14 19:00:09 UTC 2025
cloud-java-bot 8bfd02a
remove optional
JoeWang1127 7c8e653
add showcase tests
JoeWang1127 99bf64b
chore: generate libraries at Fri Mar 14 23:07:02 UTC 2025
cloud-java-bot dc73a51
remove child version
JoeWang1127 c029d4f
update showcase tests
JoeWang1127 d3a8988
exclude compile appender in 1.x
JoeWang1127 9fd9d56
exclude compile appender in 1.x
JoeWang1127 68c7a3c
exclude compile appender in 1.x
JoeWang1127 9e69796
add unit tests
JoeWang1127 4b88289
add early return
JoeWang1127 4de3bfc
refactor
JoeWang1127 ed28cd4
update tests
JoeWang1127 7529de0
rename
JoeWang1127 f295db8
chore: generate libraries at Sat Mar 15 15:30:16 UTC 2025
cloud-java-bot f54db56
add showcase test
JoeWang1127 e717447
add showcase test
JoeWang1127 8237028
refactor test
JoeWang1127 4aef523
format
JoeWang1127 f5efa2a
skip release
JoeWang1127 c26da4b
format
JoeWang1127 afb6ae6
merge main branch
JoeWang1127 12ea137
update root pom
JoeWang1127 cfc9af3
install logging module in showcase ci
JoeWang1127 9ea01fa
continue
JoeWang1127 89c469a
add unit tests
JoeWang1127 3179277
add included key
JoeWang1127 39f94ed
add unit tests
JoeWang1127 1c6f064
Merge branch 'main' into feat/sdk-logging-helper
JoeWang1127 b8db96f
format
JoeWang1127 6ed523e
add unit tests
JoeWang1127 6eb0f8e
change group id in showcase
JoeWang1127 2cc02e5
add key replacement
JoeWang1127 05fcd82
run test java 8
JoeWang1127 179ecc8
final
JoeWang1127 47b4221
Merge branch 'main' into feat/sdk-logging-helper
JoeWang1127 eea89ae
change package name
JoeWang1127 af93306
refactor
JoeWang1127 5898bd1
add comment
JoeWang1127 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
82 changes: 82 additions & 0 deletions
82
...ing/logback-extension/src/main/java/com/google/api/logging/SDKLoggingMdcJsonProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
/* | ||
* Copyright 2025 Google LLC | ||
* | ||
* Redistribution and use in source and binary forms, with or without | ||
* modification, are permitted provided that the following conditions are | ||
* met: | ||
* | ||
* * Redistributions of source code must retain the above copyright | ||
* notice, this list of conditions and the following disclaimer. | ||
* * Redistributions in binary form must reproduce the above | ||
* copyright notice, this list of conditions and the following disclaimer | ||
* in the documentation and/or other materials provided with the | ||
* distribution. | ||
* * Neither the name of Google LLC nor the names of its | ||
* contributors may be used to endorse or promote products derived from | ||
* this software without specific prior written permission. | ||
* | ||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
*/ | ||
|
||
package com.google.api.logging; | ||
|
||
import ch.qos.logback.classic.spi.ILoggingEvent; | ||
import com.fasterxml.jackson.core.JsonGenerator; | ||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import java.io.IOException; | ||
import java.util.Map; | ||
import net.logstash.logback.composite.loggingevent.MdcJsonProvider; | ||
|
||
public class SDKLoggingMdcJsonProvider extends MdcJsonProvider { | ||
|
||
private final ObjectMapper objectMapper = new ObjectMapper(); | ||
|
||
@Override | ||
public void writeTo(JsonGenerator generator, ILoggingEvent event) throws IOException { | ||
Map<String, String> mdcProperties = event.getMDCPropertyMap(); | ||
if (mdcProperties == null || mdcProperties.isEmpty()) { | ||
return; | ||
} | ||
|
||
boolean hasWrittenStart = false; | ||
for (Map.Entry<String, String> entry : mdcProperties.entrySet()) { | ||
String fieldName = entry.getKey(); | ||
String entryValueString = entry.getValue(); | ||
if (fieldName == null || entryValueString == null) { | ||
return; | ||
JoeWang1127 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
if (!hasWrittenStart && getFieldName() != null) { | ||
generator.writeObjectFieldStart(getFieldName()); | ||
hasWrittenStart = true; | ||
} | ||
generator.writeFieldName(fieldName); | ||
|
||
try { | ||
generator.writeTree(convertToTreeNode(entryValueString)); | ||
} catch (JsonProcessingException e) { | ||
// in case of conversion exception, just use String | ||
generator.writeObject(entryValueString); | ||
} | ||
} | ||
if (hasWrittenStart) { | ||
generator.writeEndObject(); | ||
} | ||
} | ||
|
||
private JsonNode convertToTreeNode(String jsonString) throws JsonProcessingException { | ||
return objectMapper.readTree(jsonString); | ||
} | ||
} |
141 changes: 141 additions & 0 deletions
141
...logback-extension/src/test/java/com/google/api/logging/SDKLoggingMdcJsonProviderTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
/* | ||
* Copyright 2025 Google LLC | ||
* | ||
* Redistribution and use in source and binary forms, with or without | ||
* modification, are permitted provided that the following conditions are | ||
* met: | ||
* | ||
* * Redistributions of source code must retain the above copyright | ||
* notice, this list of conditions and the following disclaimer. | ||
* * Redistributions in binary form must reproduce the above | ||
* copyright notice, this list of conditions and the following disclaimer | ||
* in the documentation and/or other materials provided with the | ||
* distribution. | ||
* * Neither the name of Google LLC nor the names of its | ||
* contributors may be used to endorse or promote products derived from | ||
* this software without specific prior written permission. | ||
* | ||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
*/ | ||
|
||
package com.google.api.logging; | ||
|
||
import static org.mockito.ArgumentMatchers.any; | ||
import static org.mockito.ArgumentMatchers.anyString; | ||
import static org.mockito.Mockito.inOrder; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.never; | ||
import static org.mockito.Mockito.verify; | ||
import static org.mockito.Mockito.when; | ||
|
||
import ch.qos.logback.classic.spi.ILoggingEvent; | ||
import com.fasterxml.jackson.core.JsonGenerator; | ||
import com.fasterxml.jackson.databind.JsonNode; | ||
import java.io.IOException; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import org.junit.jupiter.api.AfterEach; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.mockito.InOrder; | ||
|
||
class SDKLoggingMdcJsonProviderTest { | ||
|
||
private SDKLoggingMdcJsonProvider provider = new SDKLoggingMdcJsonProvider(); | ||
private JsonGenerator generator = mock(JsonGenerator.class); | ||
private ILoggingEvent event = mock(ILoggingEvent.class); | ||
private Map<String, String> mdc; | ||
|
||
@BeforeEach | ||
void init() { | ||
mdc = new HashMap<>(); | ||
when(event.getMDCPropertyMap()).thenReturn(mdc); | ||
} | ||
|
||
@AfterEach | ||
void post() { | ||
mdc.clear(); | ||
} | ||
|
||
@Test | ||
void testWriteNullMdcMap() throws IOException { | ||
when(event.getMDCPropertyMap()).thenReturn(null); | ||
provider.writeTo(generator, event); | ||
verify(generator, never()).writeFieldName(anyString()); | ||
verify(generator, never()).writeTree(any(JsonNode.class)); | ||
} | ||
|
||
@Test | ||
void testWriteEmptyMdcMap() throws IOException { | ||
provider.writeTo(generator, event); | ||
verify(generator, never()).writeFieldName(anyString()); | ||
verify(generator, never()).writeTree(any(JsonNode.class)); | ||
} | ||
|
||
@Test | ||
void testWriteValidJsonStringToJsonTree() throws IOException { | ||
mdc.put( | ||
"json1", | ||
"{\n" | ||
+ " \"@version\": \"1\",\n" | ||
+ " \"textPayload\": \"Received response\",\n" | ||
+ " \"response.payload\": {\n" | ||
+ " \"name\": \"example\",\n" | ||
+ " \"state\": \"ACTIVE\"\n" | ||
+ " }\n" | ||
+ "}"); | ||
|
||
provider.setFieldName("log-name"); | ||
provider.writeTo(generator, event); | ||
InOrder inOrder = inOrder(generator); | ||
inOrder.verify(generator).writeObjectFieldStart("log-name"); | ||
inOrder.verify(generator).writeFieldName("json1"); | ||
inOrder.verify(generator).writeTree(any(JsonNode.class)); | ||
inOrder.verify(generator).writeEndObject(); | ||
} | ||
|
||
@Test | ||
void testWriteInvalidJsonStringToString() throws IOException { | ||
mdc.put( | ||
"json1", | ||
"{\n" | ||
+ " \"@version\": \"1\",\n" | ||
+ " \"textPayload\": \"Received response\",\n" | ||
+ " \"response.payload\": {\n" | ||
+ " \"name\": \"example\",\n" | ||
+ " \"state\": \"ACTIVE\",\n" // the last semicolon is redundant. | ||
+ " }\n" | ||
+ "}"); | ||
provider.writeTo(generator, event); | ||
verify(generator).writeFieldName("json1"); | ||
verify(generator).writeObject(anyString()); | ||
// should not write tree node because the json string is invalid. | ||
verify(generator, never()).writeTree(any(JsonNode.class)); | ||
} | ||
|
||
@Test | ||
void testWriteNullValueDoesNotThrowsException() throws IOException { | ||
mdc.put("json1", null); | ||
provider.writeTo(generator, event); | ||
verify(generator, never()).writeObject(anyString()); | ||
verify(generator, never()).writeTree(any(JsonNode.class)); | ||
} | ||
|
||
@Test | ||
void testWriteNullKeyDoesNotThrowsException() throws IOException { | ||
mdc.put(null, "example value"); | ||
provider.writeTo(generator, event); | ||
verify(generator, never()).writeObject(anyString()); | ||
verify(generator, never()).writeTree(any(JsonNode.class)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logstash-logback has some customization capabilities that I suspect are fairly easy to restore here, like configure specific entries in the MDC to be included or excluded and renaming keys, ref: readme:mdc-fields. It might be as easy as adding these conditions here, or a bit more involved. Can you take a look? I think it nice to have it preserved.
https://github.com/logfellow/logstash-logback-encoder/blob/76f5295f35bc44518af91bfccb07a8ecb142229c/src/main/java/net/logstash/logback/composite/loggingevent/MdcJsonProvider.java#L102-L103
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added these features.