Skip to content

Commit fefb436

Browse files
committed
add logging test setup in httpjson.
1 parent 4190dc7 commit fefb436

File tree

5 files changed

+188
-46
lines changed

5 files changed

+188
-46
lines changed

gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@
5151
@InternalApi
5252
public class GrpcLoggingInterceptor implements ClientInterceptor {
5353

54-
private static final Logger logger = LoggingUtils.getLogger(GrpcLoggingInterceptor.class);
55-
private static final Gson gson = new Gson();
54+
private static final Logger LOGGER = LoggingUtils.getLogger(GrpcLoggingInterceptor.class);
55+
private static final Gson GSON = new Gson();
5656

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

@@ -66,7 +66,7 @@ public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
6666

6767
@Override
6868
public void start(Listener<RespT> responseListener, Metadata headers) {
69-
logRequestInfo(method, logDataBuilder, logger);
69+
logRequestInfo(method, logDataBuilder, LOGGER);
7070
recordRequestHeaders(headers, logDataBuilder);
7171
SimpleForwardingClientCallListener<RespT> responseLoggingListener =
7272
new SimpleForwardingClientCallListener<RespT>(responseListener) {
@@ -124,65 +124,65 @@ <ReqT, RespT> void logRequestInfo(
124124

125125
private void recordRequestHeaders(Metadata headers, LogData.Builder logDataBuilder) {
126126
try {
127-
if (logger.isDebugEnabled()) {
127+
if (LOGGER.isDebugEnabled()) {
128128
JsonObject requestHeaders = mapHeadersToJsonObject(headers);
129-
logDataBuilder.requestHeaders(gson.toJson(requestHeaders));
129+
logDataBuilder.requestHeaders(GSON.toJson(requestHeaders));
130130
}
131131
} catch (Exception e) {
132-
logger.error("Error recording request headers", e);
132+
LOGGER.error("Error recording request headers", e);
133133
}
134134
}
135135

136136
void recordResponseHeaders(Metadata headers, LogData.Builder logDataBuilder) {
137137
try {
138-
if (logger.isDebugEnabled()) {
138+
if (LOGGER.isDebugEnabled()) {
139139
JsonObject responseHeaders = mapHeadersToJsonObject(headers);
140-
logDataBuilder.responseHeaders(gson.toJson(responseHeaders));
140+
logDataBuilder.responseHeaders(GSON.toJson(responseHeaders));
141141
}
142142
} catch (Exception e) {
143-
logger.error("Error recording response headers", e);
143+
LOGGER.error("Error recording response headers", e);
144144
}
145145
}
146146

147147
<RespT> void recordResponsePayload(RespT message, LogData.Builder logDataBuilder) {
148148
try {
149-
if (logger.isDebugEnabled()) {
150-
logDataBuilder.responsePayload(gson.toJsonTree(message));
149+
if (LOGGER.isDebugEnabled()) {
150+
logDataBuilder.responsePayload(GSON.toJsonTree(message));
151151
}
152152
} catch (Exception e) {
153-
logger.error("Error recording response payload", e);
153+
LOGGER.error("Error recording response payload", e);
154154
}
155155
}
156156

157157
void logResponse(String statusCode, LogData.Builder logDataBuilder) {
158158
try {
159159

160-
if (logger.isInfoEnabled()) {
160+
if (LOGGER.isInfoEnabled()) {
161161
logDataBuilder.responseStatus(statusCode);
162162
}
163-
if (logger.isInfoEnabled() && !logger.isDebugEnabled()) {
163+
if (LOGGER.isInfoEnabled() && !LOGGER.isDebugEnabled()) {
164164
Map<String, String> responseData = logDataBuilder.build().toMapResponse();
165-
LoggingUtils.logWithMDC(logger, Level.INFO, responseData, "Received Grpc response");
165+
LoggingUtils.logWithMDC(LOGGER, Level.INFO, responseData, "Received Grpc response");
166166
}
167-
if (logger.isDebugEnabled()) {
167+
if (LOGGER.isDebugEnabled()) {
168168
Map<String, String> responsedDetailsMap = logDataBuilder.build().toMapResponse();
169-
LoggingUtils.logWithMDC(logger, Level.DEBUG, responsedDetailsMap, "Received Grpc response");
169+
LoggingUtils.logWithMDC(LOGGER, Level.DEBUG, responsedDetailsMap, "Received Grpc response");
170170
}
171171
} catch (Exception e) {
172-
logger.error("Error logging request response", e);
172+
LOGGER.error("Error logging request response", e);
173173
}
174174
}
175175

176176
<RespT> void logRequestDetails(RespT message, LogData.Builder logDataBuilder) {
177177
try {
178-
if (logger.isDebugEnabled()) {
179-
logDataBuilder.requestPayload(gson.toJson(message));
178+
if (LOGGER.isDebugEnabled()) {
179+
logDataBuilder.requestPayload(GSON.toJson(message));
180180
Map<String, String> requestDetailsMap = logDataBuilder.build().toMapRequest();
181181
LoggingUtils.logWithMDC(
182-
logger, Level.DEBUG, requestDetailsMap, "Sending gRPC request: request payload");
182+
LOGGER, Level.DEBUG, requestDetailsMap, "Sending gRPC request: request payload");
183183
}
184184
} catch (Exception e) {
185-
logger.error("Error logging request details", e);
185+
LOGGER.error("Error logging request details", e);
186186
}
187187
}
188188

gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@
4646
@InternalApi
4747
public class HttpJsonLoggingInterceptor implements HttpJsonClientInterceptor {
4848

49-
private static final Logger logger = LoggingUtils.getLogger(HttpJsonLoggingInterceptor.class);
50-
private static final Gson gson = new Gson();
49+
private static final Logger LOGGER = LoggingUtils.getLogger(HttpJsonLoggingInterceptor.class);
50+
private static final Gson GSON = new Gson();
5151

5252
@Override
5353
public <ReqT, RespT> HttpJsonClientCall<ReqT, RespT> interceptCall(
@@ -65,7 +65,7 @@ public <ReqT, RespT> HttpJsonClientCall<ReqT, RespT> interceptCall(
6565
public void start(
6666
HttpJsonClientCall.Listener<RespT> responseListener, HttpJsonMetadata headers) {
6767

68-
logRequestInfo(method, endpoint, logDataBuilder);
68+
logRequestInfo(method, endpoint, logDataBuilder, LOGGER);
6969
recordRequestHeaders(headers, logDataBuilder);
7070

7171
Listener<RespT> forwardingResponseListener =
@@ -106,8 +106,11 @@ public void sendMessage(ReqT message) {
106106

107107
// Helper methods for logging,
108108
// some duplications with grpc equivalent to avoid exposing as public method
109-
private <ReqT, RespT> void logRequestInfo(
110-
ApiMethodDescriptor<ReqT, RespT> method, String endpoint, LogData.Builder logDataBuilder) {
109+
<ReqT, RespT> void logRequestInfo(
110+
ApiMethodDescriptor<ReqT, RespT> method,
111+
String endpoint,
112+
LogData.Builder logDataBuilder,
113+
Logger logger) {
111114
try {
112115
if (logger.isInfoEnabled()) {
113116
logDataBuilder
@@ -127,70 +130,70 @@ private <ReqT, RespT> void logRequestInfo(
127130

128131
private void recordRequestHeaders(HttpJsonMetadata headers, LogData.Builder logDataBuilder) {
129132
try {
130-
if (logger.isDebugEnabled()) {
133+
if (LOGGER.isDebugEnabled()) {
131134
JsonObject requestHeaders = new JsonObject();
132135
headers
133136
.getHeaders()
134137
.forEach((key, value) -> requestHeaders.addProperty(key, value.toString()));
135-
logDataBuilder.requestHeaders(gson.toJson(requestHeaders));
138+
logDataBuilder.requestHeaders(GSON.toJson(requestHeaders));
136139
}
137140
} catch (Exception e) {
138-
logger.error("Error recording request headers", e);
141+
LOGGER.error("Error recording request headers", e);
139142
}
140143
}
141144

142145
private void recordResponseHeaders(
143146
HttpJsonMetadata responseHeaders, LogData.Builder logDataBuilder) {
144147
try {
145-
if (logger.isDebugEnabled()) {
148+
if (LOGGER.isDebugEnabled()) {
146149

147150
Map<String, List<String>> map = new HashMap<>();
148151
responseHeaders.getHeaders().forEach((key, value) -> map.put(key, (List<String>) value));
149-
logDataBuilder.responseHeaders(gson.toJson(map));
152+
logDataBuilder.responseHeaders(GSON.toJson(map));
150153
}
151154
} catch (Exception e) {
152-
logger.error("Error recording response headers", e);
155+
LOGGER.error("Error recording response headers", e);
153156
}
154157
}
155158

156159
private <RespT> void recordResponsePayload(RespT message, LogData.Builder logDataBuilder) {
157160
try {
158-
if (logger.isDebugEnabled()) {
159-
logDataBuilder.responsePayload(gson.toJsonTree(message));
161+
if (LOGGER.isDebugEnabled()) {
162+
logDataBuilder.responsePayload(GSON.toJsonTree(message));
160163
}
161164
} catch (Exception e) {
162-
logger.error("Error recording response payload", e);
165+
LOGGER.error("Error recording response payload", e);
163166
}
164167
}
165168

166169
private void logResponse(int statusCode, LogData.Builder logDataBuilder) {
167170
try {
168-
if (logger.isInfoEnabled()) {
171+
if (LOGGER.isInfoEnabled()) {
169172
logDataBuilder.responseStatus(String.valueOf(statusCode));
170173
}
171-
if (logger.isInfoEnabled() && !logger.isDebugEnabled()) {
174+
if (LOGGER.isInfoEnabled() && !LOGGER.isDebugEnabled()) {
172175
Map<String, String> responseData = logDataBuilder.build().toMapResponse();
173-
LoggingUtils.logWithMDC(logger, Level.INFO, responseData, "Received HTTP response");
176+
LoggingUtils.logWithMDC(LOGGER, Level.INFO, responseData, "Received HTTP response");
174177
}
175-
if (logger.isDebugEnabled()) {
178+
if (LOGGER.isDebugEnabled()) {
176179
Map<String, String> responsedDetailsMap = logDataBuilder.build().toMapResponse();
177-
LoggingUtils.logWithMDC(logger, Level.DEBUG, responsedDetailsMap, "Received HTTP response");
180+
LoggingUtils.logWithMDC(LOGGER, Level.DEBUG, responsedDetailsMap, "Received HTTP response");
178181
}
179182
} catch (Exception e) {
180-
logger.error("Error logging request response", e);
183+
LOGGER.error("Error logging request response", e);
181184
}
182185
}
183186

184187
private <RespT> void logRequestDetails(RespT message, LogData.Builder logDataBuilder) {
185188
try {
186-
if (logger.isDebugEnabled()) {
187-
logDataBuilder.requestPayload(gson.toJson(message));
189+
if (LOGGER.isDebugEnabled()) {
190+
logDataBuilder.requestPayload(GSON.toJson(message));
188191
Map<String, String> requestDetailsMap = logDataBuilder.build().toMapRequest();
189192
LoggingUtils.logWithMDC(
190-
logger, Level.DEBUG, requestDetailsMap, "Sending HTTP request: request payload");
193+
LOGGER, Level.DEBUG, requestDetailsMap, "Sending HTTP request: request payload");
191194
}
192195
} catch (Exception e) {
193-
logger.error("Error logging request details", e);
196+
LOGGER.error("Error logging request details", e);
194197
}
195198
}
196199
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions are
6+
* met:
7+
*
8+
* * Redistributions of source code must retain the above copyright
9+
* notice, this list of conditions and the following disclaimer.
10+
* * Redistributions in binary form must reproduce the above
11+
* copyright notice, this list of conditions and the following disclaimer
12+
* in the documentation and/or other materials provided with the
13+
* distribution.
14+
* * Neither the name of Google LLC nor the names of its
15+
* contributors may be used to endorse or promote products derived from
16+
* this software without specific prior written permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
*/
30+
31+
package com.google.api.gax.httpjson;
32+
33+
import static org.junit.Assert.assertEquals;
34+
import static org.mockito.Mockito.mock;
35+
36+
import ch.qos.logback.classic.Level;
37+
import com.google.api.gax.httpjson.ApiMethodDescriptor.MethodType;
38+
import com.google.api.gax.logging.LogData;
39+
import org.junit.jupiter.api.Assertions;
40+
import org.junit.jupiter.api.Test;
41+
import org.slf4j.Logger;
42+
import org.slf4j.LoggerFactory;
43+
44+
class HttpJsonLoggingInterceptorTest {
45+
46+
private static final Logger LOGGER =
47+
LoggerFactory.getLogger(HttpJsonLoggingInterceptorTest.class);
48+
49+
@SuppressWarnings("unchecked")
50+
private static final ApiMethodDescriptor<String, Integer> method =
51+
ApiMethodDescriptor.newBuilder()
52+
.setType(MethodType.UNARY)
53+
.setRequestFormatter(mock(HttpRequestFormatter.class))
54+
.setRequestFormatter(mock(HttpRequestFormatter.class))
55+
.setFullMethodName("FakeClient/fake-method")
56+
.setResponseParser(mock(HttpResponseParser.class))
57+
.build();
58+
59+
@Test
60+
void testLogRequestInfo() {
61+
62+
TestAppender testAppender = setupTestLogger(HttpJsonLoggingInterceptorTest.class);
63+
HttpJsonLoggingInterceptor interceptor = new HttpJsonLoggingInterceptor();
64+
interceptor.logRequestInfo(method, "fake.endpoint", LogData.builder(), LOGGER);
65+
66+
Assertions.assertEquals(1, testAppender.events.size());
67+
assertEquals(Level.INFO, testAppender.events.get(0).getLevel());
68+
assertEquals(
69+
"{\"request.url\":\"fake.endpoint\",\"message\":\"Sending HTTP request\",\"rpcName\":\"FakeClient/fake-method\"}",
70+
testAppender.events.get(0).getMessage());
71+
testAppender.stop();
72+
}
73+
74+
private TestAppender setupTestLogger(Class<?> clazz) {
75+
TestAppender testAppender = new TestAppender();
76+
testAppender.start();
77+
Logger logger = LoggerFactory.getLogger(clazz);
78+
((ch.qos.logback.classic.Logger) logger).addAppender(testAppender);
79+
return testAppender;
80+
}
81+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions are
6+
* met:
7+
*
8+
* * Redistributions of source code must retain the above copyright
9+
* notice, this list of conditions and the following disclaimer.
10+
* * Redistributions in binary form must reproduce the above
11+
* copyright notice, this list of conditions and the following disclaimer
12+
* in the documentation and/or other materials provided with the
13+
* distribution.
14+
* * Neither the name of Google LLC nor the names of its
15+
* contributors may be used to endorse or promote products derived from
16+
* this software without specific prior written permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
*/
30+
31+
package com.google.api.gax.httpjson;
32+
33+
import ch.qos.logback.classic.spi.ILoggingEvent;
34+
import ch.qos.logback.core.AppenderBase;
35+
import java.util.ArrayList;
36+
import java.util.List;
37+
38+
/** Logback appender used to set up tests. */
39+
public class TestAppender extends AppenderBase<ILoggingEvent> {
40+
public List<ILoggingEvent> events = new ArrayList<>();
41+
42+
@Override
43+
protected void append(ILoggingEvent eventObject) {
44+
// triggering Logback to capture the current MDC context and store it with the log event
45+
eventObject.getMDCPropertyMap();
46+
events.add(eventObject);
47+
}
48+
49+
public void clearEvents() {
50+
events.clear();
51+
}
52+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<configuration>
2+
<appender name="TEST_APPENDER" class="com.google.api.gax.httpjson.TestAppender" />
3+
<root level="INFO">
4+
<appender-ref ref="TEST_APPENDER" />
5+
</root>
6+
</configuration>

0 commit comments

Comments
 (0)