Skip to content

feat: add client side logging with slf4j #3403

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

Merged
merged 97 commits into from
Feb 24, 2025
Merged
Changes from 95 commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
59b5d73
poc: add log in grpc interceptor.
zhumin8 Nov 19, 2024
d34ec88
try out basics with httpjson interceptor.
zhumin8 Nov 20, 2024
b6417ae
implement conditional logging, use JUL when no binding. log formattin…
zhumin8 Nov 25, 2024
0c169ad
temp manual testing with showcase.
zhumin8 Nov 25, 2024
9ced175
simplify logic for conditional logger.
zhumin8 Nov 25, 2024
681f7d5
implement logging for basic case.
zhumin8 Nov 26, 2024
5c01ef2
add a JsonContextMapHandler to format in JUL case.
zhumin8 Nov 27, 2024
6605ef3
fix response payload for streaming. Add if guards to bypass log when …
zhumin8 Nov 27, 2024
ebbae15
when logging disabled, assign NOPLogger directly.
zhumin8 Nov 27, 2024
543298d
some temp settings for testing with showcase.
zhumin8 Dec 2, 2024
aac66f2
remove JUL wrapper for now. refactor getLogger(). Add tests for Loggi…
zhumin8 Dec 5, 2024
487614c
minor clean up.:
zhumin8 Dec 5, 2024
cc3465b
fix test after adding interceptor for logging for http.
zhumin8 Dec 5, 2024
211a3d1
cleanup and fix response header.
zhumin8 Dec 6, 2024
2970210
lint
zhumin8 Dec 6, 2024
32f8b7c
lint: license.
zhumin8 Dec 6, 2024
245c14d
refactor GrpcLoggingInterceptor for readability, intro LogData.
zhumin8 Dec 6, 2024
8358497
minor cleanups.
zhumin8 Dec 6, 2024
97087b2
GrpcLoggingInterceptor fix for thread safe, separate out debug loggin…
zhumin8 Dec 6, 2024
727a3e9
minor changes + add showcase it test.(need to run with env var, setup…
zhumin8 Dec 6, 2024
23bc111
refactor HttpJsonLoggingInterceptor. remove public modifier from logg…
zhumin8 Dec 6, 2024
bbe0700
add GrpcLoggingInterceptorTest and test interceptor structure.
zhumin8 Dec 8, 2024
3d7c7f9
fix status code '0'.
zhumin8 Dec 10, 2024
b870d81
merge logs in interceptors, record log data. remove request id.
zhumin8 Dec 10, 2024
56a2870
replace txt message to json message with all fields duplicated.
zhumin8 Dec 10, 2024
83eedf0
add try catch to logging methods
zhumin8 Dec 10, 2024
d85c848
add showcase tests. expose logging interceptors as public to setup te…
zhumin8 Dec 10, 2024
1919809
add internal api annotation.
zhumin8 Dec 10, 2024
613b6c8
lint
zhumin8 Dec 10, 2024
1169eaa
fix typo
zhumin8 Dec 11, 2024
a6b5433
remove unused test class.
zhumin8 Dec 11, 2024
fb0966e
try to exclude test from showcase-native.
zhumin8 Dec 11, 2024
77939fe
fix logback version.
zhumin8 Dec 11, 2024
04ef774
revert accidental change.
zhumin8 Dec 11, 2024
439e071
fix typo in ci.
zhumin8 Dec 11, 2024
673c9fe
cleanup gax pom. add tests.
zhumin8 Dec 11, 2024
c2b607b
add test.
zhumin8 Dec 11, 2024
3df39fa
Merge branch 'main' into client-log
zhumin8 Dec 11, 2024
4190dc7
rm redundant assignment.
zhumin8 Dec 11, 2024
fefb436
add logging test setup in httpjson.
zhumin8 Dec 11, 2024
7901d8a
add log response test to interceptors.
zhumin8 Dec 11, 2024
20f9f5a
remove redundant code.
zhumin8 Dec 11, 2024
172701d
add slf4j and test dep to dependencies.properties.
zhumin8 Dec 11, 2024
d25e742
Merge branch 'main' into client-log
zhumin8 Dec 11, 2024
a8d2f20
make requestPayload also as JsonElement in LogData.
zhumin8 Dec 11, 2024
df97834
fix typo in dependencies.properties.
zhumin8 Dec 12, 2024
32dbd0e
add test assertion to verify logger not enabled at levels when env no…
zhumin8 Dec 12, 2024
37eb391
attempt to add slf4j to bazel BUILD. Not solving bazel build for it t…
zhumin8 Dec 13, 2024
f95423b
grpc logger interceptor: cleanup logic, add to catch NSM error.
zhumin8 Dec 23, 2024
8e01aa0
lint. fix binary headers.
zhumin8 Dec 23, 2024
73210fa
refactor helper methods into LoggingUtils. update to use addKeyValue.
zhumin8 Dec 26, 2024
b1386cb
update LogDataTest, fix typo in log levels.
zhumin8 Dec 26, 2024
ef26d30
logic to switch according to slf4j 1.x or 2.x
zhumin8 Feb 4, 2025
0c466eb
rework LoggingUtilsTest to use custom implementation of Logger, remov…
zhumin8 Feb 4, 2025
7a23e2d
clean ups.
zhumin8 Feb 4, 2025
3821ec3
remove dep on logback for gax tests.
zhumin8 Feb 5, 2025
23f00e8
add tests.
zhumin8 Feb 5, 2025
9f7d77c
add tests.
zhumin8 Feb 5, 2025
86cc4d2
add test, rm logback test profiles from gax.
zhumin8 Feb 6, 2025
446910a
Merge branch 'main' into client-log
zhumin8 Feb 6, 2025
de01f37
also capture errors with try-catch to fail silently.
zhumin8 Feb 6, 2025
e3862a8
cleanup un-necessary tests in gax-grpc, gax-httpjson
zhumin8 Feb 6, 2025
427e820
add showcase tests for logging with slf4j 1x, 2x separately, setup pr…
zhumin8 Feb 7, 2025
6ff5479
headings
zhumin8 Feb 7, 2025
82ead2b
refactor to isolate slf4j classes.
zhumin8 Feb 10, 2025
b5b6e94
rename classes.
zhumin8 Feb 10, 2025
5db8884
fix surefire config to exclude/include logging tests. add to PenvVarT…
zhumin8 Feb 10, 2025
56316d3
showcase test setups.
zhumin8 Feb 11, 2025
b7234e3
add showcase logging test steps to ci. cleanups.
zhumin8 Feb 11, 2025
e7acafc
rm unused logging configs.
zhumin8 Feb 11, 2025
801cef6
fix merging miss.
zhumin8 Feb 11, 2025
cf8ecbc
minor cleanups.
zhumin8 Feb 11, 2025
19faf95
add compiler exclude config to enable-golden-tests profile.
zhumin8 Feb 11, 2025
47f1212
add missing env var for ci test.
zhumin8 Feb 11, 2025
420de1e
add test coverage, lint.
zhumin8 Feb 11, 2025
c222198
add working dir for showcase logging tests in ci.
zhumin8 Feb 11, 2025
227857a
add dep for gax-java bazel builds, for integration tests.
zhumin8 Feb 11, 2025
008048a
Merge branch 'main' into client-log
zhumin8 Feb 11, 2025
07888cb
some code cleanups.
zhumin8 Feb 11, 2025
20017cc
add comments, lint, minor fixes. updated logback version for test.
zhumin8 Feb 12, 2025
2d3ba3e
minor cleanup in test.
zhumin8 Feb 12, 2025
7c1ea76
add test cases for info level.
zhumin8 Feb 12, 2025
7aa8333
minor cleanups.
zhumin8 Feb 12, 2025
d5c759d
comments and cleanups.
zhumin8 Feb 12, 2025
1641f55
rename method.
zhumin8 Feb 12, 2025
d86b7a0
added tests.
zhumin8 Feb 12, 2025
f611c74
feedback: added comments, move slf4j dep to each module inside gax.
zhumin8 Feb 14, 2025
539031b
rename hasAddKeyValue to isSLF4J2x.
zhumin8 Feb 14, 2025
091ab38
add final to class, add comments.
zhumin8 Feb 18, 2025
b3b328e
revert final change.
zhumin8 Feb 18, 2025
6aece18
minor refactor: split internal Slf4jUtils and Slf4jLoggingHelpers.
zhumin8 Feb 18, 2025
c3db3e7
check type before casting to Message
zhumin8 Feb 18, 2025
a975fd5
fix: rm grpc from log message.
zhumin8 Feb 18, 2025
b1af879
continue: rm grpc from log message.
zhumin8 Feb 20, 2025
157921b
Merge branch 'main' into client-log
zhumin8 Feb 20, 2025
89da1b4
minor change: use guava isNullOrEmpty.
zhumin8 Feb 21, 2025
fad1865
fix typo.
zhumin8 Feb 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@ jobs:
env:
GOOGLE_CLOUD_UNIVERSE_DOMAIN: random.com
GOOGLE_CLOUD_ENABLE_DIRECT_PATH_XDS: true
GOOGLE_SDK_JAVA_LOGGING: true
- run: bazelisk version
- name: Install Maven modules
run: |
@@ -82,6 +83,7 @@ jobs:
env:
GOOGLE_CLOUD_UNIVERSE_DOMAIN: random.com
GOOGLE_CLOUD_ENABLE_DIRECT_PATH_XDS: true
GOOGLE_SDK_JAVA_LOGGING: true
- run: bazelisk version
- name: Install Maven modules
run: |
@@ -133,6 +135,7 @@ jobs:
env:
GOOGLE_CLOUD_UNIVERSE_DOMAIN: random.com
GOOGLE_CLOUD_ENABLE_DIRECT_PATH_XDS: true
GOOGLE_SDK_JAVA_LOGGING: true

build-java8-gapic-generator-java:
name: "build(8) for gapic-generator-java"
@@ -255,6 +258,34 @@ jobs:
-P enable-integration-tests \
--batch-mode \
--no-transfer-progress
# The `slf4j2_logback` profile brings logging dependency and compiles logging tests, require env var to be set
- name: Showcase integration tests - Logging SLF4J 2.x
working-directory: showcase
run: |
mvn clean verify -P '!showcase,enable-integration-tests,loggingTestBase,slf4j2_logback' \
--batch-mode \
--no-transfer-progress
# Set the Env Var for this step only
env:
GOOGLE_SDK_JAVA_LOGGING: true
# The `slf4j1_logback` profile brings logging dependency and compiles logging tests, require env var to be set
- name: Showcase integration tests - Logging SLF4J 1.x
working-directory: showcase
run: |
mvn clean verify -P '!showcase,enable-integration-tests,loggingTestBase,slf4j1_logback' \
--batch-mode \
--no-transfer-progress
# Set the Env Var for this step only
env:
GOOGLE_SDK_JAVA_LOGGING: true
# The `disabledLogging` profile tests logging disabled when logging dependency present,
# do not set env var for this step
- name: Showcase integration tests - Logging disabed
working-directory: showcase
run: |
mvn clean verify -P '!showcase,enable-integration-tests,loggingTestBase,disabledLogging' \
--batch-mode \
--no-transfer-progress

showcase-clirr:
if: ${{ github.base_ref != '' }} # Only execute on pull_request trigger event
1 change: 1 addition & 0 deletions gapic-generator-java-pom-parent/pom.xml
Original file line number Diff line number Diff line change
@@ -38,6 +38,7 @@
<j2objc-annotations.version>3.0.0</j2objc-annotations.version>
<threetenbp.version>1.7.0</threetenbp.version>
<junit.version>5.11.4</junit.version>
<slf4j.version>2.0.16</slf4j.version>
</properties>

<developers>
2 changes: 2 additions & 0 deletions gax-java/dependencies.properties
Original file line number Diff line number Diff line change
@@ -76,6 +76,8 @@ maven.com_google_http_client_google_http_client_gson=com.google.http-client:goog
maven.org_codehaus_mojo_animal_sniffer_annotations=org.codehaus.mojo:animal-sniffer-annotations:1.24
maven.javax_annotation_javax_annotation_api=javax.annotation:javax.annotation-api:1.3.2
maven.org_graalvm_sdk=org.graalvm.sdk:nativeimage:24.1.2
maven.org_slf4j_slf4j_api=org.slf4j:slf4j-api:2.0.16
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know dependencies.properties (or the bazel build) also have some form of optional dependency? Would this be something that we also need to add?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can look into this. But I don't think it really matters as bazel build here is only used for integration tests (which is irrelevant to logging feature), and we do not publish artifact with it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bazel build here is only used for integration tests

Do you know if self-service also uses the bazel build? If so, would it make sense for them to also have slfj be optional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. Correct me if I am wrong, I am adding slf4j optional dependency to Gax. For self-service, if even if they use bazel build and build with slf4j (non-optional). Their published artifact depends on a published version of GAX? If so, we are good and do not need to alter the bazel process.
For the original question: from a quick search, bazel does not have a easy equivalent of maven's optional.
cc. @blakeli0 if you know any details on the self-service process?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in person discussion: We are not adding to gradle templates and should be good here.

maven.com_google_protobuf_protobuf_java_util=com.google.protobuf:protobuf-java-util:3.25.5

# Testing maven artifacts
maven.junit_junit=junit:junit:4.13.2
6 changes: 6 additions & 0 deletions gax-java/gax-grpc/pom.xml
Original file line number Diff line number Diff line change
@@ -92,6 +92,12 @@
<artifactId>auto-value-annotations</artifactId>
<scope>provided</scope>
</dependency>
<!-- Logging dependency -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<optional>true</optional>
</dependency>

<!-- test dependencies -->
<dependency>
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* 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.gax.grpc;

import static com.google.api.gax.logging.LoggingUtils.executeWithTryCatch;
import static com.google.api.gax.logging.LoggingUtils.logRequest;
import static com.google.api.gax.logging.LoggingUtils.logResponse;
import static com.google.api.gax.logging.LoggingUtils.recordResponseHeaders;
import static com.google.api.gax.logging.LoggingUtils.recordResponsePayload;
import static com.google.api.gax.logging.LoggingUtils.recordServiceRpcAndRequestHeaders;

import com.google.api.core.InternalApi;
import com.google.api.gax.logging.LogData;
import com.google.api.gax.logging.LoggerProvider;
import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ClientInterceptor;
import io.grpc.ForwardingClientCall;
import io.grpc.ForwardingClientCallListener.SimpleForwardingClientCallListener;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
import io.grpc.Status;
import java.util.HashMap;
import java.util.Map;

@InternalApi
public class GrpcLoggingInterceptor implements ClientInterceptor {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be package private. Same thing for HttpJsonLoggingInterceptor.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the current test setup, these needs to be public for testing purposes. I'll look into the test setup again

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bumping this now again. Does this still need to be public with InternalApi?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, as conversations diverged to other topics since then, I did not got the chance to revamp the testing. As of now, these still need to be public with InternalApi.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this class has to be public due to testing purposes, would it make sense to add a final?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unfortunately, adding final prevents mocking the class.


private static final LoggerProvider LOGGER_PROVIDER =
LoggerProvider.forClazz(GrpcLoggingInterceptor.class);

ClientCall.Listener<?> currentListener; // expose for test setup

@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {

return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(
next.newCall(method, callOptions)) {
LogData.Builder logDataBuilder = LogData.builder();

@Override
public void start(Listener<RespT> responseListener, Metadata headers) {
recordServiceRpcAndRequestHeaders(
method.getServiceName(),
method.getFullMethodName(),
null, // endpoint is for http request only
metadataHeadersToMap(headers),
logDataBuilder,
LOGGER_PROVIDER);
SimpleForwardingClientCallListener<RespT> responseLoggingListener =
new SimpleForwardingClientCallListener<RespT>(responseListener) {
@Override
public void onHeaders(Metadata headers) {
recordResponseHeaders(
metadataHeadersToMap(headers), logDataBuilder, LOGGER_PROVIDER);
super.onHeaders(headers);
}

@Override
public void onMessage(RespT message) {
recordResponsePayload(message, logDataBuilder, LOGGER_PROVIDER);
super.onMessage(message);
}

@Override
public void onClose(Status status, Metadata trailers) {
logResponse(status.getCode().toString(), logDataBuilder, LOGGER_PROVIDER);
super.onClose(status, trailers);
}
};
currentListener = responseLoggingListener;
super.start(responseLoggingListener, headers);
}

@Override
public void sendMessage(ReqT message) {
logRequest(message, logDataBuilder, LOGGER_PROVIDER);
super.sendMessage(message);
}
};
}

// Helper methods for logging
private static Map<String, String> metadataHeadersToMap(Metadata headers) {

Map<String, String> headersMap = new HashMap<>();
executeWithTryCatch(
() -> {
for (String key : headers.keys()) {
// grpc header values can be either ASCII strings or binary
// https://grpc.io/docs/guides/metadata/#overview
// this condition identified binary headers and skip for logging
if (key.endsWith(Metadata.BINARY_HEADER_SUFFIX)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is more future proof to do something like !key.endsWith(Metadata.ASCII_STRING_MARSHALLER).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, there is no equivalent Metadata.ASCII_STRING_SUFFIX, only one for identifying binary header (here)

However, we should be good since according to grpc's guide here, values can only be either ASCII strings or binary data.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a comment on why we need this if statement?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added comment in 091ab38

continue;
}
Metadata.Key<String> metadataKey =
Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER);
String headerValue = headers.get(metadataKey);

headersMap.put(key, headerValue);
}
});

return headersMap;
}
}
Original file line number Diff line number Diff line change
@@ -681,6 +681,7 @@ private ManagedChannel createSingleChannel() throws IOException {
builder =
builder
.intercept(new GrpcChannelUUIDInterceptor())
.intercept(new GrpcLoggingInterceptor())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a thought (I don't know if there are any downsides or if this would work): Could we gate adding the logging interceptor here?

i.e. Check if the logging env var exists + is configured and only add an interceptor if so.

I think there was some mention about not using interceptors (potentially) so it may not work in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess there is no harm in adding a gate here for now.
In the future, another thought is to allow setting enable logging via system properties in addition to env var, in that case, it could change at runtime and this would not work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just thinking that having it gate there might be a way to remove all the if (loggingEnabled) {...} checks in LoggingUtils.java. But it may not be possible if we know there are future changes that would make this invalid.

.intercept(headerInterceptor)
.intercept(metadataHandlerInterceptor)
.userAgent(headerInterceptor.getUserAgentHeader())
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* 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.gax.grpc;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.google.api.gax.grpc.testing.FakeMethodDescriptor;
import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ClientInterceptors;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
import io.grpc.Status;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class GrpcLoggingInterceptorTest {
@Mock private Channel channel;

@Mock private ClientCall<String, Integer> call;

private static final MethodDescriptor<String, Integer> method = FakeMethodDescriptor.create();

@Test
void testInterceptor_basic() {
when(channel.newCall(Mockito.<MethodDescriptor<String, Integer>>any(), any(CallOptions.class)))
.thenReturn(call);
GrpcLoggingInterceptor interceptor = new GrpcLoggingInterceptor();
Channel intercepted = ClientInterceptors.intercept(channel, interceptor);
@SuppressWarnings("unchecked")
ClientCall.Listener<Integer> listener = mock(ClientCall.Listener.class);
ClientCall<String, Integer> interceptedCall = intercepted.newCall(method, CallOptions.DEFAULT);
// Simulate starting the call
interceptedCall.start(listener, new Metadata());
// Verify that the underlying call's start() method is invoked
verify(call).start(any(ClientCall.Listener.class), any(Metadata.class));

// Simulate sending a message
String requestMessage = "test request";
interceptedCall.sendMessage(requestMessage);
// Verify that the underlying call's sendMessage() method is invoked
verify(call).sendMessage(requestMessage);
}

@Test
void testInterceptor_responseListener() {
when(channel.newCall(Mockito.<MethodDescriptor<String, Integer>>any(), any(CallOptions.class)))
.thenReturn(call);
GrpcLoggingInterceptor interceptor = spy(new GrpcLoggingInterceptor());
Channel intercepted = ClientInterceptors.intercept(channel, interceptor);
@SuppressWarnings("unchecked")
ClientCall.Listener<Integer> listener = mock(ClientCall.Listener.class);
ClientCall<String, Integer> interceptedCall = intercepted.newCall(method, CallOptions.DEFAULT);
interceptedCall.start(listener, new Metadata());

// Simulate respond interceptor calls
Metadata responseHeaders = new Metadata();
responseHeaders.put(
Metadata.Key.of("test-header", Metadata.ASCII_STRING_MARSHALLER), "header-value");
interceptor.currentListener.onHeaders(responseHeaders);

interceptor.currentListener.onMessage(null);

Status status = Status.OK;
interceptor.currentListener.onClose(status, new Metadata());
}
}
6 changes: 6 additions & 0 deletions gax-java/gax-httpjson/pom.xml
Original file line number Diff line number Diff line change
@@ -78,6 +78,12 @@
<artifactId>auto-value-annotations</artifactId>
<scope>provided</scope>
</dependency>
<!-- Logging dependency -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<optional>true</optional>
</dependency>

<!-- test dependencies -->
<dependency>
Loading
Loading