From 59b5d739cbb0269325ab92643bcedf65147a4892 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 19 Nov 2024 11:16:25 -0500 Subject: [PATCH 01/92] poc: add log in grpc interceptor. --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 74 +++++++ .../InstantiatingGrpcChannelProvider.java | 1 + gax-java/gax/pom.xml | 6 + .../com/google/api/gax/logging/Logger.java | 193 ++++++++++++++++++ .../google/api/gax/logging/LoggingUtils.java | 48 +++++ .../com/google/api/gax/rpc/ClientContext.java | 8 + pom.xml | 1 + showcase/gapic-showcase/pom.xml | 24 +++ .../src/test/resources/logback-test.xml | 19 ++ 9 files changed, 374 insertions(+) create mode 100644 gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java create mode 100644 gax-java/gax/src/main/java/com/google/api/gax/logging/Logger.java create mode 100644 gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java create mode 100644 showcase/gapic-showcase/src/test/resources/logback-test.xml diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java new file mode 100644 index 0000000000..ff21d3ebf7 --- /dev/null +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -0,0 +1,74 @@ +package com.google.api.gax.grpc; + +import com.google.api.gax.logging.LoggingUtils; +import com.google.gson.Gson; +import io.grpc.*; +import org.slf4j.Logger; +import org.slf4j.MDC; + +public class GrpcLoggingInterceptor implements ClientInterceptor { + + private static final Logger logger = LoggingUtils.getLogger(GrpcLoggingInterceptor.class); + private static final Gson gson = new Gson(); + + @Override + public ClientCall interceptCall( + MethodDescriptor method, CallOptions callOptions, Channel next) { + + return new ForwardingClientCall.SimpleForwardingClientCall( + next.newCall(method, callOptions)) { + + @Override + public void start(Listener responseListener, Metadata headers) { + if (LoggingUtils.isLoggingEnabled()) { + // Capture request details + String serviceName = method.getServiceName(); + String methodName = method.getFullMethodName(); + + // Add request details to MDC// Example system + MDC.put("serviceName", serviceName); + MDC.put("rpcName", methodName); + // Capture and log headers + headers + .keys() + .forEach( + key -> { + Metadata.Key metadataKey = + Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER); + String headerValue = headers.get(metadataKey); + MDC.put("request.headers:" + key, headerValue); + }); + + logger.debug("Sending gRPC request"); + } + + super.start( + new ForwardingClientCallListener.SimpleForwardingClientCallListener( + responseListener) { + @Override + public void onMessage(RespT message) { + MDC.put("response.payload", gson.toJson(message)); + logger.debug("Received gRPC response."); + + super.onMessage(message); + } + + @Override + public void onClose(Status status, Metadata trailers) { + MDC.put("response.status", status.getCode().name()); + logger.info("gRPC request finished with status: {}", status); + MDC.clear(); // Clear MDC after the request + super.onClose(status, trailers); + } + }, + headers); + } + + @Override + public void sendMessage(ReqT message) { + MDC.put("request.payload", gson.toJson(message)); + super.sendMessage(message); + } + }; + } +} diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java index ae4d7f9e51..1f23868cd5 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java @@ -467,6 +467,7 @@ private ManagedChannel createSingleChannel() throws IOException { builder = builder .intercept(new GrpcChannelUUIDInterceptor()) + .intercept(new GrpcLoggingInterceptor()) .intercept(headerInterceptor) .intercept(metadataHandlerInterceptor) .userAgent(headerInterceptor.getUserAgentHeader()) diff --git a/gax-java/gax/pom.xml b/gax-java/gax/pom.xml index d2e40b6a48..1c919e09d5 100644 --- a/gax-java/gax/pom.xml +++ b/gax-java/gax/pom.xml @@ -69,6 +69,12 @@ opentelemetry-api true + + org.slf4j + slf4j-api + 2.0.16 + + diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/Logger.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/Logger.java new file mode 100644 index 0000000000..9f5d7fc15c --- /dev/null +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/Logger.java @@ -0,0 +1,193 @@ +package com.google.api.gax.logging; + +import java.util.function.Supplier; +import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; + +public final class Logger { + private final org.slf4j.Logger log; + + Logger(org.slf4j.Logger log) { + this.log = log; + } + + public org.slf4j.Logger logger() { + return log; + } + + /** + * Checks if info is enabled and if so logs the supplied message + * + * @param msg - supplier for the log message + */ + public void info(Supplier msg) { + if (log.isInfoEnabled()) { + log.info(msg.get()); + } + } + + /** + * Checks if info is enabled and if so logs the supplied message and exception + * + * @param msg - supplier for the log message + * @param throwable - a throwable to log + */ + public void info(Supplier msg, Throwable throwable) { + if (log.isInfoEnabled()) { + log.info(msg.get(), throwable); + } + } + + /** + * Checks if error is enabled and if so logs the supplied message + * + * @param msg - supplier for the log message + */ + public void error(Supplier msg) { + if (log.isErrorEnabled()) { + log.error(msg.get()); + } + } + + /** + * Checks if error is enabled and if so logs the supplied message and exception + * + * @param msg - supplier for the log message + * @param throwable - a throwable to log + */ + public void error(Supplier msg, Throwable throwable) { + if (log.isErrorEnabled()) { + log.error(msg.get(), throwable); + } + } + + /** + * Checks if debug is enabled and if so logs the supplied message + * + * @param msg - supplier for the log message + */ + public void debug(Supplier msg) { + if (log.isDebugEnabled()) { + log.debug(msg.get()); + } + } + + /** + * Checks if debug is enabled and if so logs the supplied message and exception + * + * @param msg - supplier for the log message + * @param throwable - a throwable to log + */ + public void debug(Supplier msg, Throwable throwable) { + if (log.isDebugEnabled()) { + log.debug(msg.get(), throwable); + } + } + + /** + * Checks if warn is enabled and if so logs the supplied message + * + * @param msg - supplier for the log message + */ + public void warn(Supplier msg) { + if (log.isWarnEnabled()) { + log.warn(msg.get()); + } + } + + /** + * Checks if warn is enabled and if so logs the supplied message and exception + * + * @param msg - supplier for the log message + * @param throwable - a throwable to log + */ + public void warn(Supplier msg, Throwable throwable) { + if (log.isWarnEnabled()) { + log.warn(msg.get(), throwable); + } + } + + /** + * Checks if trace is enabled and if so logs the supplied message + * + * @param msg - supplier for the log message + */ + public void trace(Supplier msg) { + if (log.isTraceEnabled()) { + log.trace(msg.get()); + } + } + + /** + * Checks if trace is enabled and if so logs the supplied message and exception + * + * @param msg - supplier for the log message + * @param throwable - a throwable to log + */ + public void trace(Supplier msg, Throwable throwable) { + if (log.isTraceEnabled()) { + log.trace(msg.get(), throwable); + } + } + + /** + * Determines if the provided log-level is enabled. + * + * @param logLevel the SLF4J log level enum + * @return whether that level is enabled + */ + public boolean isLoggingLevelEnabled(Level logLevel) { + switch (logLevel) { + case TRACE: + return log.isTraceEnabled(); + case DEBUG: + return log.isDebugEnabled(); + case INFO: + return log.isInfoEnabled(); + case WARN: + return log.isWarnEnabled(); + case ERROR: + return log.isErrorEnabled(); + default: + throw new IllegalStateException("Unsupported log level: " + logLevel); + } + } + + /** + * Log a message at the given log level (if it is enabled). + * + * @param logLevel the SLF4J log level + * @param msg supplier for the log message + */ + public void log(Level logLevel, Supplier msg) { + switch (logLevel) { + case TRACE: + trace(msg); + break; + case DEBUG: + debug(msg); + break; + case INFO: + info(msg); + break; + case WARN: + warn(msg); + break; + case ERROR: + error(msg); + break; + default: + throw new IllegalStateException("Unsupported log level: " + logLevel); + } + } + + /** + * Static factory to get a logger instance for a given class + * + * @param clz - class to get the logger for + * @return a Logger instance + */ + public static Logger loggerFor(Class clz) { + return new Logger(LoggerFactory.getLogger(clz)); + } +} diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java new file mode 100644 index 0000000000..10b7b2425e --- /dev/null +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java @@ -0,0 +1,48 @@ +package com.google.api.gax.logging; + +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.slf4j.event.Level; + +public class LoggingUtils { + + public static Logger getLogger(Class clazz) { + return LoggerFactory.getLogger(clazz); + } + + public static boolean isLoggingEnabled() { + String enableLogging = System.getenv("GOOGLE_SDK_JAVA_LOGGING"); + return "true".equalsIgnoreCase(enableLogging); + } + + public static void log(Logger logger, Level level, String msg, Map contextMap) { + + if (LoggingUtils.isLoggingEnabled()) { + MDC.setContextMap(contextMap); + switch (level) { + case TRACE: + logger.trace(msg); + break; + case DEBUG: + logger.debug(msg); + break; + case INFO: + logger.info(msg); + break; + case WARN: + logger.warn(msg); + break; + case ERROR: + logger.error(msg); + break; + default: + logger.info(msg); + // Default to INFO level if level is invalid + break; + } + MDC.clear(); + } + } +} diff --git a/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientContext.java b/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientContext.java index 5bce1ac6bb..4801d02b4c 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientContext.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientContext.java @@ -40,6 +40,7 @@ import com.google.api.gax.core.BackgroundResource; import com.google.api.gax.core.ExecutorAsBackgroundResource; import com.google.api.gax.core.ExecutorProvider; +import com.google.api.gax.logging.LoggingUtils; import com.google.api.gax.rpc.internal.QuotaProjectIdHidingCredentials; import com.google.api.gax.tracing.ApiTracerFactory; import com.google.api.gax.tracing.BaseApiTracerFactory; @@ -63,6 +64,8 @@ import java.util.concurrent.ScheduledExecutorService; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.event.Level; /** * Encapsulates client state, including executor, credentials, and transport channel. @@ -72,6 +75,8 @@ */ @AutoValue public abstract class ClientContext { + + private static final Logger LOGGER = LoggingUtils.getLogger(ClientContext.class); private static final String QUOTA_PROJECT_ID_HEADER_KEY = "x-goog-user-project"; /** @@ -170,6 +175,9 @@ public static ClientContext create(ClientSettings settings) throws IOException { * settings. */ public static ClientContext create(StubSettings settings) throws IOException { + if (LoggingUtils.isLoggingEnabled()) { + LoggingUtils.log(LOGGER, Level.INFO, "a dummy message", Collections.emptyMap()); + } ApiClock clock = settings.getClock(); ExecutorProvider backgroundExecutorProvider = settings.getBackgroundExecutorProvider(); diff --git a/pom.xml b/pom.xml index d207468e77..d4379722a8 100644 --- a/pom.xml +++ b/pom.xml @@ -24,6 +24,7 @@ gapic-generator-java-bom java-shared-dependencies sdk-platform-java-config + showcase diff --git a/showcase/gapic-showcase/pom.xml b/showcase/gapic-showcase/pom.xml index 2549eb199f..0cea523ccd 100644 --- a/showcase/gapic-showcase/pom.xml +++ b/showcase/gapic-showcase/pom.xml @@ -215,5 +215,29 @@ opentelemetry-sdk-testing test + + + org.slf4j + slf4j-api + 2.0.16 + + + ch.qos.logback + logback-classic + 1.5.11 + test + + + net.logstash.logback + logstash-logback-encoder + 7.2 + test + + + org.codehaus.janino + janino + 3.1.9 + test + diff --git a/showcase/gapic-showcase/src/test/resources/logback-test.xml b/showcase/gapic-showcase/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..533c9edb64 --- /dev/null +++ b/showcase/gapic-showcase/src/test/resources/logback-test.xml @@ -0,0 +1,19 @@ + + + + + + + + logger.equals("com.google.api.gax.grpc.GrpcLoggingInterceptor") + + + ACCEPT + DENY + + + + + + + \ No newline at end of file From d34ec88de5940c7810a1285d72f220058ce2af4c Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 20 Nov 2024 10:50:49 -0500 Subject: [PATCH 02/92] try out basics with httpjson interceptor. --- .../httpjson/HttpJsonLoggingInterceptor.java | 60 +++++++++++++++++++ .../InstantiatingHttpJsonChannelProvider.java | 1 + .../com/google/api/gax/rpc/ClientContext.java | 11 ++-- .../src/test/resources/logback-test.xml | 1 + 4 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java new file mode 100644 index 0000000000..4195d9c386 --- /dev/null +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -0,0 +1,60 @@ +package com.google.api.gax.httpjson; + +import com.google.api.gax.httpjson.ForwardingHttpJsonClientCall.SimpleForwardingHttpJsonClientCall; +import com.google.gson.Gson; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; + +public class HttpJsonLoggingInterceptor implements HttpJsonClientInterceptor { + + private static final Logger logger = LoggerFactory.getLogger(HttpJsonLoggingInterceptor.class); + private static final Gson gson = new Gson(); + + @Override + public HttpJsonClientCall interceptCall( + ApiMethodDescriptor method, + HttpJsonCallOptions callOptions, + HttpJsonChannel next) { + + return new SimpleForwardingHttpJsonClientCall(next.newCall(method, callOptions)) { + @Override + public void start( + HttpJsonClientCall.Listener responseListener, HttpJsonMetadata headers) { + // Capture request details + String methodName = method.getFullMethodName(); + + // Add request details to MDC + MDC.put("method", methodName); + + // Capture and log headers + for (Map.Entry header : headers.getHeaders().entrySet()) { + MDC.put("header." + header.getKey(), header.getValue().toString()); + } + + logger.info("Sending HTTP request"); + + super.start( + new HttpJsonClientCall.Listener() { + @Override + public void onMessage(RespT message) { + MDC.put("response.payload", gson.toJson(message)); + logger.debug("Received HTTP response."); + + responseListener.onMessage(message); + } + + @Override + public void onClose(int status, HttpJsonMetadata trailers) { + MDC.put("response.status", String.valueOf(status)); + logger.info("HTTP request finished with status: {}", status); + MDC.clear(); // Clear MDC after the request + responseListener.onClose(status, trailers); + } + }, + headers); + } + }; + } +} diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProvider.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProvider.java index f92bdf299c..1912bc5e29 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProvider.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProvider.java @@ -196,6 +196,7 @@ private HttpJsonTransportChannel createChannel() throws IOException, GeneralSecu HttpJsonClientInterceptor headerInterceptor = new HttpJsonHeaderInterceptor(headerProvider.getHeaders()); + channel = new ManagedHttpJsonInterceptorChannel(channel, new HttpJsonLoggingInterceptor()); channel = new ManagedHttpJsonInterceptorChannel(channel, headerInterceptor); if (interceptorProvider != null && interceptorProvider.getInterceptors() != null) { for (HttpJsonClientInterceptor interceptor : interceptorProvider.getInterceptors()) { diff --git a/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientContext.java b/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientContext.java index 4801d02b4c..aab38b8162 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientContext.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientContext.java @@ -40,7 +40,6 @@ import com.google.api.gax.core.BackgroundResource; import com.google.api.gax.core.ExecutorAsBackgroundResource; import com.google.api.gax.core.ExecutorProvider; -import com.google.api.gax.logging.LoggingUtils; import com.google.api.gax.rpc.internal.QuotaProjectIdHidingCredentials; import com.google.api.gax.tracing.ApiTracerFactory; import com.google.api.gax.tracing.BaseApiTracerFactory; @@ -64,8 +63,6 @@ import java.util.concurrent.ScheduledExecutorService; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import org.slf4j.Logger; -import org.slf4j.event.Level; /** * Encapsulates client state, including executor, credentials, and transport channel. @@ -76,7 +73,7 @@ @AutoValue public abstract class ClientContext { - private static final Logger LOGGER = LoggingUtils.getLogger(ClientContext.class); + // private static final Logger LOGGER = LoggingUtils.getLogger(ClientContext.class); private static final String QUOTA_PROJECT_ID_HEADER_KEY = "x-goog-user-project"; /** @@ -175,9 +172,9 @@ public static ClientContext create(ClientSettings settings) throws IOException { * settings. */ public static ClientContext create(StubSettings settings) throws IOException { - if (LoggingUtils.isLoggingEnabled()) { - LoggingUtils.log(LOGGER, Level.INFO, "a dummy message", Collections.emptyMap()); - } + // if (LoggingUtils.isLoggingEnabled()) { + // LoggingUtils.log(LOGGER, Level.INFO, "a dummy message", Collections.emptyMap()); + // } ApiClock clock = settings.getClock(); ExecutorProvider backgroundExecutorProvider = settings.getBackgroundExecutorProvider(); diff --git a/showcase/gapic-showcase/src/test/resources/logback-test.xml b/showcase/gapic-showcase/src/test/resources/logback-test.xml index 533c9edb64..02b2596516 100644 --- a/showcase/gapic-showcase/src/test/resources/logback-test.xml +++ b/showcase/gapic-showcase/src/test/resources/logback-test.xml @@ -6,6 +6,7 @@ logger.equals("com.google.api.gax.grpc.GrpcLoggingInterceptor") + logger.equals("com.google.api.gax.httpjson.HttpJsonLoggingInterceptor") ACCEPT From b6417ae719dde3927fa59e1ad0179a0476091285 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Mon, 25 Nov 2024 16:45:08 -0500 Subject: [PATCH 03/92] implement conditional logging, use JUL when no binding. log formatting needs more work. --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 95 +++-- gax-java/gax/pom.xml | 2 +- .../com/google/api/gax/logging/Logger.java | 193 ---------- .../google/api/gax/logging/LoggingUtils.java | 363 ++++++++++++++++-- .../src/test/resources/logging.properties | 4 + 5 files changed, 406 insertions(+), 251 deletions(-) delete mode 100644 gax-java/gax/src/main/java/com/google/api/gax/logging/Logger.java create mode 100644 showcase/gapic-showcase/src/test/resources/logging.properties diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index ff21d3ebf7..9a92921987 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -2,14 +2,18 @@ import com.google.api.gax.logging.LoggingUtils; import com.google.gson.Gson; +import com.google.gson.JsonObject; import io.grpc.*; +import java.util.HashMap; +import java.util.Map; import org.slf4j.Logger; -import org.slf4j.MDC; public class GrpcLoggingInterceptor implements ClientInterceptor { private static final Logger logger = LoggingUtils.getLogger(GrpcLoggingInterceptor.class); private static final Gson gson = new Gson(); + private JsonObject serviceAndRpc = new JsonObject(); + private JsonObject requestLogData = new JsonObject(); @Override public ClientCall interceptCall( @@ -20,44 +24,69 @@ public ClientCall interceptCall( @Override public void start(Listener responseListener, Metadata headers) { - if (LoggingUtils.isLoggingEnabled()) { - // Capture request details - String serviceName = method.getServiceName(); - String methodName = method.getFullMethodName(); - - // Add request details to MDC// Example system - MDC.put("serviceName", serviceName); - MDC.put("rpcName", methodName); - // Capture and log headers - headers - .keys() - .forEach( - key -> { - Metadata.Key metadataKey = - Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER); - String headerValue = headers.get(metadataKey); - MDC.put("request.headers:" + key, headerValue); - }); - - logger.debug("Sending gRPC request"); - } + // if (LoggingUtils.isLoggingEnabled()) { + // Capture request details + String serviceName = method.getServiceName(); + String methodName = method.getFullMethodName(); + + serviceAndRpc.addProperty("serviceName", serviceName); + serviceAndRpc.addProperty("rpcName", methodName); + + JsonObject responseLogData = new JsonObject(); + // Add request details to MDC// Example system + // MDC.put("serviceName", serviceName); + // MDC.put("rpcName", methodName); + // Capture and log headers + headers + .keys() + .forEach( + key -> { + Metadata.Key metadataKey = + Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER); + String headerValue = headers.get(metadataKey); + // MDC.put("request.headers:" + key, headerValue); + requestLogData.addProperty("request.headers:" + key, headerValue); + }); + + requestLogData.addProperty("message", "Sending gRPC request"); + // logger.debug("Sending gRPC request"); + // } super.start( new ForwardingClientCallListener.SimpleForwardingClientCallListener( responseListener) { @Override public void onMessage(RespT message) { - MDC.put("response.payload", gson.toJson(message)); - logger.debug("Received gRPC response."); + responseLogData.addProperty("response.payload", gson.toJson(message)); super.onMessage(message); } @Override public void onClose(Status status, Metadata trailers) { - MDC.put("response.status", status.getCode().name()); - logger.info("gRPC request finished with status: {}", status); - MDC.clear(); // Clear MDC after the request + responseLogData.addProperty("response.status", status.getCode().name()); + responseLogData.addProperty( + "message", String.format("gRPC request finished with status: %s", status)); + + // Create a JsonObject for response headers + JsonObject responseHeaders = new JsonObject(); + + // Access and add response headers to the JsonObject + trailers + .keys() + .forEach( + key -> { + Metadata.Key metadataKey = + Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER); + String headerValue = trailers.get(metadataKey); + responseHeaders.addProperty(key, headerValue); + }); + responseLogData.add("response.headers", responseHeaders); + + logger.debug( + gson.toJson(LoggingUtils.mergeJsonObject(serviceAndRpc, responseLogData))); + // logger.info("gRPC request finished with status: {}", status); + // MDC.clear(); // Clear MDC after the request super.onClose(status, trailers); } }, @@ -66,7 +95,17 @@ public void onClose(Status status, Metadata trailers) { @Override public void sendMessage(ReqT message) { - MDC.put("request.payload", gson.toJson(message)); + // MDC.put("request.payload", gson.toJson(message)); + requestLogData.addProperty("request.payload", gson.toJson(message)); + + Map map = new HashMap<>(); + serviceAndRpc + .entrySet() + .forEach(entry -> map.put(entry.getKey(), entry.getValue().getAsString())); + // MDC.setContextMap(map); + // logger.info(new MapMessage(map)); + logger.info(gson.toJson(LoggingUtils.mergeJsonObject(serviceAndRpc, requestLogData))); + // MDC.clear(); super.sendMessage(message); } }; diff --git a/gax-java/gax/pom.xml b/gax-java/gax/pom.xml index 1c919e09d5..5ada305a25 100644 --- a/gax-java/gax/pom.xml +++ b/gax-java/gax/pom.xml @@ -69,11 +69,11 @@ opentelemetry-api true + org.slf4j slf4j-api 2.0.16 - diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/Logger.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/Logger.java deleted file mode 100644 index 9f5d7fc15c..0000000000 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/Logger.java +++ /dev/null @@ -1,193 +0,0 @@ -package com.google.api.gax.logging; - -import java.util.function.Supplier; -import org.slf4j.LoggerFactory; -import org.slf4j.event.Level; - -public final class Logger { - private final org.slf4j.Logger log; - - Logger(org.slf4j.Logger log) { - this.log = log; - } - - public org.slf4j.Logger logger() { - return log; - } - - /** - * Checks if info is enabled and if so logs the supplied message - * - * @param msg - supplier for the log message - */ - public void info(Supplier msg) { - if (log.isInfoEnabled()) { - log.info(msg.get()); - } - } - - /** - * Checks if info is enabled and if so logs the supplied message and exception - * - * @param msg - supplier for the log message - * @param throwable - a throwable to log - */ - public void info(Supplier msg, Throwable throwable) { - if (log.isInfoEnabled()) { - log.info(msg.get(), throwable); - } - } - - /** - * Checks if error is enabled and if so logs the supplied message - * - * @param msg - supplier for the log message - */ - public void error(Supplier msg) { - if (log.isErrorEnabled()) { - log.error(msg.get()); - } - } - - /** - * Checks if error is enabled and if so logs the supplied message and exception - * - * @param msg - supplier for the log message - * @param throwable - a throwable to log - */ - public void error(Supplier msg, Throwable throwable) { - if (log.isErrorEnabled()) { - log.error(msg.get(), throwable); - } - } - - /** - * Checks if debug is enabled and if so logs the supplied message - * - * @param msg - supplier for the log message - */ - public void debug(Supplier msg) { - if (log.isDebugEnabled()) { - log.debug(msg.get()); - } - } - - /** - * Checks if debug is enabled and if so logs the supplied message and exception - * - * @param msg - supplier for the log message - * @param throwable - a throwable to log - */ - public void debug(Supplier msg, Throwable throwable) { - if (log.isDebugEnabled()) { - log.debug(msg.get(), throwable); - } - } - - /** - * Checks if warn is enabled and if so logs the supplied message - * - * @param msg - supplier for the log message - */ - public void warn(Supplier msg) { - if (log.isWarnEnabled()) { - log.warn(msg.get()); - } - } - - /** - * Checks if warn is enabled and if so logs the supplied message and exception - * - * @param msg - supplier for the log message - * @param throwable - a throwable to log - */ - public void warn(Supplier msg, Throwable throwable) { - if (log.isWarnEnabled()) { - log.warn(msg.get(), throwable); - } - } - - /** - * Checks if trace is enabled and if so logs the supplied message - * - * @param msg - supplier for the log message - */ - public void trace(Supplier msg) { - if (log.isTraceEnabled()) { - log.trace(msg.get()); - } - } - - /** - * Checks if trace is enabled and if so logs the supplied message and exception - * - * @param msg - supplier for the log message - * @param throwable - a throwable to log - */ - public void trace(Supplier msg, Throwable throwable) { - if (log.isTraceEnabled()) { - log.trace(msg.get(), throwable); - } - } - - /** - * Determines if the provided log-level is enabled. - * - * @param logLevel the SLF4J log level enum - * @return whether that level is enabled - */ - public boolean isLoggingLevelEnabled(Level logLevel) { - switch (logLevel) { - case TRACE: - return log.isTraceEnabled(); - case DEBUG: - return log.isDebugEnabled(); - case INFO: - return log.isInfoEnabled(); - case WARN: - return log.isWarnEnabled(); - case ERROR: - return log.isErrorEnabled(); - default: - throw new IllegalStateException("Unsupported log level: " + logLevel); - } - } - - /** - * Log a message at the given log level (if it is enabled). - * - * @param logLevel the SLF4J log level - * @param msg supplier for the log message - */ - public void log(Level logLevel, Supplier msg) { - switch (logLevel) { - case TRACE: - trace(msg); - break; - case DEBUG: - debug(msg); - break; - case INFO: - info(msg); - break; - case WARN: - warn(msg); - break; - case ERROR: - error(msg); - break; - default: - throw new IllegalStateException("Unsupported log level: " + logLevel); - } - } - - /** - * Static factory to get a logger instance for a given class - * - * @param clz - class to get the logger for - * @return a Logger instance - */ - public static Logger loggerFor(Class clz) { - return new Logger(LoggerFactory.getLogger(clz)); - } -} diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java index 10b7b2425e..cf5f4ffccd 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java @@ -1,48 +1,353 @@ package com.google.api.gax.logging; -import java.util.Map; +import com.google.gson.JsonObject; +import java.util.logging.Level; +import org.slf4j.ILoggerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.slf4j.MDC; -import org.slf4j.event.Level; +import org.slf4j.Marker; +import org.slf4j.helpers.FormattingTuple; +import org.slf4j.helpers.MessageFormatter; public class LoggingUtils { + private static final java.util.logging.Logger LOGGER = + java.util.logging.Logger.getLogger(LoggingUtils.class.getName()); + + private LoggingUtils() {} + public static Logger getLogger(Class clazz) { - return LoggerFactory.getLogger(clazz); + + Logger logger; + + if (isLoggingEnabled()) { + + ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory(); + if (loggerFactory != null && !(loggerFactory instanceof org.slf4j.helpers.NOPLoggerFactory)) { + // An SLF4j binding is present + // You can get the logger and use it: + logger = LoggerFactory.getLogger(clazz); + logger.debug("SLF4J BINDING FOUND!!!!!"); + // ... + } else { + // No SLF4j binding found + // Implement your fallback logic here + logger = new JulWrapperLogger(clazz.getName()); + logger.info("No SLF4J providers were found, fall back to JUL."); + } + } else { + // use SLF4j's NOP logger regardless of bindings + logger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); + } + return logger; } public static boolean isLoggingEnabled() { String enableLogging = System.getenv("GOOGLE_SDK_JAVA_LOGGING"); + LOGGER.info("GOOGLE_SDK_JAVA_LOGGING=" + enableLogging); return "true".equalsIgnoreCase(enableLogging); } - public static void log(Logger logger, Level level, String msg, Map contextMap) { - - if (LoggingUtils.isLoggingEnabled()) { - MDC.setContextMap(contextMap); - switch (level) { - case TRACE: - logger.trace(msg); - break; - case DEBUG: - logger.debug(msg); - break; - case INFO: - logger.info(msg); - break; - case WARN: - logger.warn(msg); - break; - case ERROR: - logger.error(msg); - break; - default: - logger.info(msg); - // Default to INFO level if level is invalid - break; + public static JsonObject mergeJsonObject(JsonObject jsonObject1, JsonObject jsonObject2) { + JsonObject mergedObject = jsonObject1.deepCopy(); + jsonObject2.entrySet().forEach(entry -> mergedObject.add(entry.getKey(), entry.getValue())); + return mergedObject; + } + // JulWrapperLogger implementation + static class JulWrapperLogger implements Logger { + + private final java.util.logging.Logger julLogger; + + public JulWrapperLogger(String name) { + this.julLogger = java.util.logging.Logger.getLogger(name); + } + + @Override + public String getName() { + return julLogger.getName(); + } + + @Override + public boolean isTraceEnabled() { + return julLogger.isLoggable(java.util.logging.Level.FINEST); + } + + @Override + public void trace(String msg) { + julLogger.log(java.util.logging.Level.FINEST, msg); + } + + @Override + public void trace(String s, Object o) {} + + @Override + public void trace(String s, Object o, Object o1) {} + + @Override + public void trace(String s, Object... objects) {} + + @Override + public void trace(String s, Throwable throwable) {} + + @Override + public boolean isTraceEnabled(Marker marker) { + return false; + } + + @Override + public void trace(Marker marker, String s) {} + + @Override + public void trace(Marker marker, String s, Object o) {} + + @Override + public void trace(Marker marker, String s, Object o, Object o1) {} + + @Override + public void trace(Marker marker, String s, Object... objects) {} + + @Override + public void trace(Marker marker, String s, Throwable throwable) {} + + @Override + public boolean isDebugEnabled() { + return julLogger.isLoggable(Level.FINE); + } + + @Override + public void debug(String msg) { + + if (isDebugEnabled()) { + julLogger.log(java.util.logging.Level.FINE, msg); + } + } + + @Override + public void debug(String format, Object arg) { + if (isDebugEnabled()) { + FormattingTuple ft = MessageFormatter.format(format, arg); + julLogger.log(Level.FINE, ft.getMessage()); + } + } + + @Override + public void debug(String s, Object o, Object o1) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void debug(String s, Object... objects) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void debug(String s, Throwable throwable) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public boolean isDebugEnabled(Marker marker) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void debug(Marker marker, String s) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void debug(Marker marker, String s, Object o) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void debug(Marker marker, String s, Object o, Object o1) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void debug(Marker marker, String s, Object... objects) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void debug(Marker marker, String s, Throwable throwable) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public boolean isInfoEnabled() { + return julLogger.isLoggable(Level.INFO); + } + + @Override + public void info(String msg) { + if (isInfoEnabled()) { + julLogger.log(java.util.logging.Level.INFO, msg); + } + } + + @Override + public void info(String format, Object arg) { + if (isInfoEnabled()) { + FormattingTuple ft = MessageFormatter.format(format, arg); + julLogger.log(java.util.logging.Level.INFO, ft.getMessage()); } - MDC.clear(); } + + @Override + public void info(String s, Object o, Object o1) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void info(String s, Object... objects) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void info(String s, Throwable throwable) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public boolean isInfoEnabled(Marker marker) { + return true; + } + + @Override + public void info(Marker marker, String s) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void info(Marker marker, String s, Object o) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void info(Marker marker, String s, Object o, Object o1) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void info(Marker marker, String s, Object... objects) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void info(Marker marker, String s, Throwable throwable) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public boolean isWarnEnabled() { + return true; + } + + @Override + public void warn(String msg) { + julLogger.log(Level.WARNING, msg); + } + + @Override + public void warn(String s, Object o) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void warn(String s, Object... objects) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void warn(String s, Object o, Object o1) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void warn(String s, Throwable throwable) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public boolean isWarnEnabled(Marker marker) { + return false; + } + + @Override + public void warn(Marker marker, String s) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void warn(Marker marker, String s, Object o) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void warn(Marker marker, String s, Object o, Object o1) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void warn(Marker marker, String s, Object... objects) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void warn(Marker marker, String s, Throwable throwable) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public boolean isErrorEnabled() { + return false; + } + + @Override + public void error(String s) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void error(String s, Object o) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void error(String s, Object o, Object o1) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void error(String s, Object... objects) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void error(String s, Throwable throwable) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public boolean isErrorEnabled(Marker marker) { + return false; + } + + @Override + public void error(Marker marker, String s) {} + + @Override + public void error(Marker marker, String s, Object o) {} + + @Override + public void error(Marker marker, String s, Object o, Object o1) {} + + @Override + public void error(Marker marker, String s, Object... objects) {} + + @Override + public void error(Marker marker, String s, Throwable throwable) {} } } diff --git a/showcase/gapic-showcase/src/test/resources/logging.properties b/showcase/gapic-showcase/src/test/resources/logging.properties new file mode 100644 index 0000000000..ea11473c27 --- /dev/null +++ b/showcase/gapic-showcase/src/test/resources/logging.properties @@ -0,0 +1,4 @@ +handlers=java.util.logging.ConsoleHandler + +java.util.logging.ConsoleHandler.level=ALL +java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter From 0c169ad9a3fdac797fb74576d650992445aeeb62 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Mon, 25 Nov 2024 16:45:32 -0500 Subject: [PATCH 04/92] temp manual testing with showcase. --- showcase/gapic-showcase/pom.xml | 47 +++++++++-------- .../showcase/v1beta1/it/ITUnaryCallable.java | 52 +++++++++++++++++++ .../src/test/resources/logback-test.xml | 5 +- 3 files changed, 79 insertions(+), 25 deletions(-) diff --git a/showcase/gapic-showcase/pom.xml b/showcase/gapic-showcase/pom.xml index 0cea523ccd..8968145955 100644 --- a/showcase/gapic-showcase/pom.xml +++ b/showcase/gapic-showcase/pom.xml @@ -216,28 +216,29 @@ test - - org.slf4j - slf4j-api - 2.0.16 - - - ch.qos.logback - logback-classic - 1.5.11 - test - - - net.logstash.logback - logstash-logback-encoder - 7.2 - test - - - org.codehaus.janino - janino - 3.1.9 - test - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITUnaryCallable.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITUnaryCallable.java index 4d6018f6fc..c9ec84b798 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITUnaryCallable.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITUnaryCallable.java @@ -22,12 +22,22 @@ import com.google.api.gax.grpc.GrpcStatusCode; import com.google.api.gax.rpc.CancelledException; import com.google.api.gax.rpc.StatusCode; +import com.google.gson.Gson; +import com.google.gson.JsonObject; import com.google.rpc.Status; import com.google.showcase.v1beta1.EchoClient; import com.google.showcase.v1beta1.EchoRequest; import com.google.showcase.v1beta1.EchoResponse; import com.google.showcase.v1beta1.it.util.TestClientInitializer; import java.util.concurrent.TimeUnit; +import java.util.logging.ConsoleHandler; +import java.util.logging.Filter; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import java.util.logging.SimpleFormatter; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -37,6 +47,7 @@ class ITUnaryCallable { private static EchoClient grpcClient; private static EchoClient httpjsonClient; + static final Logger LOGGER = Logger.getLogger(ITUnaryCallable.class.getName()); @BeforeAll static void createClients() throws Exception { @@ -44,6 +55,33 @@ static void createClients() throws Exception { grpcClient = TestClientInitializer.createGrpcEchoClient(); // Create Http JSON Echo Client httpjsonClient = TestClientInitializer.createHttpJsonEchoClient(); + + // Get the root logger + Logger rootLogger = + LogManager.getLogManager().getLogger(""); + // Set the root logger's level to ALL or FINEST to see DEBUG messages + // rootLogger.setLevel(Level.ALL); // or rootLogger.setLevel(Level.FINEST); + // Remove any existing handlers (if needed) + for (Handler handler : rootLogger.getHandlers()) { + rootLogger.removeHandler(handler); + } + + // Create a ConsoleHandler + ConsoleHandler consoleHandler = new ConsoleHandler(); + consoleHandler.setLevel(Level.ALL); + consoleHandler.setFormatter(new SimpleFormatter()); + // String targetClassName = "com.google.api.gax.logging.LoggingUtils$JulWrapperLogger"; + // Filter filter = new Filter() { + // @Override + // public boolean isLoggable(LogRecord record) { + // return record.getLoggerName().equals(targetClassName); + // } + // }; + // consoleHandler.setFilter(filter); + + // Add the ConsoleHandler to the root logger + rootLogger.addHandler(consoleHandler); + LOGGER.log(Level.INFO, "This is log message directly from JUL. Clients created."); } @AfterAll @@ -55,9 +93,23 @@ static void destroyClients() throws InterruptedException { httpjsonClient.awaitTermination( TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS); } + @Test + void test() { + Gson gson = new Gson(); + JsonObject jsonObject1 = gson.fromJson("{\"name\":\"John\", \"age\":30}", JsonObject.class); + JsonObject jsonObject2 = gson.fromJson("{\"city\":\"New York\", \"country\":\"USA\"}", JsonObject.class); + + JsonObject mergedObject = jsonObject1.deepCopy(); + mergedObject.entrySet().forEach(entry -> jsonObject2.add(entry.getKey(), entry.getValue())); + + System.out.println(jsonObject2); // Output: {"city":"New York", "country":"USA", "name":"John", "age":30} + } @Test void testGrpc_receiveContent() { + LOGGER.log( + Level.INFO, + "This is log message directly from JUL. Starting test: testGrpc_receiveContent."); assertThat(echoGrpc("grpc-echo?")).isEqualTo("grpc-echo?"); assertThat(echoGrpc("grpc-echo!")).isEqualTo("grpc-echo!"); } diff --git a/showcase/gapic-showcase/src/test/resources/logback-test.xml b/showcase/gapic-showcase/src/test/resources/logback-test.xml index 02b2596516..df5a14382a 100644 --- a/showcase/gapic-showcase/src/test/resources/logback-test.xml +++ b/showcase/gapic-showcase/src/test/resources/logback-test.xml @@ -5,8 +5,9 @@ - logger.equals("com.google.api.gax.grpc.GrpcLoggingInterceptor") - logger.equals("com.google.api.gax.httpjson.HttpJsonLoggingInterceptor") + logger.equals("com.google.api.gax.grpc.GrpcLoggingInterceptor") || + logger.equals("com.google.api.gax.httpjson.HttpJsonLoggingInterceptor") || + logger.equals("com.google.api.gax.tracing.MetricsTracer") ACCEPT From 9ced17581257fc17aadcd5c41c22334df446da1b Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Mon, 25 Nov 2024 17:19:19 -0500 Subject: [PATCH 05/92] simplify logic for conditional logger. --- .../google/api/gax/logging/LoggingUtils.java | 66 +++++++++++++------ 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java index cf5f4ffccd..8aa9f5e60d 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java @@ -1,5 +1,36 @@ +/* + * Copyright 2024 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.logging; +import com.google.api.core.InternalApi; import com.google.gson.JsonObject; import java.util.logging.Level; import org.slf4j.ILoggerFactory; @@ -9,6 +40,7 @@ import org.slf4j.helpers.FormattingTuple; import org.slf4j.helpers.MessageFormatter; +@InternalApi public class LoggingUtils { private static final java.util.logging.Logger LOGGER = @@ -17,34 +49,25 @@ public class LoggingUtils { private LoggingUtils() {} public static Logger getLogger(Class clazz) { - - Logger logger; - - if (isLoggingEnabled()) { - - ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory(); - if (loggerFactory != null && !(loggerFactory instanceof org.slf4j.helpers.NOPLoggerFactory)) { - // An SLF4j binding is present - // You can get the logger and use it: - logger = LoggerFactory.getLogger(clazz); - logger.debug("SLF4J BINDING FOUND!!!!!"); - // ... - } else { - // No SLF4j binding found - // Implement your fallback logic here - logger = new JulWrapperLogger(clazz.getName()); - logger.info("No SLF4J providers were found, fall back to JUL."); - } - } else { + if (!isLoggingEnabled()) { // use SLF4j's NOP logger regardless of bindings - logger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); + return LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); } + + ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory(); + if (loggerFactory != null && !(loggerFactory instanceof org.slf4j.helpers.NOPLoggerFactory)) { + // Use SLF4j binding when present + return LoggerFactory.getLogger(clazz); + } + // No SLF4j binding found, use JUL as fallback + Logger logger = new JulWrapperLogger(clazz.getName()); + logger.info("No SLF4J providers were found, fall back to JUL."); return logger; } public static boolean isLoggingEnabled() { String enableLogging = System.getenv("GOOGLE_SDK_JAVA_LOGGING"); - LOGGER.info("GOOGLE_SDK_JAVA_LOGGING=" + enableLogging); + LOGGER.info("GOOGLE_SDK_JAVA_LOGGING=" + enableLogging); // log for debug now, remove it. return "true".equalsIgnoreCase(enableLogging); } @@ -53,6 +76,7 @@ public static JsonObject mergeJsonObject(JsonObject jsonObject1, JsonObject json jsonObject2.entrySet().forEach(entry -> mergedObject.add(entry.getKey(), entry.getValue())); return mergedObject; } + // JulWrapperLogger implementation static class JulWrapperLogger implements Logger { From 681f7d55b8238e9e4a7ac3f9f01ff01d152a6349 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 26 Nov 2024 17:35:43 -0500 Subject: [PATCH 06/92] implement logging for basic case. --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 109 +++++++--------- .../httpjson/HttpJsonLoggingInterceptor.java | 71 ++++++++--- .../gax/httpjson/ManagedHttpJsonChannel.java | 4 + .../gax/logging/JsonContextMapHandler.java | 45 +++++++ .../google/api/gax/logging/LoggingUtils.java | 119 +++++++++++++++--- 5 files changed, 257 insertions(+), 91 deletions(-) create mode 100644 gax-java/gax/src/main/java/com/google/api/gax/logging/JsonContextMapHandler.java diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index 9a92921987..9fea7f1200 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -6,14 +6,16 @@ import io.grpc.*; import java.util.HashMap; import java.util.Map; +import java.util.UUID; import org.slf4j.Logger; +import org.slf4j.event.Level; public class GrpcLoggingInterceptor implements ClientInterceptor { private static final Logger logger = LoggingUtils.getLogger(GrpcLoggingInterceptor.class); private static final Gson gson = new Gson(); - private JsonObject serviceAndRpc = new JsonObject(); - private JsonObject requestLogData = new JsonObject(); + private Map serviceAndRpc = new HashMap<>(); + private Map requestLogData = new HashMap<>(); @Override public ClientCall interceptCall( @@ -24,33 +26,21 @@ public ClientCall interceptCall( @Override public void start(Listener responseListener, Metadata headers) { - // if (LoggingUtils.isLoggingEnabled()) { - // Capture request details + String requestId = UUID.randomUUID().toString(); String serviceName = method.getServiceName(); String methodName = method.getFullMethodName(); - serviceAndRpc.addProperty("serviceName", serviceName); - serviceAndRpc.addProperty("rpcName", methodName); - - JsonObject responseLogData = new JsonObject(); - // Add request details to MDC// Example system - // MDC.put("serviceName", serviceName); - // MDC.put("rpcName", methodName); - // Capture and log headers - headers - .keys() - .forEach( - key -> { - Metadata.Key metadataKey = - Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER); - String headerValue = headers.get(metadataKey); - // MDC.put("request.headers:" + key, headerValue); - requestLogData.addProperty("request.headers:" + key, headerValue); - }); - - requestLogData.addProperty("message", "Sending gRPC request"); - // logger.debug("Sending gRPC request"); - // } + serviceAndRpc.put("serviceName", serviceName); + serviceAndRpc.put("rpcName", methodName); + serviceAndRpc.put("requestId", requestId); + + requestLogData.putAll(serviceAndRpc); + + LoggingUtils.logWithMDC(logger, Level.INFO, serviceAndRpc, "Sending gRPC request"); + + Map responseLogData = new HashMap<>(); + JsonObject requestHeaders = mapHeadersToJsonObject(headers); + requestLogData.put("request.headers", gson.toJson(requestHeaders)); super.start( new ForwardingClientCallListener.SimpleForwardingClientCallListener( @@ -58,35 +48,24 @@ public void start(Listener responseListener, Metadata headers) { @Override public void onMessage(RespT message) { - responseLogData.addProperty("response.payload", gson.toJson(message)); + responseLogData.put("response.payload", gson.toJson(message)); super.onMessage(message); } @Override public void onClose(Status status, Metadata trailers) { - responseLogData.addProperty("response.status", status.getCode().name()); - responseLogData.addProperty( - "message", String.format("gRPC request finished with status: %s", status)); - - // Create a JsonObject for response headers - JsonObject responseHeaders = new JsonObject(); - - // Access and add response headers to the JsonObject - trailers - .keys() - .forEach( - key -> { - Metadata.Key metadataKey = - Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER); - String headerValue = trailers.get(metadataKey); - responseHeaders.addProperty(key, headerValue); - }); - responseLogData.add("response.headers", responseHeaders); - - logger.debug( - gson.toJson(LoggingUtils.mergeJsonObject(serviceAndRpc, responseLogData))); - // logger.info("gRPC request finished with status: {}", status); - // MDC.clear(); // Clear MDC after the request + serviceAndRpc.put("response.status", status.getCode().name()); + responseLogData.putAll(serviceAndRpc); + + LoggingUtils.logWithMDC(logger, Level.INFO, serviceAndRpc, "Received response."); + + // Access and add response headers + JsonObject responseHeaders = mapHeadersToJsonObject(trailers); + responseLogData.put("response.headers", gson.toJson(responseHeaders)); + + LoggingUtils.logWithMDC( + logger, Level.DEBUG, responseLogData, "Received response header and payload."); + super.onClose(status, trailers); } }, @@ -95,19 +74,27 @@ public void onClose(Status status, Metadata trailers) { @Override public void sendMessage(ReqT message) { - // MDC.put("request.payload", gson.toJson(message)); - requestLogData.addProperty("request.payload", gson.toJson(message)); - - Map map = new HashMap<>(); - serviceAndRpc - .entrySet() - .forEach(entry -> map.put(entry.getKey(), entry.getValue().getAsString())); - // MDC.setContextMap(map); - // logger.info(new MapMessage(map)); - logger.info(gson.toJson(LoggingUtils.mergeJsonObject(serviceAndRpc, requestLogData))); - // MDC.clear(); + + requestLogData.put("request.payload", gson.toJson(message)); + + LoggingUtils.logWithMDC( + logger, Level.DEBUG, requestLogData, "grpc request header and payload."); super.sendMessage(message); } }; } + + private static JsonObject mapHeadersToJsonObject(Metadata headers) { + JsonObject jsonHeaders = new JsonObject(); + headers + .keys() + .forEach( + key -> { + Metadata.Key metadataKey = + Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER); + String headerValue = headers.get(metadataKey); + jsonHeaders.addProperty(key, headerValue); + }); + return jsonHeaders; + } } diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index 4195d9c386..05df206970 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -1,16 +1,21 @@ package com.google.api.gax.httpjson; import com.google.api.gax.httpjson.ForwardingHttpJsonClientCall.SimpleForwardingHttpJsonClientCall; +import com.google.api.gax.logging.LoggingUtils; import com.google.gson.Gson; +import com.google.gson.JsonObject; +import java.util.HashMap; import java.util.Map; +import java.util.UUID; import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.MDC; +import org.slf4j.event.Level; public class HttpJsonLoggingInterceptor implements HttpJsonClientInterceptor { - private static final Logger logger = LoggerFactory.getLogger(HttpJsonLoggingInterceptor.class); + private static final Logger logger = LoggingUtils.getLogger(HttpJsonLoggingInterceptor.class); private static final Gson gson = new Gson(); + private Map serviceAndRpc = new HashMap<>(); + private Map requestLogData = new HashMap<>(); @Override public HttpJsonClientCall interceptCall( @@ -18,43 +23,79 @@ public HttpJsonClientCall interceptCall( HttpJsonCallOptions callOptions, HttpJsonChannel next) { + String endpoint = ((ManagedHttpJsonChannel) next).getEndpoint(); return new SimpleForwardingHttpJsonClientCall(next.newCall(method, callOptions)) { @Override public void start( HttpJsonClientCall.Listener responseListener, HttpJsonMetadata headers) { + String requestId = UUID.randomUUID().toString(); // Capture request details String methodName = method.getFullMethodName(); + String httpMethod = method.getHttpMethod(); - // Add request details to MDC - MDC.put("method", methodName); + serviceAndRpc.put("rpcName", methodName); + serviceAndRpc.put("requestId", requestId); + requestLogData.putAll(serviceAndRpc); + requestLogData.put("request.url", endpoint); + requestLogData.put("request.method", httpMethod); // Capture and log headers - for (Map.Entry header : headers.getHeaders().entrySet()) { - MDC.put("header." + header.getKey(), header.getValue().toString()); - } - - logger.info("Sending HTTP request"); + JsonObject jsonHeaders = new JsonObject(); + headers + .getHeaders() + .entrySet() + .forEach( + entry -> { + jsonHeaders.addProperty(entry.getKey(), entry.getValue().toString()); + }); + requestLogData.put("request.headers", gson.toJson(jsonHeaders)); + LoggingUtils.logWithMDC(logger, Level.INFO, serviceAndRpc, "Sending HTTP request"); + Map responseLogData = new HashMap<>(); super.start( new HttpJsonClientCall.Listener() { @Override public void onMessage(RespT message) { - MDC.put("response.payload", gson.toJson(message)); - logger.debug("Received HTTP response."); + responseLogData.put("response.payload", gson.toJson(message)); responseListener.onMessage(message); } @Override public void onClose(int status, HttpJsonMetadata trailers) { - MDC.put("response.status", String.valueOf(status)); - logger.info("HTTP request finished with status: {}", status); - MDC.clear(); // Clear MDC after the request + serviceAndRpc.put("response.status", String.valueOf(status)); + responseLogData.putAll(serviceAndRpc); + LoggingUtils.logWithMDC( + logger, Level.INFO, serviceAndRpc, "HTTP request finished."); + + JsonObject jsonHeaders = new JsonObject(); + headers + .getHeaders() + .entrySet() + .forEach( + entry -> { + jsonHeaders.addProperty(entry.getKey(), entry.getValue().toString()); + }); + responseLogData.put("request.headers", gson.toJson(jsonHeaders)); + + LoggingUtils.logWithMDC( + logger, Level.DEBUG, responseLogData, "Received response header and payload."); + responseListener.onClose(status, trailers); } }, headers); } + + @Override + public void sendMessage(ReqT message) { + requestLogData.put("request.payload", gson.toJson(message)); + + LoggingUtils.logWithMDC( + logger, Level.DEBUG, requestLogData, "HTTP request header and payload."); + + super.sendMessage(message); + } }; } } diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ManagedHttpJsonChannel.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ManagedHttpJsonChannel.java index 7a2e7a2f26..bd3bed8556 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ManagedHttpJsonChannel.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ManagedHttpJsonChannel.java @@ -57,6 +57,10 @@ protected ManagedHttpJsonChannel() { this(null, true, null, null); } + String getEndpoint() { + return endpoint; + } + private ManagedHttpJsonChannel( Executor executor, boolean usingDefaultExecutor, diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/JsonContextMapHandler.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/JsonContextMapHandler.java new file mode 100644 index 0000000000..74c7d30e13 --- /dev/null +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/JsonContextMapHandler.java @@ -0,0 +1,45 @@ +package com.google.api.gax.logging; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Handler; +import java.util.logging.LogRecord; + +public class JsonContextMapHandler extends Handler { + + private final Gson gson = new GsonBuilder().setPrettyPrinting().create(); + + @Override + public void publish(LogRecord record) { + Object[] params = record.getParameters(); + if (params != null && params.length > 0 && params[0] instanceof Map) { + @SuppressWarnings("unchecked") + Map contextMap = (Map) params[0]; + + // Create a map to hold all log data + Map logData = new HashMap<>(); + logData.put("message", record.getMessage()); + logData.put("severity", record.getLevel().getName()); + logData.put("timestamp", record.getMillis()); + logData.put("logger", record.getLoggerName()); + + // Add all context data to the top level + logData.putAll(contextMap); + + // Convert to JSON + String jsonLog = gson.toJson(logData); + System.out.println(jsonLog); + } else { + // Handle cases where the context map is not present + System.out.println("Log message without context: " + record.getMessage()); + } + } + + @Override + public void flush() {} + + @Override + public void close() throws SecurityException {} +} diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java index 8aa9f5e60d..034358bdb5 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java @@ -32,10 +32,13 @@ import com.google.api.core.InternalApi; import com.google.gson.JsonObject; +import java.util.Map; import java.util.logging.Level; +import java.util.logging.LogRecord; import org.slf4j.ILoggerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.slf4j.MDC; import org.slf4j.Marker; import org.slf4j.helpers.FormattingTuple; import org.slf4j.helpers.MessageFormatter; @@ -77,6 +80,60 @@ public static JsonObject mergeJsonObject(JsonObject jsonObject1, JsonObject json return mergedObject; } + public static Level mapToJulLevel(org.slf4j.event.Level slf4jLevel) { + switch (slf4jLevel) { + case ERROR: + return Level.SEVERE; + case WARN: + return Level.WARNING; + case INFO: + return Level.INFO; + case DEBUG: + return Level.FINE; + case TRACE: + return Level.FINEST; + default: + return Level.INFO; + } + } + + public static void logWithMDC( + Logger logger, org.slf4j.event.Level level, Map contextMap, String message) { + + if (logger instanceof JulWrapperLogger) { + // Simulate MDC behavior for JUL + LogRecord record = new LogRecord(mapToJulLevel(level), message); + // Add context map to the LogRecord + record.setParameters(new Object[] {contextMap}); + ((JulWrapperLogger) logger).getJulLogger().log(record); + return; + } + contextMap.forEach(MDC::put); + + switch (level) { + case TRACE: + logger.trace(message); + break; + case DEBUG: + logger.debug(message); + break; + case INFO: + logger.info(message); + break; + case WARN: + logger.warn(message); + break; + case ERROR: + logger.error(message); + break; + default: + logger.info(message); + // Default to INFO level + } + + MDC.clear(); + } + // JulWrapperLogger implementation static class JulWrapperLogger implements Logger { @@ -86,6 +143,10 @@ public JulWrapperLogger(String name) { this.julLogger = java.util.logging.Logger.getLogger(name); } + public java.util.logging.Logger getJulLogger() { + return julLogger; + } + @Override public String getName() { return julLogger.getName(); @@ -102,16 +163,24 @@ public void trace(String msg) { } @Override - public void trace(String s, Object o) {} + public void trace(String s, Object o) { + throw new UnsupportedOperationException("This method is not supported."); + } @Override - public void trace(String s, Object o, Object o1) {} + public void trace(String s, Object o, Object o1) { + throw new UnsupportedOperationException("This method is not supported."); + } @Override - public void trace(String s, Object... objects) {} + public void trace(String s, Object... objects) { + throw new UnsupportedOperationException("This method is not supported."); + } @Override - public void trace(String s, Throwable throwable) {} + public void trace(String s, Throwable throwable) { + throw new UnsupportedOperationException("This method is not supported."); + } @Override public boolean isTraceEnabled(Marker marker) { @@ -119,19 +188,29 @@ public boolean isTraceEnabled(Marker marker) { } @Override - public void trace(Marker marker, String s) {} + public void trace(Marker marker, String s) { + throw new UnsupportedOperationException("This method is not supported."); + } @Override - public void trace(Marker marker, String s, Object o) {} + public void trace(Marker marker, String s, Object o) { + throw new UnsupportedOperationException("This method is not supported."); + } @Override - public void trace(Marker marker, String s, Object o, Object o1) {} + public void trace(Marker marker, String s, Object o, Object o1) { + throw new UnsupportedOperationException("This method is not supported."); + } @Override - public void trace(Marker marker, String s, Object... objects) {} + public void trace(Marker marker, String s, Object... objects) { + throw new UnsupportedOperationException("This method is not supported."); + } @Override - public void trace(Marker marker, String s, Throwable throwable) {} + public void trace(Marker marker, String s, Throwable throwable) { + throw new UnsupportedOperationException("This method is not supported."); + } @Override public boolean isDebugEnabled() { @@ -356,22 +435,32 @@ public void error(String s, Throwable throwable) { @Override public boolean isErrorEnabled(Marker marker) { - return false; + throw new UnsupportedOperationException("This method is not supported."); } @Override - public void error(Marker marker, String s) {} + public void error(Marker marker, String s) { + throw new UnsupportedOperationException("This method is not supported."); + } @Override - public void error(Marker marker, String s, Object o) {} + public void error(Marker marker, String s, Object o) { + throw new UnsupportedOperationException("This method is not supported."); + } @Override - public void error(Marker marker, String s, Object o, Object o1) {} + public void error(Marker marker, String s, Object o, Object o1) { + throw new UnsupportedOperationException("This method is not supported."); + } @Override - public void error(Marker marker, String s, Object... objects) {} + public void error(Marker marker, String s, Object... objects) { + throw new UnsupportedOperationException("This method is not supported."); + } @Override - public void error(Marker marker, String s, Throwable throwable) {} + public void error(Marker marker, String s, Throwable throwable) { + throw new UnsupportedOperationException("This method is not supported."); + } } } From 5c01ef24cd922bfca0a8975613b09777a3cd28de Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 26 Nov 2024 20:53:46 -0500 Subject: [PATCH 07/92] add a JsonContextMapHandler to format in JUL case. --- .../gax/logging/JsonContextMapHandler.java | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/JsonContextMapHandler.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/JsonContextMapHandler.java index 74c7d30e13..870cb62ae5 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/JsonContextMapHandler.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/JsonContextMapHandler.java @@ -1,9 +1,40 @@ +/* + * Copyright 2024 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.logging; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.util.HashMap; import java.util.Map; +import java.util.logging.ConsoleHandler; import java.util.logging.Handler; import java.util.logging.LogRecord; @@ -14,6 +45,8 @@ public class JsonContextMapHandler extends Handler { @Override public void publish(LogRecord record) { Object[] params = record.getParameters(); + + ConsoleHandler consoleHandler = new ConsoleHandler(); if (params != null && params.length > 0 && params[0] instanceof Map) { @SuppressWarnings("unchecked") Map contextMap = (Map) params[0]; @@ -30,10 +63,14 @@ public void publish(LogRecord record) { // Convert to JSON String jsonLog = gson.toJson(logData); - System.out.println(jsonLog); + + LogRecord jsonRecord = new LogRecord(record.getLevel(), jsonLog); + consoleHandler.publish(jsonRecord); } else { // Handle cases where the context map is not present - System.out.println("Log message without context: " + record.getMessage()); + LogRecord messageRecord = + new LogRecord(record.getLevel(), "Log message without context: " + record.getMessage()); + consoleHandler.publish(messageRecord); } } From 6605ef3878164544bfdf14ac0699e4824fb76877 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 27 Nov 2024 11:12:04 -0500 Subject: [PATCH 08/92] fix response payload for streaming. Add if guards to bypass log when not enabled. --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 84 ++++++++------ .../httpjson/HttpJsonLoggingInterceptor.java | 109 ++++++++++-------- 2 files changed, 113 insertions(+), 80 deletions(-) diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index 9fea7f1200..e599c9eb68 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -2,6 +2,7 @@ import com.google.api.gax.logging.LoggingUtils; import com.google.gson.Gson; +import com.google.gson.JsonArray; import com.google.gson.JsonObject; import io.grpc.*; import java.util.HashMap; @@ -14,57 +15,73 @@ public class GrpcLoggingInterceptor implements ClientInterceptor { private static final Logger logger = LoggingUtils.getLogger(GrpcLoggingInterceptor.class); private static final Gson gson = new Gson(); - private Map serviceAndRpc = new HashMap<>(); - private Map requestLogData = new HashMap<>(); @Override public ClientCall interceptCall( MethodDescriptor method, CallOptions callOptions, Channel next) { + Map serviceAndRpc = new HashMap<>(); + Map requestLogData = new HashMap<>(); + Map responseLogData = new HashMap<>(); + + // Initialize a JsonArray to hold all responses + JsonArray responsePayloads = new JsonArray(); return new ForwardingClientCall.SimpleForwardingClientCall( next.newCall(method, callOptions)) { @Override public void start(Listener responseListener, Metadata headers) { - String requestId = UUID.randomUUID().toString(); - String serviceName = method.getServiceName(); - String methodName = method.getFullMethodName(); - - serviceAndRpc.put("serviceName", serviceName); - serviceAndRpc.put("rpcName", methodName); - serviceAndRpc.put("requestId", requestId); - - requestLogData.putAll(serviceAndRpc); - - LoggingUtils.logWithMDC(logger, Level.INFO, serviceAndRpc, "Sending gRPC request"); - - Map responseLogData = new HashMap<>(); - JsonObject requestHeaders = mapHeadersToJsonObject(headers); - requestLogData.put("request.headers", gson.toJson(requestHeaders)); + if (logger.isInfoEnabled()) { + String requestId = UUID.randomUUID().toString(); + String serviceName = method.getServiceName(); + String methodName = method.getFullMethodName(); + + serviceAndRpc.put("serviceName", serviceName); + serviceAndRpc.put("rpcName", methodName); + serviceAndRpc.put("requestId", requestId); + } + if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { + LoggingUtils.logWithMDC(logger, Level.INFO, serviceAndRpc, "Sending gRPC request"); + } + + if (logger.isDebugEnabled()) { + requestLogData.putAll(serviceAndRpc); + + JsonObject requestHeaders = mapHeadersToJsonObject(headers); + requestLogData.put("request.headers", gson.toJson(requestHeaders)); + } super.start( new ForwardingClientCallListener.SimpleForwardingClientCallListener( responseListener) { @Override public void onMessage(RespT message) { - - responseLogData.put("response.payload", gson.toJson(message)); + if (logger.isDebugEnabled()) { + // Add each message to the array + responsePayloads.add(gson.toJsonTree(message)); + } super.onMessage(message); } @Override public void onClose(Status status, Metadata trailers) { - serviceAndRpc.put("response.status", status.getCode().name()); - responseLogData.putAll(serviceAndRpc); - - LoggingUtils.logWithMDC(logger, Level.INFO, serviceAndRpc, "Received response."); - - // Access and add response headers - JsonObject responseHeaders = mapHeadersToJsonObject(trailers); - responseLogData.put("response.headers", gson.toJson(responseHeaders)); - - LoggingUtils.logWithMDC( - logger, Level.DEBUG, responseLogData, "Received response header and payload."); + if (logger.isInfoEnabled()) { + serviceAndRpc.put("response.status", status.getCode().name()); + responseLogData.putAll(serviceAndRpc); + } + if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { + LoggingUtils.logWithMDC(logger, Level.INFO, serviceAndRpc, "Received response."); + } + if (logger.isDebugEnabled()) { + // Access and add response headers + JsonObject responseHeaders = mapHeadersToJsonObject(trailers); + responseLogData.put("response.headers", gson.toJson(responseHeaders)); + // Add the array of payloads to the responseLogData + responseLogData.put("response.payload", gson.toJson(responsePayloads)); + + LoggingUtils.logWithMDC( + logger, Level.DEBUG, responseLogData, "Received response."); + } super.onClose(status, trailers); } @@ -75,10 +92,11 @@ public void onClose(Status status, Metadata trailers) { @Override public void sendMessage(ReqT message) { - requestLogData.put("request.payload", gson.toJson(message)); + if (logger.isDebugEnabled()) { + requestLogData.put("request.payload", gson.toJson(message)); + LoggingUtils.logWithMDC(logger, Level.DEBUG, requestLogData, "Sending gRPC request."); + } - LoggingUtils.logWithMDC( - logger, Level.DEBUG, requestLogData, "grpc request header and payload."); super.sendMessage(message); } }; diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index 05df206970..10d1c1021c 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -3,6 +3,7 @@ import com.google.api.gax.httpjson.ForwardingHttpJsonClientCall.SimpleForwardingHttpJsonClientCall; import com.google.api.gax.logging.LoggingUtils; import com.google.gson.Gson; +import com.google.gson.JsonArray; import com.google.gson.JsonObject; import java.util.HashMap; import java.util.Map; @@ -14,72 +15,85 @@ public class HttpJsonLoggingInterceptor implements HttpJsonClientInterceptor { private static final Logger logger = LoggingUtils.getLogger(HttpJsonLoggingInterceptor.class); private static final Gson gson = new Gson(); - private Map serviceAndRpc = new HashMap<>(); - private Map requestLogData = new HashMap<>(); @Override public HttpJsonClientCall interceptCall( ApiMethodDescriptor method, HttpJsonCallOptions callOptions, HttpJsonChannel next) { + Map requestLogData = new HashMap<>(); + // Initialize a JsonArray to hold all responses + JsonArray responsePayloads = new JsonArray(); String endpoint = ((ManagedHttpJsonChannel) next).getEndpoint(); return new SimpleForwardingHttpJsonClientCall(next.newCall(method, callOptions)) { @Override public void start( HttpJsonClientCall.Listener responseListener, HttpJsonMetadata headers) { - String requestId = UUID.randomUUID().toString(); - // Capture request details - String methodName = method.getFullMethodName(); - String httpMethod = method.getHttpMethod(); - serviceAndRpc.put("rpcName", methodName); - serviceAndRpc.put("requestId", requestId); - requestLogData.putAll(serviceAndRpc); - requestLogData.put("request.url", endpoint); - requestLogData.put("request.method", httpMethod); - - // Capture and log headers - JsonObject jsonHeaders = new JsonObject(); - headers - .getHeaders() - .entrySet() - .forEach( - entry -> { - jsonHeaders.addProperty(entry.getKey(), entry.getValue().toString()); - }); - requestLogData.put("request.headers", gson.toJson(jsonHeaders)); - - LoggingUtils.logWithMDC(logger, Level.INFO, serviceAndRpc, "Sending HTTP request"); + Map serviceAndRpc = new HashMap<>(); + if (logger.isInfoEnabled()) { + String requestId = UUID.randomUUID().toString(); + // Capture request details + String methodName = method.getFullMethodName(); + String httpMethod = method.getHttpMethod(); + serviceAndRpc.put("rpcName", methodName); + serviceAndRpc.put("requestId", requestId); + requestLogData.putAll(serviceAndRpc); + requestLogData.put("request.url", endpoint); + requestLogData.put("request.method", httpMethod); + } + if (logger.isDebugEnabled()) { + // Capture and log headers + JsonObject jsonHeaders = new JsonObject(); + headers + .getHeaders() + .forEach((key, value) -> jsonHeaders.addProperty(key, value.toString())); + requestLogData.put("request.headers", gson.toJson(jsonHeaders)); + } + + if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { + LoggingUtils.logWithMDC(logger, Level.INFO, serviceAndRpc, "Sending HTTP request"); + } Map responseLogData = new HashMap<>(); super.start( new HttpJsonClientCall.Listener() { @Override public void onMessage(RespT message) { - responseLogData.put("response.payload", gson.toJson(message)); - + if (logger.isDebugEnabled()) { + // Add each message to the array + responsePayloads.add(gson.toJsonTree(message)); + } responseListener.onMessage(message); } @Override public void onClose(int status, HttpJsonMetadata trailers) { - serviceAndRpc.put("response.status", String.valueOf(status)); - responseLogData.putAll(serviceAndRpc); - LoggingUtils.logWithMDC( - logger, Level.INFO, serviceAndRpc, "HTTP request finished."); - - JsonObject jsonHeaders = new JsonObject(); - headers - .getHeaders() - .entrySet() - .forEach( - entry -> { - jsonHeaders.addProperty(entry.getKey(), entry.getValue().toString()); - }); - responseLogData.put("request.headers", gson.toJson(jsonHeaders)); - - LoggingUtils.logWithMDC( - logger, Level.DEBUG, responseLogData, "Received response header and payload."); + if (logger.isInfoEnabled()) { + + serviceAndRpc.put("response.status", String.valueOf(status)); + responseLogData.putAll(serviceAndRpc); + } + if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { + LoggingUtils.logWithMDC( + logger, Level.INFO, serviceAndRpc, "HTTP request finished."); + } + if (logger.isDebugEnabled()) { + + JsonObject jsonHeaders = new JsonObject(); + headers + .getHeaders() + .forEach((key, value) -> jsonHeaders.addProperty(key, value.toString())); + responseLogData.put("response.headers", gson.toJson(jsonHeaders)); + + // Add the array of payloads to the responseLogData + responseLogData.put("response.payload", gson.toJson(responsePayloads)); + LoggingUtils.logWithMDC( + logger, + Level.DEBUG, + responseLogData, + "Received response header and payload."); + } responseListener.onClose(status, trailers); } @@ -89,10 +103,11 @@ public void onClose(int status, HttpJsonMetadata trailers) { @Override public void sendMessage(ReqT message) { - requestLogData.put("request.payload", gson.toJson(message)); - - LoggingUtils.logWithMDC( - logger, Level.DEBUG, requestLogData, "HTTP request header and payload."); + if (logger.isDebugEnabled()) { + requestLogData.put("request.payload", gson.toJson(message)); + LoggingUtils.logWithMDC( + logger, Level.DEBUG, requestLogData, "HTTP request header and payload."); + } super.sendMessage(message); } From ebbae154a94e7875f688a8963c5d6bd90f1e48d8 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 27 Nov 2024 16:52:20 -0500 Subject: [PATCH 09/92] when logging disabled, assign NOPLogger directly. --- .../src/main/java/com/google/api/gax/logging/LoggingUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java index 034358bdb5..436a9e35c8 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java @@ -42,6 +42,7 @@ import org.slf4j.Marker; import org.slf4j.helpers.FormattingTuple; import org.slf4j.helpers.MessageFormatter; +import org.slf4j.helpers.NOPLogger; @InternalApi public class LoggingUtils { @@ -54,7 +55,7 @@ private LoggingUtils() {} public static Logger getLogger(Class clazz) { if (!isLoggingEnabled()) { // use SLF4j's NOP logger regardless of bindings - return LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); + return LoggerFactory.getLogger(NOPLogger.class); } ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory(); From 543298d0c94eaf62b320fc1bf69dd0497a2dfcfc Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Mon, 2 Dec 2024 11:42:36 -0500 Subject: [PATCH 10/92] some temp settings for testing with showcase. --- .../com/google/api/gax/rpc/ClientContext.java | 5 -- showcase/gapic-showcase/pom.xml | 51 ++++++++++--------- .../showcase/v1beta1/it/ITUnaryCallable.java | 40 +++++---------- .../src/test/resources/logback-test.xml | 3 ++ .../src/test/resources/logging.properties | 3 +- 5 files changed, 45 insertions(+), 57 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientContext.java b/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientContext.java index aab38b8162..5bce1ac6bb 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientContext.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientContext.java @@ -72,8 +72,6 @@ */ @AutoValue public abstract class ClientContext { - - // private static final Logger LOGGER = LoggingUtils.getLogger(ClientContext.class); private static final String QUOTA_PROJECT_ID_HEADER_KEY = "x-goog-user-project"; /** @@ -172,9 +170,6 @@ public static ClientContext create(ClientSettings settings) throws IOException { * settings. */ public static ClientContext create(StubSettings settings) throws IOException { - // if (LoggingUtils.isLoggingEnabled()) { - // LoggingUtils.log(LOGGER, Level.INFO, "a dummy message", Collections.emptyMap()); - // } ApiClock clock = settings.getClock(); ExecutorProvider backgroundExecutorProvider = settings.getBackgroundExecutorProvider(); diff --git a/showcase/gapic-showcase/pom.xml b/showcase/gapic-showcase/pom.xml index 8968145955..48d8212834 100644 --- a/showcase/gapic-showcase/pom.xml +++ b/showcase/gapic-showcase/pom.xml @@ -217,28 +217,33 @@ - - - - - - - - - - - - - - - - - - - - - - - + + org.slf4j + slf4j-api + 2.0.16 + + + ch.qos.logback + logback-classic + 1.5.11 + test + + + ch.qos.logback + logback-core + 1.5.11 + + + net.logstash.logback + logstash-logback-encoder + 7.2 + test + + + org.codehaus.janino + janino + 3.1.9 + test + diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITUnaryCallable.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITUnaryCallable.java index c9ec84b798..edb98facdb 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITUnaryCallable.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITUnaryCallable.java @@ -20,10 +20,9 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import com.google.api.gax.grpc.GrpcStatusCode; +import com.google.api.gax.logging.JsonContextMapHandler; import com.google.api.gax.rpc.CancelledException; import com.google.api.gax.rpc.StatusCode; -import com.google.gson.Gson; -import com.google.gson.JsonObject; import com.google.rpc.Status; import com.google.showcase.v1beta1.EchoClient; import com.google.showcase.v1beta1.EchoRequest; @@ -31,11 +30,9 @@ import com.google.showcase.v1beta1.it.util.TestClientInitializer; import java.util.concurrent.TimeUnit; import java.util.logging.ConsoleHandler; -import java.util.logging.Filter; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogManager; -import java.util.logging.LogRecord; import java.util.logging.Logger; import java.util.logging.SimpleFormatter; import org.junit.jupiter.api.AfterAll; @@ -56,28 +53,26 @@ static void createClients() throws Exception { // Create Http JSON Echo Client httpjsonClient = TestClientInitializer.createHttpJsonEchoClient(); + // Settings for JUL as fallback // Get the root logger - Logger rootLogger = - LogManager.getLogManager().getLogger(""); - // Set the root logger's level to ALL or FINEST to see DEBUG messages - // rootLogger.setLevel(Level.ALL); // or rootLogger.setLevel(Level.FINEST); - // Remove any existing handlers (if needed) + Logger rootLogger = LogManager.getLogManager().getLogger(""); + // Set the root logger's level to ALL to see DEBUG messages + // rootLogger.setLevel(Level.ALL); + // Remove any existing handlers for (Handler handler : rootLogger.getHandlers()) { rootLogger.removeHandler(handler); } + // Create and add your ContextMapHandler + JsonContextMapHandler contextHandler = new JsonContextMapHandler(); + contextHandler.setLevel(Level.ALL); // Set the desired level + // Add a formatter if needed (optional) + // contextHandler.setFormatter(...); + rootLogger.addHandler(contextHandler); // Create a ConsoleHandler ConsoleHandler consoleHandler = new ConsoleHandler(); consoleHandler.setLevel(Level.ALL); consoleHandler.setFormatter(new SimpleFormatter()); - // String targetClassName = "com.google.api.gax.logging.LoggingUtils$JulWrapperLogger"; - // Filter filter = new Filter() { - // @Override - // public boolean isLoggable(LogRecord record) { - // return record.getLoggerName().equals(targetClassName); - // } - // }; - // consoleHandler.setFilter(filter); // Add the ConsoleHandler to the root logger rootLogger.addHandler(consoleHandler); @@ -93,17 +88,6 @@ static void destroyClients() throws InterruptedException { httpjsonClient.awaitTermination( TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS); } - @Test - void test() { - Gson gson = new Gson(); - JsonObject jsonObject1 = gson.fromJson("{\"name\":\"John\", \"age\":30}", JsonObject.class); - JsonObject jsonObject2 = gson.fromJson("{\"city\":\"New York\", \"country\":\"USA\"}", JsonObject.class); - - JsonObject mergedObject = jsonObject1.deepCopy(); - mergedObject.entrySet().forEach(entry -> jsonObject2.add(entry.getKey(), entry.getValue())); - - System.out.println(jsonObject2); // Output: {"city":"New York", "country":"USA", "name":"John", "age":30} - } @Test void testGrpc_receiveContent() { diff --git a/showcase/gapic-showcase/src/test/resources/logback-test.xml b/showcase/gapic-showcase/src/test/resources/logback-test.xml index df5a14382a..f216becaa9 100644 --- a/showcase/gapic-showcase/src/test/resources/logback-test.xml +++ b/showcase/gapic-showcase/src/test/resources/logback-test.xml @@ -2,6 +2,9 @@ + + severity + diff --git a/showcase/gapic-showcase/src/test/resources/logging.properties b/showcase/gapic-showcase/src/test/resources/logging.properties index ea11473c27..ce394f934c 100644 --- a/showcase/gapic-showcase/src/test/resources/logging.properties +++ b/showcase/gapic-showcase/src/test/resources/logging.properties @@ -1,4 +1,5 @@ -handlers=java.util.logging.ConsoleHandler +handlers=java.util.logging.ConsoleHandler, com.google.api.gax.logging.JsonContextMapHandler java.util.logging.ConsoleHandler.level=ALL java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter +com.google.api.gax.logging.JsonContextMapHandler.level=ALL From aac66f2acde15e78cf0678db2c68ad31e41102e4 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Thu, 5 Dec 2024 16:25:22 -0500 Subject: [PATCH 11/92] remove JUL wrapper for now. refactor getLogger(). Add tests for LoggingUtils. --- gax-java/gax/pom.xml | 14 + .../google/api/gax/logging/LoggingUtils.java | 414 ++---------------- .../internal/SystemEnvironmentProvider.java | 50 +++ .../api/gax/logging/LoggingUtilsTest.java | 130 ++++++ 4 files changed, 224 insertions(+), 384 deletions(-) create mode 100644 gax-java/gax/src/main/java/com/google/api/gax/rpc/internal/SystemEnvironmentProvider.java create mode 100644 gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java diff --git a/gax-java/gax/pom.xml b/gax-java/gax/pom.xml index 5ada305a25..2f86586d82 100644 --- a/gax-java/gax/pom.xml +++ b/gax-java/gax/pom.xml @@ -75,6 +75,20 @@ slf4j-api 2.0.16 + + + ch.qos.logback + logback-classic + + 1.3.14 + test + + + ch.qos.logback + logback-core + 1.3.14 + test + diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java index 436a9e35c8..6eba4a3430 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java @@ -31,86 +31,47 @@ package com.google.api.gax.logging; import com.google.api.core.InternalApi; -import com.google.gson.JsonObject; +import com.google.api.gax.rpc.internal.EnvironmentProvider; +import com.google.api.gax.rpc.internal.SystemEnvironmentProvider; import java.util.Map; -import java.util.logging.Level; -import java.util.logging.LogRecord; import org.slf4j.ILoggerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; -import org.slf4j.Marker; -import org.slf4j.helpers.FormattingTuple; -import org.slf4j.helpers.MessageFormatter; -import org.slf4j.helpers.NOPLogger; @InternalApi public class LoggingUtils { - private static final java.util.logging.Logger LOGGER = - java.util.logging.Logger.getLogger(LoggingUtils.class.getName()); + private static EnvironmentProvider environmentProvider = SystemEnvironmentProvider.getInstance(); + private static final Logger NO_OP_LOGGER = org.slf4j.helpers.NOPLogger.NOP_LOGGER; + private static boolean loggingEnabled = isLoggingEnabled(); + static final String GOOGLE_SDK_JAVA_LOGGING = "GOOGLE_SDK_JAVA_LOGGING"; + // expose this setter for testing purposes + static void setEnvironmentProvider(EnvironmentProvider provider) { + environmentProvider = provider; + // Recalculate LOGGING_ENABLED after setting the new provider + loggingEnabled = isLoggingEnabled(); + } private LoggingUtils() {} public static Logger getLogger(Class clazz) { - if (!isLoggingEnabled()) { - // use SLF4j's NOP logger regardless of bindings - return LoggerFactory.getLogger(NOPLogger.class); - } - - ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory(); - if (loggerFactory != null && !(loggerFactory instanceof org.slf4j.helpers.NOPLoggerFactory)) { - // Use SLF4j binding when present - return LoggerFactory.getLogger(clazz); - } - // No SLF4j binding found, use JUL as fallback - Logger logger = new JulWrapperLogger(clazz.getName()); - logger.info("No SLF4J providers were found, fall back to JUL."); - return logger; - } - - public static boolean isLoggingEnabled() { - String enableLogging = System.getenv("GOOGLE_SDK_JAVA_LOGGING"); - LOGGER.info("GOOGLE_SDK_JAVA_LOGGING=" + enableLogging); // log for debug now, remove it. - return "true".equalsIgnoreCase(enableLogging); - } - - public static JsonObject mergeJsonObject(JsonObject jsonObject1, JsonObject jsonObject2) { - JsonObject mergedObject = jsonObject1.deepCopy(); - jsonObject2.entrySet().forEach(entry -> mergedObject.add(entry.getKey(), entry.getValue())); - return mergedObject; + return getLogger(clazz, new DefaultLoggerFactoryProvider()); } - public static Level mapToJulLevel(org.slf4j.event.Level slf4jLevel) { - switch (slf4jLevel) { - case ERROR: - return Level.SEVERE; - case WARN: - return Level.WARNING; - case INFO: - return Level.INFO; - case DEBUG: - return Level.FINE; - case TRACE: - return Level.FINEST; - default: - return Level.INFO; + // constructor with LoggerFactoryProvider to make testing easier + static Logger getLogger(Class clazz, LoggerFactoryProvider factoryProvider) { + if (loggingEnabled) { + return factoryProvider.getLoggerFactory().getLogger(clazz.getName()); + } else { + // use SLF4j's NOP logger regardless of bindings + return NO_OP_LOGGER; } } public static void logWithMDC( Logger logger, org.slf4j.event.Level level, Map contextMap, String message) { - - if (logger instanceof JulWrapperLogger) { - // Simulate MDC behavior for JUL - LogRecord record = new LogRecord(mapToJulLevel(level), message); - // Add context map to the LogRecord - record.setParameters(new Object[] {contextMap}); - ((JulWrapperLogger) logger).getJulLogger().log(record); - return; - } contextMap.forEach(MDC::put); - switch (level) { case TRACE: logger.trace(message); @@ -131,337 +92,22 @@ public static void logWithMDC( logger.info(message); // Default to INFO level } - MDC.clear(); } - // JulWrapperLogger implementation - static class JulWrapperLogger implements Logger { - - private final java.util.logging.Logger julLogger; - - public JulWrapperLogger(String name) { - this.julLogger = java.util.logging.Logger.getLogger(name); - } - - public java.util.logging.Logger getJulLogger() { - return julLogger; - } - - @Override - public String getName() { - return julLogger.getName(); - } - - @Override - public boolean isTraceEnabled() { - return julLogger.isLoggable(java.util.logging.Level.FINEST); - } - - @Override - public void trace(String msg) { - julLogger.log(java.util.logging.Level.FINEST, msg); - } - - @Override - public void trace(String s, Object o) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void trace(String s, Object o, Object o1) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void trace(String s, Object... objects) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void trace(String s, Throwable throwable) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public boolean isTraceEnabled(Marker marker) { - return false; - } - - @Override - public void trace(Marker marker, String s) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void trace(Marker marker, String s, Object o) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void trace(Marker marker, String s, Object o, Object o1) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void trace(Marker marker, String s, Object... objects) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void trace(Marker marker, String s, Throwable throwable) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public boolean isDebugEnabled() { - return julLogger.isLoggable(Level.FINE); - } - - @Override - public void debug(String msg) { - - if (isDebugEnabled()) { - julLogger.log(java.util.logging.Level.FINE, msg); - } - } - - @Override - public void debug(String format, Object arg) { - if (isDebugEnabled()) { - FormattingTuple ft = MessageFormatter.format(format, arg); - julLogger.log(Level.FINE, ft.getMessage()); - } - } - - @Override - public void debug(String s, Object o, Object o1) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void debug(String s, Object... objects) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void debug(String s, Throwable throwable) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public boolean isDebugEnabled(Marker marker) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void debug(Marker marker, String s) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void debug(Marker marker, String s, Object o) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void debug(Marker marker, String s, Object o, Object o1) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void debug(Marker marker, String s, Object... objects) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void debug(Marker marker, String s, Throwable throwable) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public boolean isInfoEnabled() { - return julLogger.isLoggable(Level.INFO); - } - - @Override - public void info(String msg) { - if (isInfoEnabled()) { - julLogger.log(java.util.logging.Level.INFO, msg); - } - } - - @Override - public void info(String format, Object arg) { - if (isInfoEnabled()) { - FormattingTuple ft = MessageFormatter.format(format, arg); - julLogger.log(java.util.logging.Level.INFO, ft.getMessage()); - } - } - - @Override - public void info(String s, Object o, Object o1) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void info(String s, Object... objects) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void info(String s, Throwable throwable) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public boolean isInfoEnabled(Marker marker) { - return true; - } - - @Override - public void info(Marker marker, String s) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void info(Marker marker, String s, Object o) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void info(Marker marker, String s, Object o, Object o1) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void info(Marker marker, String s, Object... objects) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void info(Marker marker, String s, Throwable throwable) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public boolean isWarnEnabled() { - return true; - } - - @Override - public void warn(String msg) { - julLogger.log(Level.WARNING, msg); - } - - @Override - public void warn(String s, Object o) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void warn(String s, Object... objects) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void warn(String s, Object o, Object o1) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void warn(String s, Throwable throwable) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public boolean isWarnEnabled(Marker marker) { - return false; - } - - @Override - public void warn(Marker marker, String s) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void warn(Marker marker, String s, Object o) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void warn(Marker marker, String s, Object o, Object o1) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void warn(Marker marker, String s, Object... objects) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void warn(Marker marker, String s, Throwable throwable) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public boolean isErrorEnabled() { - return false; - } - - @Override - public void error(String s) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void error(String s, Object o) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void error(String s, Object o, Object o1) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void error(String s, Object... objects) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void error(String s, Throwable throwable) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public boolean isErrorEnabled(Marker marker) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void error(Marker marker, String s) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void error(Marker marker, String s, Object o) { - throw new UnsupportedOperationException("This method is not supported."); - } - - @Override - public void error(Marker marker, String s, Object o, Object o1) { - throw new UnsupportedOperationException("This method is not supported."); - } + static boolean isLoggingEnabled() { + String enableLogging = environmentProvider.getenv(GOOGLE_SDK_JAVA_LOGGING); + return "true".equalsIgnoreCase(enableLogging); + } - @Override - public void error(Marker marker, String s, Object... objects) { - throw new UnsupportedOperationException("This method is not supported."); - } + interface LoggerFactoryProvider { + ILoggerFactory getLoggerFactory(); + } + static class DefaultLoggerFactoryProvider implements LoggerFactoryProvider { @Override - public void error(Marker marker, String s, Throwable throwable) { - throw new UnsupportedOperationException("This method is not supported."); + public ILoggerFactory getLoggerFactory() { + return LoggerFactory.getILoggerFactory(); } } } diff --git a/gax-java/gax/src/main/java/com/google/api/gax/rpc/internal/SystemEnvironmentProvider.java b/gax-java/gax/src/main/java/com/google/api/gax/rpc/internal/SystemEnvironmentProvider.java new file mode 100644 index 0000000000..29d45ba3c5 --- /dev/null +++ b/gax-java/gax/src/main/java/com/google/api/gax/rpc/internal/SystemEnvironmentProvider.java @@ -0,0 +1,50 @@ +/* + * Copyright 2024 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.rpc.internal; + +import com.google.api.core.InternalApi; + +/** Represents the default system environment provider. */ +@InternalApi +public class SystemEnvironmentProvider implements EnvironmentProvider { + static final SystemEnvironmentProvider INSTANCE = new SystemEnvironmentProvider(); + + private SystemEnvironmentProvider() {} + + @Override + public String getenv(String name) { + return System.getenv(name); + } + + public static SystemEnvironmentProvider getInstance() { + return INSTANCE; + } +} diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java new file mode 100644 index 0000000000..d07a00439b --- /dev/null +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java @@ -0,0 +1,130 @@ +package com.google.api.gax.logging; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.encoder.PatternLayoutEncoder; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.ConsoleAppender; +import com.google.api.gax.logging.LoggingUtils.LoggerFactoryProvider; +import com.google.api.gax.rpc.internal.EnvironmentProvider; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.mockito.Mockito; +import org.slf4j.ILoggerFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.helpers.NOPLogger; + +public class LoggingUtilsTest { + + private EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class); + + @BeforeEach + public void setup() { + EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class); + + // need to setup a ConsoleAppender and attach to root logger because TestAppender + // does not correctly capture MDC info + LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); + + PatternLayoutEncoder patternLayoutEncoder = new PatternLayoutEncoder(); + patternLayoutEncoder.setPattern("%-4relative [%thread] %-5level %logger{35} - %msg%n"); + patternLayoutEncoder.setContext(loggerContext); + + patternLayoutEncoder.start(); + + ConsoleAppender consoleAppender = new ConsoleAppender<>(); + consoleAppender.setEncoder(patternLayoutEncoder); + + consoleAppender.setContext(loggerContext); + consoleAppender.setName("CONSOLE"); + + consoleAppender.start(); + + ch.qos.logback.classic.Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + rootLogger.addAppender(consoleAppender); + } + + @AfterEach + public void tearDown() { + LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); + loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).detachAppender("CONSOLE"); + } + + @org.junit.Test + public void testGetLogger_loggingEnabled_slf4jBindingPresent() { + Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("true"); + LoggingUtils.setEnvironmentProvider(envProvider); + Logger logger = LoggingUtils.getLogger(LoggingUtilsTest.class); + assertTrue(logger instanceof org.slf4j.Logger); + assertNotEquals(logger.getClass(), NOPLogger.class); + } + + @org.junit.Test + public void testGetLogger_loggingDisabled() { + Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("false"); + LoggingUtils.setEnvironmentProvider(envProvider); + + Logger logger = LoggingUtils.getLogger(LoggingUtilsTest.class); + assertEquals(NOPLogger.class, logger.getClass()); + } + + @org.junit.Test + public void testGetLogger_loggingEnabled_noBinding() { + Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("true"); + LoggingUtils.setEnvironmentProvider(envProvider); + // Create a mock LoggerFactoryProvider + LoggerFactoryProvider mockLoggerFactoryProvider = mock(LoggerFactoryProvider.class); + ILoggerFactory mockLoggerFactory = mock(ILoggerFactory.class); + when(mockLoggerFactoryProvider.getLoggerFactory()).thenReturn(mockLoggerFactory); + when(mockLoggerFactory.getLogger(anyString())) + .thenReturn(org.slf4j.helpers.NOPLogger.NOP_LOGGER); + + // Use the mock LoggerFactoryProvider in getLogger() + Logger logger = LoggingUtils.getLogger(LoggingUtilsTest.class, mockLoggerFactoryProvider); + + // Assert that the returned logger is a NOPLogger + assertTrue(logger instanceof org.slf4j.helpers.NOPLogger); + } + + @org.junit.Test + public void testIsLoggingEnabled_true() { + Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("true"); + LoggingUtils.setEnvironmentProvider(envProvider); + assertTrue(LoggingUtils.isLoggingEnabled()); + Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("TRUE"); + LoggingUtils.setEnvironmentProvider(envProvider); + assertTrue(LoggingUtils.isLoggingEnabled()); + Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("True"); + LoggingUtils.setEnvironmentProvider(envProvider); + assertTrue(LoggingUtils.isLoggingEnabled()); + } + + @org.junit.Test + public void testIsLoggingEnabled_defaultToFalse() { + LoggingUtils.setEnvironmentProvider(envProvider); + assertFalse(LoggingUtils.isLoggingEnabled()); + } + + // @Test + // public void testLogWithMDC_slf4jLogger() { + // TestAppender.clearEvents(); + // Map contextMap = new HashMap<>(); + // contextMap.put("key", "value"); + // LoggingUtils.logWithMDC(LOGGER, org.slf4j.event.Level.DEBUG, contextMap, "test message"); + // + // assertEquals(1, TestAppender.events.size()); + // assertEquals("test message", TestAppender.events.get(0).getFormattedMessage()); + // + // // Verify MDC content + // ILoggingEvent event = TestAppender.events.get(0); + // assertEquals("value", event.getMDCPropertyMap().get("key")); + // } +} From 487614cd16c2c088b300906bafd70e060ec890f3 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Thu, 5 Dec 2024 16:31:01 -0500 Subject: [PATCH 12/92] minor clean up.: --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 10 ++- .../gax/logging/JsonContextMapHandler.java | 82 ------------------- 2 files changed, 9 insertions(+), 83 deletions(-) delete mode 100644 gax-java/gax/src/main/java/com/google/api/gax/logging/JsonContextMapHandler.java diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index e599c9eb68..3b3adbd968 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -4,7 +4,15 @@ import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonObject; -import io.grpc.*; +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.ForwardingClientCall; +import io.grpc.ForwardingClientCallListener; +import io.grpc.Metadata; +import io.grpc.MethodDescriptor; +import io.grpc.Status; import java.util.HashMap; import java.util.Map; import java.util.UUID; diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/JsonContextMapHandler.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/JsonContextMapHandler.java deleted file mode 100644 index 870cb62ae5..0000000000 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/JsonContextMapHandler.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2024 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.logging; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import java.util.HashMap; -import java.util.Map; -import java.util.logging.ConsoleHandler; -import java.util.logging.Handler; -import java.util.logging.LogRecord; - -public class JsonContextMapHandler extends Handler { - - private final Gson gson = new GsonBuilder().setPrettyPrinting().create(); - - @Override - public void publish(LogRecord record) { - Object[] params = record.getParameters(); - - ConsoleHandler consoleHandler = new ConsoleHandler(); - if (params != null && params.length > 0 && params[0] instanceof Map) { - @SuppressWarnings("unchecked") - Map contextMap = (Map) params[0]; - - // Create a map to hold all log data - Map logData = new HashMap<>(); - logData.put("message", record.getMessage()); - logData.put("severity", record.getLevel().getName()); - logData.put("timestamp", record.getMillis()); - logData.put("logger", record.getLoggerName()); - - // Add all context data to the top level - logData.putAll(contextMap); - - // Convert to JSON - String jsonLog = gson.toJson(logData); - - LogRecord jsonRecord = new LogRecord(record.getLevel(), jsonLog); - consoleHandler.publish(jsonRecord); - } else { - // Handle cases where the context map is not present - LogRecord messageRecord = - new LogRecord(record.getLevel(), "Log message without context: " + record.getMessage()); - consoleHandler.publish(messageRecord); - } - } - - @Override - public void flush() {} - - @Override - public void close() throws SecurityException {} -} From cc3465bd54128d32c9a1b3731ea66f74c06a7180 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Thu, 5 Dec 2024 17:06:29 -0500 Subject: [PATCH 13/92] fix test after adding interceptor for logging for http. --- ...nstantiatingHttpJsonChannelProviderTest.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProviderTest.java b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProviderTest.java index 2e46157534..641469aa17 100644 --- a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProviderTest.java +++ b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProviderTest.java @@ -118,8 +118,11 @@ void managedChannelUsesDefaultChannelExecutor() throws IOException { // By default, the channel will be wrapped with ManagedHttpJsonInterceptorChannel ManagedHttpJsonInterceptorChannel interceptorChannel = (ManagedHttpJsonInterceptorChannel) httpJsonTransportChannel.getManagedChannel(); - ManagedHttpJsonChannel managedHttpJsonChannel = interceptorChannel.getChannel(); - assertThat(managedHttpJsonChannel.getExecutor()).isNotNull(); + // call getChannel() twice because interceptors are chained in layers by recursive construction + // inside com.google.api.gax.httpjson.InstantiatingHttpJsonChannelProvider.createChannel + ManagedHttpJsonInterceptorChannel managedHttpJsonChannel = (ManagedHttpJsonInterceptorChannel) interceptorChannel.getChannel(); + ManagedHttpJsonChannel channel = managedHttpJsonChannel.getChannel(); + assertThat(channel.getExecutor()).isNotNull(); // Clean up the resources (executor, deadlineScheduler, httpTransport) instantiatingHttpJsonChannelProvider.getTransportChannel().shutdownNow(); @@ -146,9 +149,13 @@ void managedChannelUsesCustomExecutor() throws IOException { // By default, the channel will be wrapped with ManagedHttpJsonInterceptorChannel ManagedHttpJsonInterceptorChannel interceptorChannel = (ManagedHttpJsonInterceptorChannel) httpJsonTransportChannel.getManagedChannel(); - ManagedHttpJsonChannel managedHttpJsonChannel = interceptorChannel.getChannel(); - assertThat(managedHttpJsonChannel.getExecutor()).isNotNull(); - assertThat(managedHttpJsonChannel.getExecutor()).isEqualTo(executor); + // call getChannel() twice because interceptors are chained in layers by recursive construction + // inside com.google.api.gax.httpjson.InstantiatingHttpJsonChannelProvider.createChannel + ManagedHttpJsonInterceptorChannel managedHttpJsonChannel = (ManagedHttpJsonInterceptorChannel) interceptorChannel.getChannel(); + ManagedHttpJsonChannel channel = managedHttpJsonChannel.getChannel(); + + assertThat(channel.getExecutor()).isNotNull(); + assertThat(channel.getExecutor()).isEqualTo(executor); // Clean up the resources (executor, deadlineScheduler, httpTransport) instantiatingHttpJsonChannelProvider.getTransportChannel().shutdownNow(); From 211a3d174c62d6f2116a7bbdcff12f8278498b56 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Thu, 5 Dec 2024 21:49:33 -0500 Subject: [PATCH 14/92] cleanup and fix response header. --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 26 +++++++---- .../httpjson/HttpJsonLoggingInterceptor.java | 44 ++++++++++++------- ...tantiatingHttpJsonChannelProviderTest.java | 6 ++- 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index 3b3adbd968..91b41c6af0 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -9,7 +9,7 @@ import io.grpc.ClientCall; import io.grpc.ClientInterceptor; import io.grpc.ForwardingClientCall; -import io.grpc.ForwardingClientCallListener; +import io.grpc.ForwardingClientCallListener.SimpleForwardingClientCallListener; import io.grpc.Metadata; import io.grpc.MethodDescriptor; import io.grpc.Status; @@ -59,9 +59,19 @@ public void start(Listener responseListener, Metadata headers) { requestLogData.put("request.headers", gson.toJson(requestHeaders)); } - super.start( - new ForwardingClientCallListener.SimpleForwardingClientCallListener( - responseListener) { + SimpleForwardingClientCallListener loggingListener = + new SimpleForwardingClientCallListener(responseListener) { + @Override + public void onHeaders(Metadata headers) { + + if (logger.isDebugEnabled()) { + // Access and add response headers + JsonObject responseHeaders = mapHeadersToJsonObject(headers); + responseLogData.put("response.headers", gson.toJson(responseHeaders)); + } + super.onHeaders(headers); + } + @Override public void onMessage(RespT message) { if (logger.isDebugEnabled()) { @@ -81,9 +91,6 @@ public void onClose(Status status, Metadata trailers) { LoggingUtils.logWithMDC(logger, Level.INFO, serviceAndRpc, "Received response."); } if (logger.isDebugEnabled()) { - // Access and add response headers - JsonObject responseHeaders = mapHeadersToJsonObject(trailers); - responseLogData.put("response.headers", gson.toJson(responseHeaders)); // Add the array of payloads to the responseLogData responseLogData.put("response.payload", gson.toJson(responsePayloads)); @@ -93,8 +100,9 @@ public void onClose(Status status, Metadata trailers) { super.onClose(status, trailers); } - }, - headers); + }; + + super.start(loggingListener, headers); } @Override diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index 10d1c1021c..9c683cc2c5 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -1,11 +1,13 @@ package com.google.api.gax.httpjson; import com.google.api.gax.httpjson.ForwardingHttpJsonClientCall.SimpleForwardingHttpJsonClientCall; +import com.google.api.gax.httpjson.ForwardingHttpJsonClientCallListener.SimpleForwardingHttpJsonClientCallListener; import com.google.api.gax.logging.LoggingUtils; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; import org.slf4j.Logger; @@ -56,22 +58,38 @@ public void start( LoggingUtils.logWithMDC(logger, Level.INFO, serviceAndRpc, "Sending HTTP request"); } Map responseLogData = new HashMap<>(); - super.start( - new HttpJsonClientCall.Listener() { + Listener forwardingResponseListener = + new SimpleForwardingHttpJsonClientCallListener(responseListener) { + @Override + public void onHeaders(HttpJsonMetadata responseHeaders) { + + if (logger.isDebugEnabled()) { + + Map> map = new HashMap<>(); + responseHeaders + .getHeaders() + .forEach((key, value) -> map.put(key, (List) value)); + responseLogData.put("response.headers", gson.toJson(map)); + } + super.onHeaders(responseHeaders); + } + @Override public void onMessage(RespT message) { + if (logger.isDebugEnabled()) { // Add each message to the array responsePayloads.add(gson.toJsonTree(message)); } - responseListener.onMessage(message); + super.onMessage(message); } @Override - public void onClose(int status, HttpJsonMetadata trailers) { + public void onClose(int statusCode, HttpJsonMetadata trailers) { + if (logger.isInfoEnabled()) { - serviceAndRpc.put("response.status", String.valueOf(status)); + serviceAndRpc.put("response.status", String.valueOf(statusCode)); responseLogData.putAll(serviceAndRpc); } if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { @@ -79,13 +97,6 @@ public void onClose(int status, HttpJsonMetadata trailers) { logger, Level.INFO, serviceAndRpc, "HTTP request finished."); } if (logger.isDebugEnabled()) { - - JsonObject jsonHeaders = new JsonObject(); - headers - .getHeaders() - .forEach((key, value) -> jsonHeaders.addProperty(key, value.toString())); - responseLogData.put("response.headers", gson.toJson(jsonHeaders)); - // Add the array of payloads to the responseLogData responseLogData.put("response.payload", gson.toJson(responsePayloads)); LoggingUtils.logWithMDC( @@ -94,11 +105,11 @@ public void onClose(int status, HttpJsonMetadata trailers) { responseLogData, "Received response header and payload."); } - - responseListener.onClose(status, trailers); + super.onClose(statusCode, trailers); } - }, - headers); + }; + + super.start(forwardingResponseListener, headers); } @Override @@ -108,7 +119,6 @@ public void sendMessage(ReqT message) { LoggingUtils.logWithMDC( logger, Level.DEBUG, requestLogData, "HTTP request header and payload."); } - super.sendMessage(message); } }; diff --git a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProviderTest.java b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProviderTest.java index 641469aa17..3e6b2d56d1 100644 --- a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProviderTest.java +++ b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProviderTest.java @@ -120,7 +120,8 @@ void managedChannelUsesDefaultChannelExecutor() throws IOException { (ManagedHttpJsonInterceptorChannel) httpJsonTransportChannel.getManagedChannel(); // call getChannel() twice because interceptors are chained in layers by recursive construction // inside com.google.api.gax.httpjson.InstantiatingHttpJsonChannelProvider.createChannel - ManagedHttpJsonInterceptorChannel managedHttpJsonChannel = (ManagedHttpJsonInterceptorChannel) interceptorChannel.getChannel(); + ManagedHttpJsonInterceptorChannel managedHttpJsonChannel = + (ManagedHttpJsonInterceptorChannel) interceptorChannel.getChannel(); ManagedHttpJsonChannel channel = managedHttpJsonChannel.getChannel(); assertThat(channel.getExecutor()).isNotNull(); @@ -151,7 +152,8 @@ void managedChannelUsesCustomExecutor() throws IOException { (ManagedHttpJsonInterceptorChannel) httpJsonTransportChannel.getManagedChannel(); // call getChannel() twice because interceptors are chained in layers by recursive construction // inside com.google.api.gax.httpjson.InstantiatingHttpJsonChannelProvider.createChannel - ManagedHttpJsonInterceptorChannel managedHttpJsonChannel = (ManagedHttpJsonInterceptorChannel) interceptorChannel.getChannel(); + ManagedHttpJsonInterceptorChannel managedHttpJsonChannel = + (ManagedHttpJsonInterceptorChannel) interceptorChannel.getChannel(); ManagedHttpJsonChannel channel = managedHttpJsonChannel.getChannel(); assertThat(channel.getExecutor()).isNotNull(); From 29702105c0eedbc3b0c8e7eff1c51f464e5bc4f1 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Thu, 5 Dec 2024 22:10:08 -0500 Subject: [PATCH 15/92] lint --- .../java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index 91b41c6af0..ee3e23df20 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -34,6 +34,7 @@ public ClientCall interceptCall( // Initialize a JsonArray to hold all responses JsonArray responsePayloads = new JsonArray(); + return new ForwardingClientCall.SimpleForwardingClientCall( next.newCall(method, callOptions)) { @@ -51,7 +52,6 @@ public void start(Listener responseListener, Metadata headers) { if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { LoggingUtils.logWithMDC(logger, Level.INFO, serviceAndRpc, "Sending gRPC request"); } - if (logger.isDebugEnabled()) { requestLogData.putAll(serviceAndRpc); From 32f8b7cd790aa4cf7413db9702cd853b5fa339ca Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Fri, 6 Dec 2024 09:34:45 -0500 Subject: [PATCH 16/92] lint: license. --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 30 +++++++++++++++++++ .../httpjson/HttpJsonLoggingInterceptor.java | 30 +++++++++++++++++++ .../api/gax/logging/LoggingUtilsTest.java | 30 +++++++++++++++++++ 3 files changed, 90 insertions(+) diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index ee3e23df20..bc22ef9a6d 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -1,3 +1,33 @@ +/* + * Copyright 2024 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 com.google.api.gax.logging.LoggingUtils; diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index 9c683cc2c5..1d9a1c621a 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -1,3 +1,33 @@ +/* + * Copyright 2024 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.httpjson; import com.google.api.gax.httpjson.ForwardingHttpJsonClientCall.SimpleForwardingHttpJsonClientCall; diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java index d07a00439b..fddc565df9 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java @@ -1,3 +1,33 @@ +/* + * Copyright 2024 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.logging; import static org.junit.Assert.assertEquals; From 245c14d68e10a32877725c7035d55ad93272761e Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Fri, 6 Dec 2024 10:36:00 -0500 Subject: [PATCH 17/92] refactor GrpcLoggingInterceptor for readability, intro LogData. --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 144 ++++++++++-------- .../com/google/api/gax/logging/LogData.java | 139 +++++++++++++++++ 2 files changed, 223 insertions(+), 60 deletions(-) create mode 100644 gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index bc22ef9a6d..ca70ae2c7f 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -30,9 +30,9 @@ package com.google.api.gax.grpc; +import com.google.api.gax.logging.LogData; import com.google.api.gax.logging.LoggingUtils; import com.google.gson.Gson; -import com.google.gson.JsonArray; import com.google.gson.JsonObject; import io.grpc.CallOptions; import io.grpc.Channel; @@ -43,7 +43,6 @@ import io.grpc.Metadata; import io.grpc.MethodDescriptor; import io.grpc.Status; -import java.util.HashMap; import java.util.Map; import java.util.UUID; import org.slf4j.Logger; @@ -58,96 +57,121 @@ public class GrpcLoggingInterceptor implements ClientInterceptor { public ClientCall interceptCall( MethodDescriptor method, CallOptions callOptions, Channel next) { - Map serviceAndRpc = new HashMap<>(); - Map requestLogData = new HashMap<>(); - Map responseLogData = new HashMap<>(); - - // Initialize a JsonArray to hold all responses - JsonArray responsePayloads = new JsonArray(); + LogData.Builder logDataBuilder = LogData.builder(); return new ForwardingClientCall.SimpleForwardingClientCall( next.newCall(method, callOptions)) { @Override public void start(Listener responseListener, Metadata headers) { - if (logger.isInfoEnabled()) { - String requestId = UUID.randomUUID().toString(); - String serviceName = method.getServiceName(); - String methodName = method.getFullMethodName(); - - serviceAndRpc.put("serviceName", serviceName); - serviceAndRpc.put("rpcName", methodName); - serviceAndRpc.put("requestId", requestId); - } - if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { - LoggingUtils.logWithMDC(logger, Level.INFO, serviceAndRpc, "Sending gRPC request"); - } - if (logger.isDebugEnabled()) { - requestLogData.putAll(serviceAndRpc); - - JsonObject requestHeaders = mapHeadersToJsonObject(headers); - requestLogData.put("request.headers", gson.toJson(requestHeaders)); - } - - SimpleForwardingClientCallListener loggingListener = + logRequest(method, logDataBuilder, headers); + SimpleForwardingClientCallListener responseLoggingListener = new SimpleForwardingClientCallListener(responseListener) { @Override public void onHeaders(Metadata headers) { - - if (logger.isDebugEnabled()) { - // Access and add response headers - JsonObject responseHeaders = mapHeadersToJsonObject(headers); - responseLogData.put("response.headers", gson.toJson(responseHeaders)); - } + recordResponseHeaders(headers, logDataBuilder); super.onHeaders(headers); } @Override public void onMessage(RespT message) { - if (logger.isDebugEnabled()) { - // Add each message to the array - responsePayloads.add(gson.toJsonTree(message)); - } + recordResponsePayload(message, logDataBuilder); super.onMessage(message); } @Override public void onClose(Status status, Metadata trailers) { - if (logger.isInfoEnabled()) { - serviceAndRpc.put("response.status", status.getCode().name()); - responseLogData.putAll(serviceAndRpc); - } - if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { - LoggingUtils.logWithMDC(logger, Level.INFO, serviceAndRpc, "Received response."); - } - if (logger.isDebugEnabled()) { - // Add the array of payloads to the responseLogData - responseLogData.put("response.payload", gson.toJson(responsePayloads)); - - LoggingUtils.logWithMDC( - logger, Level.DEBUG, responseLogData, "Received response."); - } - + logResponse(status, logDataBuilder); super.onClose(status, trailers); } }; - super.start(loggingListener, headers); + super.start(responseLoggingListener, headers); } @Override public void sendMessage(ReqT message) { - - if (logger.isDebugEnabled()) { - requestLogData.put("request.payload", gson.toJson(message)); - LoggingUtils.logWithMDC(logger, Level.DEBUG, requestLogData, "Sending gRPC request."); - } - + logResponseDetails(message, logDataBuilder); super.sendMessage(message); } }; } + // --- Helper methods for logging --- + private void logRequest( + MethodDescriptor method, LogData.Builder logDataBuilder, Metadata headers) { + + if (logger.isInfoEnabled()) { + String requestId = UUID.randomUUID().toString(); + logDataBuilder + .serviceName(method.getServiceName()) + .rpcName(method.getFullMethodName()) + .requestId(requestId); + // serviceAndRpc.put("serviceName", method.getServiceName()); + // serviceAndRpc.put("rpcName", method.getFullMethodName()); + // serviceAndRpc.put("requestId", requestId); + + if (!logger.isDebugEnabled()) { + LoggingUtils.logWithMDC( + logger, + Level.INFO, + logDataBuilder.build().serviceAndRpcToMap(), + "Sending gRPC request"); + } + } + if (logger.isDebugEnabled()) { + // requestLogData.putAll(serviceAndRpc); + JsonObject requestHeaders = mapHeadersToJsonObject(headers); + // requestLogData.put("request.headers", gson.toJson(requestHeaders)); + logDataBuilder.requestHeaders(gson.toJson(requestHeaders)); + } + } + + private void recordResponseHeaders(Metadata headers, LogData.Builder logDataBuilder) { + if (logger.isDebugEnabled()) { + // Access and add response headers + JsonObject responseHeaders = mapHeadersToJsonObject(headers); + // responseLogData.put("response.headers", gson.toJson(responseHeaders)); + logDataBuilder.responseHeaders(gson.toJson(responseHeaders)); + } + } + + private void recordResponsePayload(RespT message, LogData.Builder logDataBuilder) { + if (logger.isDebugEnabled()) { + // Add each message to the array + // responsePayloads.add(gson.toJsonTree(message)); + logDataBuilder.responsePayload(gson.toJsonTree(message)); + } + } + + private void logResponse(Status status, LogData.Builder logDataBuilder) { + if (logger.isInfoEnabled()) { + // serviceAndRpc.put("response.status", status.getCode().name()); + // responseLogData.putAll(serviceAndRpc); + logDataBuilder.responseStatus(status.getCode().name()); + } + if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { + Map responseData = logDataBuilder.build().responseInfoToMap(); + LoggingUtils.logWithMDC(logger, Level.INFO, responseData, "Received response."); + } + if (logger.isDebugEnabled()) { + // Add the array of payloads to the responseLogData + // responseLogData.put("response.payload", gson.toJson(responsePayloads)); + // logDataBuilder.responsePayload(gson.toJson(responsePayloads)); + Map responsedDetailsMap = logDataBuilder.build().responseDetailsToMap(); + LoggingUtils.logWithMDC(logger, Level.DEBUG, responsedDetailsMap, "Received response."); + } + } + + private void logResponseDetails(RespT message, LogData.Builder logDataBuilder) { + if (logger.isDebugEnabled()) { + // requestLogData.put("request.payload", gson.toJson(message)); + logDataBuilder.requestPayload(gson.toJson(message)); + Map requestDetailsMap = logDataBuilder.build().requestDetailsToMap(); + LoggingUtils.logWithMDC(logger, Level.DEBUG, requestDetailsMap, "Sending gRPC request."); + } + } + private static JsonObject mapHeadersToJsonObject(Metadata headers) { JsonObject jsonHeaders = new JsonObject(); headers diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java new file mode 100644 index 0000000000..8008cab6d8 --- /dev/null +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java @@ -0,0 +1,139 @@ +/* + * Copyright 2024 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.logging; + +import com.google.api.core.InternalApi; +import com.google.auto.value.AutoValue; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Nullable; + +@InternalApi +@AutoValue +public abstract class LogData { + private static final Gson gson = new Gson(); + + public abstract String serviceName(); + + public abstract String rpcName(); + + @Nullable + public abstract String requestId(); + + @Nullable + public abstract String requestHeaders(); + + @Nullable + public abstract String requestPayload(); + + @Nullable + public abstract String responseStatus(); + + @Nullable + public abstract String responseHeaders(); + + @Nullable + public abstract JsonElement responsePayload(); + + public static Builder builder() { + return new AutoValue_LogData.Builder(); + } + + @AutoValue.Builder + public abstract static class Builder { + public abstract Builder serviceName(String serviceName); + + public abstract Builder rpcName(String rpcName); + + public abstract Builder requestId(String requestId); + + public abstract Builder requestHeaders(String requestHeaders); + + public abstract Builder requestPayload(String requestPayload); + + public abstract Builder responseStatus(String responseStatus); + + public abstract Builder responseHeaders(String responseHeaders); + + public abstract Builder responsePayload(JsonElement responsePayload); + + public abstract LogData build(); + } + + public Map serviceAndRpcToMap() { + Map map = new HashMap<>(); + map.put("serviceName", serviceName()); + map.put("rpcName", rpcName()); + return map; + } + + public Map requestDetailsToMap() { + Map map = new HashMap<>(); + map.put("serviceName", serviceName()); + map.put("rpcName", rpcName()); + map.put("request.headers", requestHeaders()); + map.put("request.payload", requestPayload()); + return map; + } + + public Map responseInfoToMap() { + Map map = new HashMap<>(); + map.put("serviceName", serviceName()); + map.put("rpcName", rpcName()); + map.put("response.status", responseStatus()); + return map; + } + + public Map responseDetailsToMap() { + Map map = new HashMap<>(); + map.put("serviceName", serviceName()); + map.put("rpcName", rpcName()); + map.put("response.status", responseStatus()); + map.put("response.payload", gson.toJson(responsePayload())); + map.put("response.headers", responseHeaders()); + return map; + } + + public Map toMap() { + Map map = new HashMap<>(); + map.put("serviceName", serviceName()); + map.put("rpcName", rpcName()); + map.put("requestId", requestId()); + map.put("request.headers", requestHeaders()); + map.put("request.payload", requestPayload()); + map.put("response.status", responseStatus()); + map.put("response.headers", responseHeaders()); + map.put("response.payload", responsePayload().toString()); + return map; + } +} From 8358497907d35db5d9b06a50313e4b6d35d9bd7c Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Fri, 6 Dec 2024 13:27:40 -0500 Subject: [PATCH 18/92] minor cleanups. --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 30 +++++-------------- .../com/google/api/gax/logging/LogData.java | 2 ++ .../showcase/v1beta1/it/ITUnaryCallable.java | 28 ----------------- .../src/test/resources/logback-test.xml | 11 +++++-- 4 files changed, 18 insertions(+), 53 deletions(-) diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index ca70ae2c7f..931d45bce1 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -64,7 +64,8 @@ public ClientCall interceptCall( @Override public void start(Listener responseListener, Metadata headers) { - logRequest(method, logDataBuilder, headers); + logRequestInfo(method, logDataBuilder); + recordRequestHeaders(logDataBuilder, headers); SimpleForwardingClientCallListener responseLoggingListener = new SimpleForwardingClientCallListener(responseListener) { @Override @@ -85,31 +86,25 @@ public void onClose(Status status, Metadata trailers) { super.onClose(status, trailers); } }; - super.start(responseLoggingListener, headers); } - @Override public void sendMessage(ReqT message) { - logResponseDetails(message, logDataBuilder); + logRequestDetails(message, logDataBuilder); super.sendMessage(message); } }; } // --- Helper methods for logging --- - private void logRequest( - MethodDescriptor method, LogData.Builder logDataBuilder, Metadata headers) { - + private void logRequestInfo( + MethodDescriptor method, LogData.Builder logDataBuilder) { if (logger.isInfoEnabled()) { String requestId = UUID.randomUUID().toString(); logDataBuilder .serviceName(method.getServiceName()) .rpcName(method.getFullMethodName()) .requestId(requestId); - // serviceAndRpc.put("serviceName", method.getServiceName()); - // serviceAndRpc.put("rpcName", method.getFullMethodName()); - // serviceAndRpc.put("requestId", requestId); if (!logger.isDebugEnabled()) { LoggingUtils.logWithMDC( @@ -119,19 +114,17 @@ private void logRequest( "Sending gRPC request"); } } + } + private void recordRequestHeaders(LogData.Builder logDataBuilder, Metadata headers) { if (logger.isDebugEnabled()) { - // requestLogData.putAll(serviceAndRpc); JsonObject requestHeaders = mapHeadersToJsonObject(headers); - // requestLogData.put("request.headers", gson.toJson(requestHeaders)); logDataBuilder.requestHeaders(gson.toJson(requestHeaders)); } } - private void recordResponseHeaders(Metadata headers, LogData.Builder logDataBuilder) { if (logger.isDebugEnabled()) { // Access and add response headers JsonObject responseHeaders = mapHeadersToJsonObject(headers); - // responseLogData.put("response.headers", gson.toJson(responseHeaders)); logDataBuilder.responseHeaders(gson.toJson(responseHeaders)); } } @@ -139,15 +132,12 @@ private void recordResponseHeaders(Metadata headers, LogData.Builder logDataBuil private void recordResponsePayload(RespT message, LogData.Builder logDataBuilder) { if (logger.isDebugEnabled()) { // Add each message to the array - // responsePayloads.add(gson.toJsonTree(message)); logDataBuilder.responsePayload(gson.toJsonTree(message)); } } private void logResponse(Status status, LogData.Builder logDataBuilder) { if (logger.isInfoEnabled()) { - // serviceAndRpc.put("response.status", status.getCode().name()); - // responseLogData.putAll(serviceAndRpc); logDataBuilder.responseStatus(status.getCode().name()); } if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { @@ -155,17 +145,13 @@ private void logResponse(Status status, LogData.Builder logDataBuilder) { LoggingUtils.logWithMDC(logger, Level.INFO, responseData, "Received response."); } if (logger.isDebugEnabled()) { - // Add the array of payloads to the responseLogData - // responseLogData.put("response.payload", gson.toJson(responsePayloads)); - // logDataBuilder.responsePayload(gson.toJson(responsePayloads)); Map responsedDetailsMap = logDataBuilder.build().responseDetailsToMap(); LoggingUtils.logWithMDC(logger, Level.DEBUG, responsedDetailsMap, "Received response."); } } - private void logResponseDetails(RespT message, LogData.Builder logDataBuilder) { + private void logRequestDetails(RespT message, LogData.Builder logDataBuilder) { if (logger.isDebugEnabled()) { - // requestLogData.put("request.payload", gson.toJson(message)); logDataBuilder.requestPayload(gson.toJson(message)); Map requestDetailsMap = logDataBuilder.build().requestDetailsToMap(); LoggingUtils.logWithMDC(logger, Level.DEBUG, requestDetailsMap, "Sending gRPC request."); diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java index 8008cab6d8..b3be9f0222 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java @@ -90,6 +90,8 @@ public abstract static class Builder { public abstract LogData build(); } + // helper functions to convert to map for logging + // todo: error handling? public Map serviceAndRpcToMap() { Map map = new HashMap<>(); map.put("serviceName", serviceName()); diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITUnaryCallable.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITUnaryCallable.java index edb98facdb..76a78e7ff9 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITUnaryCallable.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITUnaryCallable.java @@ -20,7 +20,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import com.google.api.gax.grpc.GrpcStatusCode; -import com.google.api.gax.logging.JsonContextMapHandler; import com.google.api.gax.rpc.CancelledException; import com.google.api.gax.rpc.StatusCode; import com.google.rpc.Status; @@ -29,12 +28,8 @@ import com.google.showcase.v1beta1.EchoResponse; import com.google.showcase.v1beta1.it.util.TestClientInitializer; import java.util.concurrent.TimeUnit; -import java.util.logging.ConsoleHandler; -import java.util.logging.Handler; import java.util.logging.Level; -import java.util.logging.LogManager; import java.util.logging.Logger; -import java.util.logging.SimpleFormatter; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -53,29 +48,6 @@ static void createClients() throws Exception { // Create Http JSON Echo Client httpjsonClient = TestClientInitializer.createHttpJsonEchoClient(); - // Settings for JUL as fallback - // Get the root logger - Logger rootLogger = LogManager.getLogManager().getLogger(""); - // Set the root logger's level to ALL to see DEBUG messages - // rootLogger.setLevel(Level.ALL); - // Remove any existing handlers - for (Handler handler : rootLogger.getHandlers()) { - rootLogger.removeHandler(handler); - } - - // Create and add your ContextMapHandler - JsonContextMapHandler contextHandler = new JsonContextMapHandler(); - contextHandler.setLevel(Level.ALL); // Set the desired level - // Add a formatter if needed (optional) - // contextHandler.setFormatter(...); - rootLogger.addHandler(contextHandler); - // Create a ConsoleHandler - ConsoleHandler consoleHandler = new ConsoleHandler(); - consoleHandler.setLevel(Level.ALL); - consoleHandler.setFormatter(new SimpleFormatter()); - - // Add the ConsoleHandler to the root logger - rootLogger.addHandler(consoleHandler); LOGGER.log(Level.INFO, "This is log message directly from JUL. Clients created."); } diff --git a/showcase/gapic-showcase/src/test/resources/logback-test.xml b/showcase/gapic-showcase/src/test/resources/logback-test.xml index f216becaa9..2fabc5467a 100644 --- a/showcase/gapic-showcase/src/test/resources/logback-test.xml +++ b/showcase/gapic-showcase/src/test/resources/logback-test.xml @@ -8,10 +8,15 @@ - logger.equals("com.google.api.gax.grpc.GrpcLoggingInterceptor") || + + ((logger.equals("com.google.api.gax.grpc.GrpcLoggingInterceptor") || logger.equals("com.google.api.gax.httpjson.HttpJsonLoggingInterceptor") || - logger.equals("com.google.api.gax.tracing.MetricsTracer") - + logger.equals("com.google.api.gax.tracing.MetricsTracer")) + && (mdc.get("serviceName").equals("google.showcase.v1beta1.Echo"))) + + + + ACCEPT DENY From 97087b22582774397ed34612187b540b774c9d6d Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Fri, 6 Dec 2024 14:55:06 -0500 Subject: [PATCH 19/92] GrpcLoggingInterceptor fix for thread safe, separate out debug logging entries, add request id to track. --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 110 ++++++++++-------- .../com/google/api/gax/logging/LogData.java | 4 +- 2 files changed, 67 insertions(+), 47 deletions(-) diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index 931d45bce1..82fd8bc9f3 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -56,18 +56,19 @@ public class GrpcLoggingInterceptor implements ClientInterceptor { @Override public ClientCall interceptCall( MethodDescriptor method, CallOptions callOptions, Channel next) { - - LogData.Builder logDataBuilder = LogData.builder(); - return new ForwardingClientCall.SimpleForwardingClientCall( next.newCall(method, callOptions)) { + // Generate request ID here + String requestId = UUID.randomUUID().toString(); + @Override public void start(Listener responseListener, Metadata headers) { - logRequestInfo(method, logDataBuilder); - recordRequestHeaders(logDataBuilder, headers); + logRequestInfoAndHeaders(method, headers, requestId); SimpleForwardingClientCallListener responseLoggingListener = new SimpleForwardingClientCallListener(responseListener) { + LogData.Builder logDataBuilder = LogData.builder(); + @Override public void onHeaders(Metadata headers) { recordResponseHeaders(headers, logDataBuilder); @@ -82,48 +83,56 @@ public void onMessage(RespT message) { @Override public void onClose(Status status, Metadata trailers) { - logResponse(status, logDataBuilder); + try { + logResponse(status, logDataBuilder, requestId); + } finally { + logDataBuilder = null; // release resource + } super.onClose(status, trailers); } }; super.start(responseLoggingListener, headers); } + @Override public void sendMessage(ReqT message) { - logRequestDetails(message, logDataBuilder); + logRequestDetails(message, requestId); super.sendMessage(message); } }; } // --- Helper methods for logging --- - private void logRequestInfo( - MethodDescriptor method, LogData.Builder logDataBuilder) { - if (logger.isInfoEnabled()) { - String requestId = UUID.randomUUID().toString(); - logDataBuilder - .serviceName(method.getServiceName()) - .rpcName(method.getFullMethodName()) - .requestId(requestId); - - if (!logger.isDebugEnabled()) { - LoggingUtils.logWithMDC( - logger, - Level.INFO, - logDataBuilder.build().serviceAndRpcToMap(), - "Sending gRPC request"); + private void logRequestInfoAndHeaders( + MethodDescriptor method, Metadata headers, String requestId) { + try { + if (logger.isInfoEnabled()) { + LogData.Builder logDataBuilder = LogData.builder(); + logDataBuilder + .serviceName(method.getServiceName()) + .rpcName(method.getFullMethodName()) + .requestId(requestId); + + if (logger.isDebugEnabled()) { + JsonObject requestHeaders = mapHeadersToJsonObject(headers); + logDataBuilder.requestHeaders(gson.toJson(requestHeaders)); + LoggingUtils.logWithMDC( + logger, Level.DEBUG, logDataBuilder.build().toMap(), "Sending gRPC request."); + } else { + LoggingUtils.logWithMDC( + logger, + Level.INFO, + logDataBuilder.build().serviceAndRpcToMap(), + "Sending gRPC request"); + } } + } catch (Exception e) { + logger.error("Error logging request info (and headers)", e); } } - private void recordRequestHeaders(LogData.Builder logDataBuilder, Metadata headers) { - if (logger.isDebugEnabled()) { - JsonObject requestHeaders = mapHeadersToJsonObject(headers); - logDataBuilder.requestHeaders(gson.toJson(requestHeaders)); - } - } + private void recordResponseHeaders(Metadata headers, LogData.Builder logDataBuilder) { if (logger.isDebugEnabled()) { - // Access and add response headers JsonObject responseHeaders = mapHeadersToJsonObject(headers); logDataBuilder.responseHeaders(gson.toJson(responseHeaders)); } @@ -131,30 +140,39 @@ private void recordResponseHeaders(Metadata headers, LogData.Builder logDataBuil private void recordResponsePayload(RespT message, LogData.Builder logDataBuilder) { if (logger.isDebugEnabled()) { - // Add each message to the array logDataBuilder.responsePayload(gson.toJsonTree(message)); } } - private void logResponse(Status status, LogData.Builder logDataBuilder) { - if (logger.isInfoEnabled()) { - logDataBuilder.responseStatus(status.getCode().name()); - } - if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { - Map responseData = logDataBuilder.build().responseInfoToMap(); - LoggingUtils.logWithMDC(logger, Level.INFO, responseData, "Received response."); - } - if (logger.isDebugEnabled()) { - Map responsedDetailsMap = logDataBuilder.build().responseDetailsToMap(); - LoggingUtils.logWithMDC(logger, Level.DEBUG, responsedDetailsMap, "Received response."); + private void logResponse(Status status, LogData.Builder logDataBuilder, String requestId) { + try { + if (logger.isInfoEnabled()) { + logDataBuilder.responseStatus(status.getCode().name()).requestId(requestId); + } + if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { + Map responseData = logDataBuilder.build().toMap(); + LoggingUtils.logWithMDC(logger, Level.INFO, responseData, "Received response."); + } + if (logger.isDebugEnabled()) { + Map responsedDetailsMap = logDataBuilder.build().toMap(); + LoggingUtils.logWithMDC(logger, Level.DEBUG, responsedDetailsMap, "Received response."); + } + } catch (Exception e) { + logger.error("Error logging request response", e); } } - private void logRequestDetails(RespT message, LogData.Builder logDataBuilder) { - if (logger.isDebugEnabled()) { - logDataBuilder.requestPayload(gson.toJson(message)); - Map requestDetailsMap = logDataBuilder.build().requestDetailsToMap(); - LoggingUtils.logWithMDC(logger, Level.DEBUG, requestDetailsMap, "Sending gRPC request."); + private void logRequestDetails(RespT message, String requestId) { + try { + if (logger.isDebugEnabled()) { + LogData.Builder logDataBuilder = LogData.builder(); + logDataBuilder.requestPayload(gson.toJson(message)).requestId(requestId); + Map requestDetailsMap = logDataBuilder.build().toMap(); + LoggingUtils.logWithMDC( + logger, Level.DEBUG, requestDetailsMap, "Sending gRPC request: request payload."); + } + } catch (Exception e) { + logger.error("Error logging request details", e); } } diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java index b3be9f0222..ecb97414eb 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java @@ -43,8 +43,10 @@ public abstract class LogData { private static final Gson gson = new Gson(); + @Nullable public abstract String serviceName(); + @Nullable public abstract String rpcName(); @Nullable @@ -135,7 +137,7 @@ public Map toMap() { map.put("request.payload", requestPayload()); map.put("response.status", responseStatus()); map.put("response.headers", responseHeaders()); - map.put("response.payload", responsePayload().toString()); + map.put("response.payload", gson.toJson(responsePayload())); return map; } } From 727a3e90ff4cd6aeb1d33fa8ea3a7ddcdf226987 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Fri, 6 Dec 2024 16:36:07 -0500 Subject: [PATCH 20/92] minor changes + add showcase it test.(need to run with env var, setup not done yet.) --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 13 +- .../httpjson/HttpJsonLoggingInterceptor.java | 2 +- .../com/google/api/gax/logging/LogData.java | 66 +++----- showcase/gapic-showcase/pom.xml | 6 + .../google/showcase/v1beta1/it/ITLogging.java | 146 ++++++++++++++++++ .../v1beta1/it/util/TestAppender.java | 40 +++++ .../src/test/resources/logback-test.xml | 64 +++++--- 7 files changed, 261 insertions(+), 76 deletions(-) create mode 100644 showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLogging.java create mode 100644 showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/util/TestAppender.java diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index 82fd8bc9f3..5fceaf81b2 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -117,13 +117,10 @@ private void logRequestInfoAndHeaders( JsonObject requestHeaders = mapHeadersToJsonObject(headers); logDataBuilder.requestHeaders(gson.toJson(requestHeaders)); LoggingUtils.logWithMDC( - logger, Level.DEBUG, logDataBuilder.build().toMap(), "Sending gRPC request."); + logger, Level.DEBUG, logDataBuilder.build().toMap(), "Sending gRPC request"); } else { LoggingUtils.logWithMDC( - logger, - Level.INFO, - logDataBuilder.build().serviceAndRpcToMap(), - "Sending gRPC request"); + logger, Level.INFO, logDataBuilder.build().toMap(), "Sending gRPC request"); } } } catch (Exception e) { @@ -151,11 +148,11 @@ private void logResponse(Status status, LogData.Builder logDataBuilder, String r } if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { Map responseData = logDataBuilder.build().toMap(); - LoggingUtils.logWithMDC(logger, Level.INFO, responseData, "Received response."); + LoggingUtils.logWithMDC(logger, Level.INFO, responseData, "Received Grpc response"); } if (logger.isDebugEnabled()) { Map responsedDetailsMap = logDataBuilder.build().toMap(); - LoggingUtils.logWithMDC(logger, Level.DEBUG, responsedDetailsMap, "Received response."); + LoggingUtils.logWithMDC(logger, Level.DEBUG, responsedDetailsMap, "Received Grpc response"); } } catch (Exception e) { logger.error("Error logging request response", e); @@ -169,7 +166,7 @@ private void logRequestDetails(RespT message, String requestId) { logDataBuilder.requestPayload(gson.toJson(message)).requestId(requestId); Map requestDetailsMap = logDataBuilder.build().toMap(); LoggingUtils.logWithMDC( - logger, Level.DEBUG, requestDetailsMap, "Sending gRPC request: request payload."); + logger, Level.DEBUG, requestDetailsMap, "Sending gRPC request: request payload"); } } catch (Exception e) { logger.error("Error logging request details", e); diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index 1d9a1c621a..43df5775d0 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -124,7 +124,7 @@ public void onClose(int statusCode, HttpJsonMetadata trailers) { } if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { LoggingUtils.logWithMDC( - logger, Level.INFO, serviceAndRpc, "HTTP request finished."); + logger, Level.INFO, serviceAndRpc, "received HTTP response"); } if (logger.isDebugEnabled()) { // Add the array of payloads to the responseLogData diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java index ecb97414eb..67ceb8057e 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java @@ -94,50 +94,32 @@ public abstract static class Builder { // helper functions to convert to map for logging // todo: error handling? - public Map serviceAndRpcToMap() { - Map map = new HashMap<>(); - map.put("serviceName", serviceName()); - map.put("rpcName", rpcName()); - return map; - } - - public Map requestDetailsToMap() { - Map map = new HashMap<>(); - map.put("serviceName", serviceName()); - map.put("rpcName", rpcName()); - map.put("request.headers", requestHeaders()); - map.put("request.payload", requestPayload()); - return map; - } - - public Map responseInfoToMap() { - Map map = new HashMap<>(); - map.put("serviceName", serviceName()); - map.put("rpcName", rpcName()); - map.put("response.status", responseStatus()); - return map; - } - - public Map responseDetailsToMap() { - Map map = new HashMap<>(); - map.put("serviceName", serviceName()); - map.put("rpcName", rpcName()); - map.put("response.status", responseStatus()); - map.put("response.payload", gson.toJson(responsePayload())); - map.put("response.headers", responseHeaders()); - return map; - } - public Map toMap() { Map map = new HashMap<>(); - map.put("serviceName", serviceName()); - map.put("rpcName", rpcName()); - map.put("requestId", requestId()); - map.put("request.headers", requestHeaders()); - map.put("request.payload", requestPayload()); - map.put("response.status", responseStatus()); - map.put("response.headers", responseHeaders()); - map.put("response.payload", gson.toJson(responsePayload())); + if (serviceName() != null) { + map.put("serviceName", serviceName()); + } + if (rpcName() != null) { + map.put("rpcName", rpcName()); + } + if (requestId() != null) { + map.put("requestId", requestId()); + } + if (requestHeaders() != null) { + map.put("request.headers", requestHeaders()); + } + if (requestPayload() != null) { + map.put("request.payload", requestPayload()); + } + if (responseStatus() != null) { + map.put("response.status", responseStatus()); + } + if (responseHeaders() != null) { + map.put("response.headers", responseHeaders()); + } + if (responsePayload() != null) { + map.put("response.payload", gson.toJson(responsePayload())); + } return map; } } diff --git a/showcase/gapic-showcase/pom.xml b/showcase/gapic-showcase/pom.xml index 48d8212834..cbf15d5269 100644 --- a/showcase/gapic-showcase/pom.xml +++ b/showcase/gapic-showcase/pom.xml @@ -245,5 +245,11 @@ 3.1.9 test + + org.mockito + mockito-core + 4.11.0 + test + diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLogging.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLogging.java new file mode 100644 index 0000000000..41d4993dec --- /dev/null +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLogging.java @@ -0,0 +1,146 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.showcase.v1beta1.it; + +import static com.google.common.truth.Truth.assertThat; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import com.google.api.gax.grpc.GrpcLoggingInterceptor; +import com.google.api.gax.httpjson.HttpJsonLoggingInterceptor; +import com.google.api.gax.logging.LoggingUtils; +import com.google.showcase.v1beta1.EchoClient; +import com.google.showcase.v1beta1.EchoRequest; +import com.google.showcase.v1beta1.EchoResponse; +import com.google.showcase.v1beta1.it.util.TestAppender; +import com.google.showcase.v1beta1.it.util.TestClientInitializer; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +// This test needs to run with GOOGLE_SDK_JAVA_LOGGING=true +public class ITLogging { + private static EchoClient grpcClient; + + private static EchoClient httpjsonClient; + static final Logger LOGGER = Logger.getLogger(ITUnaryCallable.class.getName()); + + @BeforeAll + static void createClients() throws Exception { + // Create gRPC Echo Client + grpcClient = TestClientInitializer.createGrpcEchoClient(); + // Create Http JSON Echo Client + httpjsonClient = TestClientInitializer.createHttpJsonEchoClient(); + + // EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class); + // Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("true"); + // LoggingUtils.setEnvironmentProvider(envProvider); + LOGGER.log(Level.INFO, "This is log message directly from JUL. Clients created."); + } + + @AfterAll + static void destroyClients() throws InterruptedException { + grpcClient.close(); + httpjsonClient.close(); + + grpcClient.awaitTermination(TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS); + httpjsonClient.awaitTermination( + TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS); + } + + @Test + void testGrpc_receiveContent_logDebug() { + LOGGER.log( + Level.INFO, + "This is log message directly from JUL. Starting test: testGrpc_receiveContent."); + + TestAppender.clearEvents(); + assertThat(echoGrpc("grpc-echo?")).isEqualTo("grpc-echo?"); + assertThat(TestAppender.events.size()).isEqualTo(3); + assertThat(TestAppender.events.get(0).getMessage()).isEqualTo("Sending gRPC request"); + assertThat(TestAppender.events.get(0).getLevel()).isEqualTo(ch.qos.logback.classic.Level.DEBUG); + assertThat(TestAppender.events.get(1).getMessage()) + .isEqualTo("Sending gRPC request: request payload"); + assertThat(TestAppender.events.get(2).getMessage()).isEqualTo("Received Grpc response"); + } + + @Test + void testGrpc_receiveContent_logInfo() { + ch.qos.logback.classic.Logger logger = + (ch.qos.logback.classic.Logger) LoggingUtils.getLogger(GrpcLoggingInterceptor.class); + ch.qos.logback.classic.Level originalLevel = logger.getLevel(); + try { + logger.setLevel(ch.qos.logback.classic.Level.INFO); + assertThat(logger.getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); + + TestAppender.clearEvents(); + assertThat(echoGrpc("grpc-echo?")).isEqualTo("grpc-echo?"); + assertThat(TestAppender.events.size()).isEqualTo(2); + ILoggingEvent loggingEvent1 = TestAppender.events.get(0); + assertThat(loggingEvent1.getMessage()).isEqualTo("Sending gRPC request"); + assertThat(loggingEvent1.getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); + assertThat(loggingEvent1.getMDCPropertyMap()).hasSize(3); + assertThat(loggingEvent1.getMDCPropertyMap()) + .containsEntry("serviceName", "google.showcase.v1beta1.Echo"); + assertThat(loggingEvent1.getMDCPropertyMap()) + .containsEntry("rpcName", "google.showcase.v1beta1.Echo/Echo"); + assertThat(TestAppender.events.get(1).getMessage()).isEqualTo("Received Grpc response"); + assertThat(TestAppender.events.get(1).getLevel()) + .isEqualTo(ch.qos.logback.classic.Level.INFO); + } finally { + logger.setLevel(originalLevel); + } + } + + @Test + void testHttpJson_receiveContent() { + + ch.qos.logback.classic.Logger logger = + (ch.qos.logback.classic.Logger) LoggingUtils.getLogger(HttpJsonLoggingInterceptor.class); + ch.qos.logback.classic.Level originalLevel = logger.getLevel(); + try { + logger.setLevel(ch.qos.logback.classic.Level.INFO); + assertThat(logger.getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); + + TestAppender.clearEvents(); + assertThat(echoHttpJson("http-echo?")).isEqualTo("http-echo?"); + assertThat(TestAppender.events.size()).isEqualTo(2); + ILoggingEvent loggingEvent1 = TestAppender.events.get(0); + assertThat(loggingEvent1.getMessage()).isEqualTo("Sending HTTP request"); + assertThat(loggingEvent1.getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); + assertThat(loggingEvent1.getMDCPropertyMap()).hasSize(2); + assertThat(loggingEvent1.getMDCPropertyMap()) + .containsEntry("rpcName", "google.showcase.v1beta1.Echo/Echo"); + assertThat(TestAppender.events.get(1).getMessage()).isEqualTo("received HTTP response"); + // assertThat(TestAppender.events.get(1).getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); + } finally { + logger.setLevel(originalLevel); + } + } + + private String echoGrpc(String value) { + EchoResponse response = grpcClient.echo(EchoRequest.newBuilder().setContent(value).build()); + return response.getContent(); + } + + private String echoHttpJson(String value) { + EchoResponse response = httpjsonClient.echo(EchoRequest.newBuilder().setContent(value).build()); + return response.getContent(); + } +} diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/util/TestAppender.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/util/TestAppender.java new file mode 100644 index 0000000000..4ed5439fc7 --- /dev/null +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/util/TestAppender.java @@ -0,0 +1,40 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.showcase.v1beta1.it.util; + + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import java.util.ArrayList; +import java.util.List; + +/** Logback appender used to set up tests. */ +public class TestAppender extends AppenderBase { + public static List events = new ArrayList<>(); + + @Override + protected void append(ILoggingEvent eventObject) { + // triggering Logback to capture the current MDC context and store it with the log event + eventObject.getMDCPropertyMap(); + + events.add(eventObject); + } + public static void clearEvents() { + events.clear(); + } +} + diff --git a/showcase/gapic-showcase/src/test/resources/logback-test.xml b/showcase/gapic-showcase/src/test/resources/logback-test.xml index 2fabc5467a..c38d09e794 100644 --- a/showcase/gapic-showcase/src/test/resources/logback-test.xml +++ b/showcase/gapic-showcase/src/test/resources/logback-test.xml @@ -1,29 +1,43 @@ - - - - - severity - - - - - - ((logger.equals("com.google.api.gax.grpc.GrpcLoggingInterceptor") || - logger.equals("com.google.api.gax.httpjson.HttpJsonLoggingInterceptor") || - logger.equals("com.google.api.gax.tracing.MetricsTracer")) - && (mdc.get("serviceName").equals("google.showcase.v1beta1.Echo"))) - + + + + + + + + + + + + + + + + + + + + + + + - + - - ACCEPT - DENY - - + + + + + - - - - \ No newline at end of file + + + + + + + + + + \ No newline at end of file From 23bc1110864fa61fc83f0a0b5bb068879a4ab075 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Fri, 6 Dec 2024 17:54:24 -0500 Subject: [PATCH 21/92] refactor HttpJsonLoggingInterceptor. remove public modifier from logging interceptors. --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 13 +- .../httpjson/HttpJsonLoggingInterceptor.java | 163 +++++++++++------- .../com/google/api/gax/logging/LogData.java | 16 ++ .../google/showcase/v1beta1/it/ITLogging.java | 127 ++++++++------ 4 files changed, 191 insertions(+), 128 deletions(-) diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index 5fceaf81b2..ccced9a7d6 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -48,7 +48,7 @@ import org.slf4j.Logger; import org.slf4j.event.Level; -public class GrpcLoggingInterceptor implements ClientInterceptor { +class GrpcLoggingInterceptor implements ClientInterceptor { private static final Logger logger = LoggingUtils.getLogger(GrpcLoggingInterceptor.class); private static final Gson gson = new Gson(); @@ -59,7 +59,6 @@ public ClientCall interceptCall( return new ForwardingClientCall.SimpleForwardingClientCall( next.newCall(method, callOptions)) { - // Generate request ID here String requestId = UUID.randomUUID().toString(); @Override @@ -84,7 +83,7 @@ public void onMessage(RespT message) { @Override public void onClose(Status status, Metadata trailers) { try { - logResponse(status, logDataBuilder, requestId); + logResponse(status.getCode().value(), logDataBuilder, requestId); } finally { logDataBuilder = null; // release resource } @@ -102,7 +101,8 @@ public void sendMessage(ReqT message) { }; } - // --- Helper methods for logging --- + // Helper methods for logging + // some duplications with http equivalent to avoid exposing as public method private void logRequestInfoAndHeaders( MethodDescriptor method, Metadata headers, String requestId) { try { @@ -141,10 +141,11 @@ private void recordResponsePayload(RespT message, LogData.Builder logDat } } - private void logResponse(Status status, LogData.Builder logDataBuilder, String requestId) { + private void logResponse(int statusCode, LogData.Builder logDataBuilder, String requestId) { try { + if (logger.isInfoEnabled()) { - logDataBuilder.responseStatus(status.getCode().name()).requestId(requestId); + logDataBuilder.responseStatus(String.valueOf(statusCode)).requestId(requestId); } if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { Map responseData = logDataBuilder.build().toMap(); diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index 43df5775d0..21481ff7b9 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -32,9 +32,9 @@ import com.google.api.gax.httpjson.ForwardingHttpJsonClientCall.SimpleForwardingHttpJsonClientCall; import com.google.api.gax.httpjson.ForwardingHttpJsonClientCallListener.SimpleForwardingHttpJsonClientCallListener; +import com.google.api.gax.logging.LogData; import com.google.api.gax.logging.LoggingUtils; import com.google.gson.Gson; -import com.google.gson.JsonArray; import com.google.gson.JsonObject; import java.util.HashMap; import java.util.List; @@ -43,7 +43,7 @@ import org.slf4j.Logger; import org.slf4j.event.Level; -public class HttpJsonLoggingInterceptor implements HttpJsonClientInterceptor { +class HttpJsonLoggingInterceptor implements HttpJsonClientInterceptor { private static final Logger logger = LoggingUtils.getLogger(HttpJsonLoggingInterceptor.class); private static final Gson gson = new Gson(); @@ -53,104 +53,135 @@ public HttpJsonClientCall interceptCall( ApiMethodDescriptor method, HttpJsonCallOptions callOptions, HttpJsonChannel next) { - Map requestLogData = new HashMap<>(); - // Initialize a JsonArray to hold all responses - JsonArray responsePayloads = new JsonArray(); + String requestId = UUID.randomUUID().toString(); String endpoint = ((ManagedHttpJsonChannel) next).getEndpoint(); + return new SimpleForwardingHttpJsonClientCall(next.newCall(method, callOptions)) { @Override public void start( HttpJsonClientCall.Listener responseListener, HttpJsonMetadata headers) { - Map serviceAndRpc = new HashMap<>(); - if (logger.isInfoEnabled()) { - String requestId = UUID.randomUUID().toString(); - // Capture request details - String methodName = method.getFullMethodName(); - String httpMethod = method.getHttpMethod(); - serviceAndRpc.put("rpcName", methodName); - serviceAndRpc.put("requestId", requestId); - requestLogData.putAll(serviceAndRpc); - requestLogData.put("request.url", endpoint); - requestLogData.put("request.method", httpMethod); - } - if (logger.isDebugEnabled()) { - // Capture and log headers - JsonObject jsonHeaders = new JsonObject(); - headers - .getHeaders() - .forEach((key, value) -> jsonHeaders.addProperty(key, value.toString())); - requestLogData.put("request.headers", gson.toJson(jsonHeaders)); - } + logRequestInfoAndHeaders(method, headers, endpoint, requestId); - if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { - LoggingUtils.logWithMDC(logger, Level.INFO, serviceAndRpc, "Sending HTTP request"); - } - Map responseLogData = new HashMap<>(); Listener forwardingResponseListener = new SimpleForwardingHttpJsonClientCallListener(responseListener) { + LogData.Builder logDataBuilder = LogData.builder(); + @Override public void onHeaders(HttpJsonMetadata responseHeaders) { - - if (logger.isDebugEnabled()) { - - Map> map = new HashMap<>(); - responseHeaders - .getHeaders() - .forEach((key, value) -> map.put(key, (List) value)); - responseLogData.put("response.headers", gson.toJson(map)); - } + recordResponseHeaders(responseHeaders, logDataBuilder); super.onHeaders(responseHeaders); } @Override public void onMessage(RespT message) { - - if (logger.isDebugEnabled()) { - // Add each message to the array - responsePayloads.add(gson.toJsonTree(message)); - } + recordResponsePayload(message, logDataBuilder); super.onMessage(message); } @Override public void onClose(int statusCode, HttpJsonMetadata trailers) { - - if (logger.isInfoEnabled()) { - - serviceAndRpc.put("response.status", String.valueOf(statusCode)); - responseLogData.putAll(serviceAndRpc); - } - if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { - LoggingUtils.logWithMDC( - logger, Level.INFO, serviceAndRpc, "received HTTP response"); - } - if (logger.isDebugEnabled()) { - // Add the array of payloads to the responseLogData - responseLogData.put("response.payload", gson.toJson(responsePayloads)); - LoggingUtils.logWithMDC( - logger, - Level.DEBUG, - responseLogData, - "Received response header and payload."); + try { + logResponse(statusCode, logDataBuilder, requestId); + } finally { + logDataBuilder = null; // release resource } super.onClose(statusCode, trailers); } }; - super.start(forwardingResponseListener, headers); } @Override public void sendMessage(ReqT message) { + logRequestDetails(message, requestId); + super.sendMessage(message); + } + }; + } + + // Helper methods for logging, + // some duplications with grpc equivalent to avoid exposing as public method + private void logRequestInfoAndHeaders( + ApiMethodDescriptor method, + HttpJsonMetadata headers, + String endpoint, + String requestId) { + try { + if (logger.isInfoEnabled()) { + LogData.Builder logDataBuilder = LogData.builder(); + logDataBuilder + .rpcName(method.getFullMethodName()) + .httpMethod(method.getHttpMethod()) + .httpUrl(endpoint) + .requestId(requestId); + if (logger.isDebugEnabled()) { - requestLogData.put("request.payload", gson.toJson(message)); + JsonObject requestHeaders = new JsonObject(); + headers + .getHeaders() + .forEach((key, value) -> requestHeaders.addProperty(key, value.toString())); + logDataBuilder.requestHeaders(gson.toJson(requestHeaders)); + LoggingUtils.logWithMDC( + logger, Level.DEBUG, logDataBuilder.build().toMap(), "Sending HTTP request"); + } else { LoggingUtils.logWithMDC( - logger, Level.DEBUG, requestLogData, "HTTP request header and payload."); + logger, Level.INFO, logDataBuilder.build().toMap(), "Sending HTTP request"); } - super.sendMessage(message); } - }; + } catch (Exception e) { + logger.error("Error logging request info (and headers)", e); + } + } + + private void recordResponseHeaders( + HttpJsonMetadata responseHeaders, LogData.Builder logDataBuilder) { + + if (logger.isDebugEnabled()) { + + Map> map = new HashMap<>(); + responseHeaders.getHeaders().forEach((key, value) -> map.put(key, (List) value)); + logDataBuilder.responseHeaders(gson.toJson(map)); + } + } + + private void recordResponsePayload(RespT message, LogData.Builder logDataBuilder) { + if (logger.isDebugEnabled()) { + logDataBuilder.responsePayload(gson.toJsonTree(message)); + } + } + + private void logResponse(int statusCode, LogData.Builder logDataBuilder, String requestId) { + try { + + if (logger.isInfoEnabled()) { + logDataBuilder.responseStatus(String.valueOf(statusCode)).requestId(requestId); + } + if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { + Map responseData = logDataBuilder.build().toMap(); + LoggingUtils.logWithMDC(logger, Level.INFO, responseData, "Received HTTP response"); + } + if (logger.isDebugEnabled()) { + Map responsedDetailsMap = logDataBuilder.build().toMap(); + LoggingUtils.logWithMDC(logger, Level.DEBUG, responsedDetailsMap, "Received HTTP response"); + } + } catch (Exception e) { + logger.error("Error logging request response", e); + } + } + + private void logRequestDetails(RespT message, String requestId) { + try { + if (logger.isDebugEnabled()) { + LogData.Builder logDataBuilder = LogData.builder(); + logDataBuilder.requestPayload(gson.toJson(message)).requestId(requestId); + Map requestDetailsMap = logDataBuilder.build().toMap(); + LoggingUtils.logWithMDC( + logger, Level.DEBUG, requestDetailsMap, "Sending HTTP request: request payload"); + } + } catch (Exception e) { + logger.error("Error logging request details", e); + } } } diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java index 67ceb8057e..87768c0ccf 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java @@ -67,6 +67,12 @@ public abstract class LogData { @Nullable public abstract JsonElement responsePayload(); + @Nullable + public abstract String httpMethod(); + + @Nullable + public abstract String httpUrl(); + public static Builder builder() { return new AutoValue_LogData.Builder(); } @@ -89,6 +95,10 @@ public abstract static class Builder { public abstract Builder responsePayload(JsonElement responsePayload); + public abstract Builder httpMethod(String httpMethod); + + public abstract Builder httpUrl(String httpUrl); + public abstract LogData build(); } @@ -120,6 +130,12 @@ public Map toMap() { if (responsePayload() != null) { map.put("response.payload", gson.toJson(responsePayload())); } + if (httpMethod() != null) { + map.put("request.method", httpMethod()); + } + if (httpUrl() != null) { + map.put("request.url", httpUrl()); + } return map; } } diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLogging.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLogging.java index 41d4993dec..a0fb67e2fe 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLogging.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLogging.java @@ -19,9 +19,6 @@ import static com.google.common.truth.Truth.assertThat; import ch.qos.logback.classic.spi.ILoggingEvent; -import com.google.api.gax.grpc.GrpcLoggingInterceptor; -import com.google.api.gax.httpjson.HttpJsonLoggingInterceptor; -import com.google.api.gax.logging.LoggingUtils; import com.google.showcase.v1beta1.EchoClient; import com.google.showcase.v1beta1.EchoRequest; import com.google.showcase.v1beta1.EchoResponse; @@ -48,9 +45,6 @@ static void createClients() throws Exception { // Create Http JSON Echo Client httpjsonClient = TestClientInitializer.createHttpJsonEchoClient(); - // EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class); - // Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("true"); - // LoggingUtils.setEnvironmentProvider(envProvider); LOGGER.log(Level.INFO, "This is log message directly from JUL. Clients created."); } @@ -80,60 +74,81 @@ void testGrpc_receiveContent_logDebug() { assertThat(TestAppender.events.get(2).getMessage()).isEqualTo("Received Grpc response"); } - @Test - void testGrpc_receiveContent_logInfo() { - ch.qos.logback.classic.Logger logger = - (ch.qos.logback.classic.Logger) LoggingUtils.getLogger(GrpcLoggingInterceptor.class); - ch.qos.logback.classic.Level originalLevel = logger.getLevel(); - try { - logger.setLevel(ch.qos.logback.classic.Level.INFO); - assertThat(logger.getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); - - TestAppender.clearEvents(); - assertThat(echoGrpc("grpc-echo?")).isEqualTo("grpc-echo?"); - assertThat(TestAppender.events.size()).isEqualTo(2); - ILoggingEvent loggingEvent1 = TestAppender.events.get(0); - assertThat(loggingEvent1.getMessage()).isEqualTo("Sending gRPC request"); - assertThat(loggingEvent1.getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); - assertThat(loggingEvent1.getMDCPropertyMap()).hasSize(3); - assertThat(loggingEvent1.getMDCPropertyMap()) - .containsEntry("serviceName", "google.showcase.v1beta1.Echo"); - assertThat(loggingEvent1.getMDCPropertyMap()) - .containsEntry("rpcName", "google.showcase.v1beta1.Echo/Echo"); - assertThat(TestAppender.events.get(1).getMessage()).isEqualTo("Received Grpc response"); - assertThat(TestAppender.events.get(1).getLevel()) - .isEqualTo(ch.qos.logback.classic.Level.INFO); - } finally { - logger.setLevel(originalLevel); - } - } + // @Test + // void testGrpc_receiveContent_logInfo() { + // ch.qos.logback.classic.Logger logger = + // (ch.qos.logback.classic.Logger) LoggingUtils.getLogger(GrpcLoggingInterceptor.class); + // ch.qos.logback.classic.Level originalLevel = logger.getLevel(); + // try { + // logger.setLevel(ch.qos.logback.classic.Level.INFO); + // assertThat(logger.getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); + // + // TestAppender.clearEvents(); + // assertThat(echoGrpc("grpc-echo?")).isEqualTo("grpc-echo?"); + // assertThat(TestAppender.events.size()).isEqualTo(2); + // ILoggingEvent loggingEvent1 = TestAppender.events.get(0); + // assertThat(loggingEvent1.getMessage()).isEqualTo("Sending gRPC request"); + // assertThat(loggingEvent1.getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); + // assertThat(loggingEvent1.getMDCPropertyMap()).hasSize(3); + // assertThat(loggingEvent1.getMDCPropertyMap()) + // .containsEntry("serviceName", "google.showcase.v1beta1.Echo"); + // assertThat(loggingEvent1.getMDCPropertyMap()) + // .containsEntry("rpcName", "google.showcase.v1beta1.Echo/Echo"); + // assertThat(TestAppender.events.get(1).getMessage()).isEqualTo("Received Grpc response"); + // assertThat(TestAppender.events.get(1).getLevel()) + // .isEqualTo(ch.qos.logback.classic.Level.INFO); + // } finally { + // logger.setLevel(originalLevel); + // } + // } @Test - void testHttpJson_receiveContent() { - - ch.qos.logback.classic.Logger logger = - (ch.qos.logback.classic.Logger) LoggingUtils.getLogger(HttpJsonLoggingInterceptor.class); - ch.qos.logback.classic.Level originalLevel = logger.getLevel(); - try { - logger.setLevel(ch.qos.logback.classic.Level.INFO); - assertThat(logger.getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); - - TestAppender.clearEvents(); - assertThat(echoHttpJson("http-echo?")).isEqualTo("http-echo?"); - assertThat(TestAppender.events.size()).isEqualTo(2); - ILoggingEvent loggingEvent1 = TestAppender.events.get(0); - assertThat(loggingEvent1.getMessage()).isEqualTo("Sending HTTP request"); - assertThat(loggingEvent1.getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); - assertThat(loggingEvent1.getMDCPropertyMap()).hasSize(2); - assertThat(loggingEvent1.getMDCPropertyMap()) - .containsEntry("rpcName", "google.showcase.v1beta1.Echo/Echo"); - assertThat(TestAppender.events.get(1).getMessage()).isEqualTo("received HTTP response"); - // assertThat(TestAppender.events.get(1).getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); - } finally { - logger.setLevel(originalLevel); - } + void testHttpJson_receiveContent_logDebug() { + TestAppender.clearEvents(); + assertThat(echoHttpJson("http-echo?")).isEqualTo("http-echo?"); + assertThat(TestAppender.events.size()).isEqualTo(3); + ILoggingEvent loggingEvent1 = TestAppender.events.get(0); + assertThat(loggingEvent1.getMessage()).isEqualTo("Sending HTTP request"); + assertThat(loggingEvent1.getLevel()).isEqualTo(ch.qos.logback.classic.Level.DEBUG); + assertThat(loggingEvent1.getMDCPropertyMap()).hasSize(5); + assertThat(loggingEvent1.getMDCPropertyMap()).containsKey("request.headers"); + assertThat(TestAppender.events.get(1).getMessage()) + .isEqualTo("Sending HTTP request: request payload"); + assertThat(TestAppender.events.get(2).getMessage()).isEqualTo("Received HTTP response"); } + // @Test + // void testHttpJson_receiveContent_logInfo() { + // + // ch.qos.logback.classic.Logger logger = + // (ch.qos.logback.classic.Logger) LoggingUtils.getLogger(HttpJsonLoggingInterceptor.class); + // ch.qos.logback.classic.Level originalLevel = logger.getLevel(); + // try { + // logger.setLevel(ch.qos.logback.classic.Level.INFO); + // assertThat(logger.getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); + // + // TestAppender.clearEvents(); + // assertThat(echoHttpJson("http-echo?")).isEqualTo("http-echo?"); + // assertThat(TestAppender.events.size()).isEqualTo(2); + // ILoggingEvent loggingEvent1 = TestAppender.events.get(0); + // assertThat(loggingEvent1.getMessage()).isEqualTo("Sending HTTP request"); + // assertThat(loggingEvent1.getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); + // assertThat(loggingEvent1.getMDCPropertyMap()).hasSize(4); + // assertThat(loggingEvent1.getMDCPropertyMap()) + // .containsEntry("rpcName", "google.showcase.v1beta1.Echo/Echo"); + // assertThat(loggingEvent1.getMDCPropertyMap()).containsEntry("request.method", "POST"); + // assertThat(loggingEvent1.getMDCPropertyMap()) + // .containsEntry("request.url", "http://localhost:7469"); + // assertThat(TestAppender.events.get(1).getMessage()).isEqualTo("Received HTTP response"); + // assertThat(TestAppender.events.get(1).getLevel()) + // .isEqualTo(ch.qos.logback.classic.Level.INFO); + // assertThat(TestAppender.events.get(1).getMDCPropertyMap()) + // .containsEntry("response.status", "200"); + // } finally { + // logger.setLevel(originalLevel); + // } + // } + private String echoGrpc(String value) { EchoResponse response = grpcClient.echo(EchoRequest.newBuilder().setContent(value).build()); return response.getContent(); From bbe0700ff751f7e721326794a6ee2102f3855a00 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Sat, 7 Dec 2024 21:41:28 -0500 Subject: [PATCH 22/92] add GrpcLoggingInterceptorTest and test interceptor structure. --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 13 +- .../gax/grpc/GrpcLoggingInterceptorTest.java | 116 ++++++++++++++++++ 2 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index ccced9a7d6..44de135a43 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -53,6 +53,8 @@ class GrpcLoggingInterceptor implements ClientInterceptor { private static final Logger logger = LoggingUtils.getLogger(GrpcLoggingInterceptor.class); private static final Gson gson = new Gson(); + ClientCall.Listener currentListener; + @Override public ClientCall interceptCall( MethodDescriptor method, CallOptions callOptions, Channel next) { @@ -90,6 +92,7 @@ public void onClose(Status status, Metadata trailers) { super.onClose(status, trailers); } }; + currentListener = responseLoggingListener; super.start(responseLoggingListener, headers); } @@ -103,7 +106,7 @@ public void sendMessage(ReqT message) { // Helper methods for logging // some duplications with http equivalent to avoid exposing as public method - private void logRequestInfoAndHeaders( + void logRequestInfoAndHeaders( MethodDescriptor method, Metadata headers, String requestId) { try { if (logger.isInfoEnabled()) { @@ -128,20 +131,20 @@ private void logRequestInfoAndHeaders( } } - private void recordResponseHeaders(Metadata headers, LogData.Builder logDataBuilder) { + void recordResponseHeaders(Metadata headers, LogData.Builder logDataBuilder) { if (logger.isDebugEnabled()) { JsonObject responseHeaders = mapHeadersToJsonObject(headers); logDataBuilder.responseHeaders(gson.toJson(responseHeaders)); } } - private void recordResponsePayload(RespT message, LogData.Builder logDataBuilder) { + void recordResponsePayload(RespT message, LogData.Builder logDataBuilder) { if (logger.isDebugEnabled()) { logDataBuilder.responsePayload(gson.toJsonTree(message)); } } - private void logResponse(int statusCode, LogData.Builder logDataBuilder, String requestId) { + void logResponse(int statusCode, LogData.Builder logDataBuilder, String requestId) { try { if (logger.isInfoEnabled()) { @@ -160,7 +163,7 @@ private void logResponse(int statusCode, LogData.Builder logDataBuilder, String } } - private void logRequestDetails(RespT message, String requestId) { + void logRequestDetails(RespT message, String requestId) { try { if (logger.isDebugEnabled()) { LogData.Builder logDataBuilder = LogData.builder(); diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java new file mode 100644 index 0000000000..3729d2931d --- /dev/null +++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java @@ -0,0 +1,116 @@ +/* + * Copyright 2024 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.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +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 com.google.api.gax.logging.LogData; +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.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +class GrpcLoggingInterceptorTest { + @Mock private Channel channel; + + @Mock private ClientCall call; + + private static final MethodDescriptor method = FakeMethodDescriptor.create(); + + /** Sets up mocks. */ + @BeforeEach + void setUp() { + MockitoAnnotations.initMocks(this); + when(channel.newCall(Mockito.>any(), any(CallOptions.class))) + .thenReturn(call); + } + + @Test + void testInterceptor_basic() { + GrpcLoggingInterceptor interceptor = new GrpcLoggingInterceptor(); + Channel intercepted = ClientInterceptors.intercept(channel, interceptor); + @SuppressWarnings("unchecked") + ClientCall.Listener listener = mock(ClientCall.Listener.class); + ClientCall 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() { + GrpcLoggingInterceptor interceptor = spy(new GrpcLoggingInterceptor()); + Channel intercepted = ClientInterceptors.intercept(channel, interceptor); + @SuppressWarnings("unchecked") + ClientCall.Listener listener = mock(ClientCall.Listener.class); + ClientCall 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()); + + // --- Verify that the response listener's methods were called --- + verify(interceptor).recordResponseHeaders(eq(responseHeaders), any(LogData.Builder.class)); + verify(interceptor).recordResponsePayload(any(), any(LogData.Builder.class)); + verify(interceptor) + .logResponse(eq(status.getCode().value()), any(LogData.Builder.class), anyString()); + } +} From 3d7c7f9962ad703678f929dd4bec8ad69eab12bd Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Mon, 9 Dec 2024 22:37:16 -0500 Subject: [PATCH 23/92] fix status code '0'. --- .../com/google/api/gax/grpc/GrpcLoggingInterceptor.java | 6 +++--- .../com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index 44de135a43..e79639bf28 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -53,7 +53,7 @@ class GrpcLoggingInterceptor implements ClientInterceptor { private static final Logger logger = LoggingUtils.getLogger(GrpcLoggingInterceptor.class); private static final Gson gson = new Gson(); - ClientCall.Listener currentListener; + ClientCall.Listener currentListener; // expose for test setup @Override public ClientCall interceptCall( @@ -85,7 +85,7 @@ public void onMessage(RespT message) { @Override public void onClose(Status status, Metadata trailers) { try { - logResponse(status.getCode().value(), logDataBuilder, requestId); + logResponse(status.getCode().toString(), logDataBuilder, requestId); } finally { logDataBuilder = null; // release resource } @@ -144,7 +144,7 @@ void recordResponsePayload(RespT message, LogData.Builder logDataBuilder } } - void logResponse(int statusCode, LogData.Builder logDataBuilder, String requestId) { + void logResponse(String statusCode, LogData.Builder logDataBuilder, String requestId) { try { if (logger.isInfoEnabled()) { diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java index 3729d2931d..eeeab58d55 100644 --- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java +++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java @@ -111,6 +111,6 @@ void testInterceptor_responseListener() { verify(interceptor).recordResponseHeaders(eq(responseHeaders), any(LogData.Builder.class)); verify(interceptor).recordResponsePayload(any(), any(LogData.Builder.class)); verify(interceptor) - .logResponse(eq(status.getCode().value()), any(LogData.Builder.class), anyString()); + .logResponse(eq(status.getCode().toString()), any(LogData.Builder.class), anyString()); } } From b870d81a83a597af43b33907bcc48b834187383a Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Mon, 9 Dec 2024 23:18:01 -0500 Subject: [PATCH 24/92] merge logs in interceptors, record log data. remove request id. --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 61 ++++++++--------- .../gax/grpc/GrpcLoggingInterceptorTest.java | 4 +- .../httpjson/HttpJsonLoggingInterceptor.java | 68 ++++++++++--------- .../HttpJsonLoggingInterceptorTest.java | 43 ++++++++++++ .../com/google/api/gax/logging/LogData.java | 49 +++++++++++++ 5 files changed, 158 insertions(+), 67 deletions(-) create mode 100644 gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index e79639bf28..f96a873312 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -44,7 +44,6 @@ import io.grpc.MethodDescriptor; import io.grpc.Status; import java.util.Map; -import java.util.UUID; import org.slf4j.Logger; import org.slf4j.event.Level; @@ -58,18 +57,17 @@ class GrpcLoggingInterceptor implements ClientInterceptor { @Override public ClientCall interceptCall( MethodDescriptor method, CallOptions callOptions, Channel next) { + return new ForwardingClientCall.SimpleForwardingClientCall( next.newCall(method, callOptions)) { - - String requestId = UUID.randomUUID().toString(); + LogData.Builder logDataBuilder = LogData.builder(); @Override public void start(Listener responseListener, Metadata headers) { - logRequestInfoAndHeaders(method, headers, requestId); + logRequestInfo(method, logDataBuilder); + recordRequestHeaders(headers, logDataBuilder); SimpleForwardingClientCallListener responseLoggingListener = new SimpleForwardingClientCallListener(responseListener) { - LogData.Builder logDataBuilder = LogData.builder(); - @Override public void onHeaders(Metadata headers) { recordResponseHeaders(headers, logDataBuilder); @@ -85,7 +83,7 @@ public void onMessage(RespT message) { @Override public void onClose(Status status, Metadata trailers) { try { - logResponse(status.getCode().toString(), logDataBuilder, requestId); + logResponse(status.getCode().toString(), logDataBuilder); } finally { logDataBuilder = null; // release resource } @@ -98,7 +96,7 @@ public void onClose(Status status, Metadata trailers) { @Override public void sendMessage(ReqT message) { - logRequestDetails(message, requestId); + logRequestDetails(message, logDataBuilder); super.sendMessage(message); } }; @@ -106,24 +104,15 @@ public void sendMessage(ReqT message) { // Helper methods for logging // some duplications with http equivalent to avoid exposing as public method - void logRequestInfoAndHeaders( - MethodDescriptor method, Metadata headers, String requestId) { + void logRequestInfo( + MethodDescriptor method, LogData.Builder logDataBuilder) { try { if (logger.isInfoEnabled()) { - LogData.Builder logDataBuilder = LogData.builder(); - logDataBuilder - .serviceName(method.getServiceName()) - .rpcName(method.getFullMethodName()) - .requestId(requestId); - - if (logger.isDebugEnabled()) { - JsonObject requestHeaders = mapHeadersToJsonObject(headers); - logDataBuilder.requestHeaders(gson.toJson(requestHeaders)); - LoggingUtils.logWithMDC( - logger, Level.DEBUG, logDataBuilder.build().toMap(), "Sending gRPC request"); - } else { + logDataBuilder.serviceName(method.getServiceName()).rpcName(method.getFullMethodName()); + + if (!logger.isDebugEnabled()) { LoggingUtils.logWithMDC( - logger, Level.INFO, logDataBuilder.build().toMap(), "Sending gRPC request"); + logger, Level.INFO, logDataBuilder.build().toMapRequest(), "Sending gRPC request"); } } } catch (Exception e) { @@ -131,6 +120,17 @@ void logRequestInfoAndHeaders( } } + private void recordRequestHeaders(Metadata headers, LogData.Builder logDataBuilder) { + try { + if (logger.isDebugEnabled()) { + JsonObject requestHeaders = mapHeadersToJsonObject(headers); + logDataBuilder.requestHeaders(gson.toJson(requestHeaders)); + } + } catch (Exception e) { + logger.error("Error recording request headers", e); + } + } + void recordResponseHeaders(Metadata headers, LogData.Builder logDataBuilder) { if (logger.isDebugEnabled()) { JsonObject responseHeaders = mapHeadersToJsonObject(headers); @@ -144,18 +144,18 @@ void recordResponsePayload(RespT message, LogData.Builder logDataBuilder } } - void logResponse(String statusCode, LogData.Builder logDataBuilder, String requestId) { + void logResponse(String statusCode, LogData.Builder logDataBuilder) { try { if (logger.isInfoEnabled()) { - logDataBuilder.responseStatus(String.valueOf(statusCode)).requestId(requestId); + logDataBuilder.responseStatus(statusCode); } if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { - Map responseData = logDataBuilder.build().toMap(); + Map responseData = logDataBuilder.build().toMapResponse(); LoggingUtils.logWithMDC(logger, Level.INFO, responseData, "Received Grpc response"); } if (logger.isDebugEnabled()) { - Map responsedDetailsMap = logDataBuilder.build().toMap(); + Map responsedDetailsMap = logDataBuilder.build().toMapResponse(); LoggingUtils.logWithMDC(logger, Level.DEBUG, responsedDetailsMap, "Received Grpc response"); } } catch (Exception e) { @@ -163,12 +163,11 @@ void logResponse(String statusCode, LogData.Builder logDataBuilder, String reque } } - void logRequestDetails(RespT message, String requestId) { + void logRequestDetails(RespT message, LogData.Builder logDataBuilder) { try { if (logger.isDebugEnabled()) { - LogData.Builder logDataBuilder = LogData.builder(); - logDataBuilder.requestPayload(gson.toJson(message)).requestId(requestId); - Map requestDetailsMap = logDataBuilder.build().toMap(); + logDataBuilder.requestPayload(gson.toJson(message)); + Map requestDetailsMap = logDataBuilder.build().toMapRequest(); LoggingUtils.logWithMDC( logger, Level.DEBUG, requestDetailsMap, "Sending gRPC request: request payload"); } diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java index eeeab58d55..d3fe5e5881 100644 --- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java +++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java @@ -31,7 +31,6 @@ package com.google.api.gax.grpc; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -110,7 +109,6 @@ void testInterceptor_responseListener() { // --- Verify that the response listener's methods were called --- verify(interceptor).recordResponseHeaders(eq(responseHeaders), any(LogData.Builder.class)); verify(interceptor).recordResponsePayload(any(), any(LogData.Builder.class)); - verify(interceptor) - .logResponse(eq(status.getCode().toString()), any(LogData.Builder.class), anyString()); + verify(interceptor).logResponse(eq(status.getCode().toString()), any(LogData.Builder.class)); } } diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index 21481ff7b9..fcaeff1fc6 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -39,7 +39,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.UUID; import org.slf4j.Logger; import org.slf4j.event.Level; @@ -54,19 +53,21 @@ public HttpJsonClientCall interceptCall( HttpJsonCallOptions callOptions, HttpJsonChannel next) { - String requestId = UUID.randomUUID().toString(); String endpoint = ((ManagedHttpJsonChannel) next).getEndpoint(); return new SimpleForwardingHttpJsonClientCall(next.newCall(method, callOptions)) { + + LogData.Builder logDataBuilder = LogData.builder(); + @Override public void start( HttpJsonClientCall.Listener responseListener, HttpJsonMetadata headers) { - logRequestInfoAndHeaders(method, headers, endpoint, requestId); + logRequestInfo(method, endpoint, logDataBuilder); + recordRequestHeaders(headers, logDataBuilder); Listener forwardingResponseListener = new SimpleForwardingHttpJsonClientCallListener(responseListener) { - LogData.Builder logDataBuilder = LogData.builder(); @Override public void onHeaders(HttpJsonMetadata responseHeaders) { @@ -83,7 +84,7 @@ public void onMessage(RespT message) { @Override public void onClose(int statusCode, HttpJsonMetadata trailers) { try { - logResponse(statusCode, logDataBuilder, requestId); + logResponse(statusCode, logDataBuilder); } finally { logDataBuilder = null; // release resource } @@ -95,7 +96,7 @@ public void onClose(int statusCode, HttpJsonMetadata trailers) { @Override public void sendMessage(ReqT message) { - logRequestDetails(message, requestId); + logRequestDetails(message, logDataBuilder); super.sendMessage(message); } }; @@ -103,31 +104,18 @@ public void sendMessage(ReqT message) { // Helper methods for logging, // some duplications with grpc equivalent to avoid exposing as public method - private void logRequestInfoAndHeaders( - ApiMethodDescriptor method, - HttpJsonMetadata headers, - String endpoint, - String requestId) { + private void logRequestInfo( + ApiMethodDescriptor method, String endpoint, LogData.Builder logDataBuilder) { try { if (logger.isInfoEnabled()) { - LogData.Builder logDataBuilder = LogData.builder(); logDataBuilder .rpcName(method.getFullMethodName()) .httpMethod(method.getHttpMethod()) - .httpUrl(endpoint) - .requestId(requestId); - - if (logger.isDebugEnabled()) { - JsonObject requestHeaders = new JsonObject(); - headers - .getHeaders() - .forEach((key, value) -> requestHeaders.addProperty(key, value.toString())); - logDataBuilder.requestHeaders(gson.toJson(requestHeaders)); - LoggingUtils.logWithMDC( - logger, Level.DEBUG, logDataBuilder.build().toMap(), "Sending HTTP request"); - } else { + .httpUrl(endpoint); + + if (!logger.isDebugEnabled()) { LoggingUtils.logWithMDC( - logger, Level.INFO, logDataBuilder.build().toMap(), "Sending HTTP request"); + logger, Level.INFO, logDataBuilder.build().toMapRequest(), "Sending HTTP request"); } } } catch (Exception e) { @@ -135,6 +123,21 @@ private void logRequestInfoAndHeaders( } } + private void recordRequestHeaders(HttpJsonMetadata headers, LogData.Builder logDataBuilder) { + try { + if (logger.isDebugEnabled()) { + JsonObject requestHeaders = new JsonObject(); + headers + .getHeaders() + .forEach((key, value) -> requestHeaders.addProperty(key, value.toString())); + logDataBuilder.requestHeaders(gson.toJson(requestHeaders)); + logDataBuilder.requestHeaders(gson.toJson(requestHeaders)); + } + } catch (Exception e) { + logger.error("Error recording request headers", e); + } + } + private void recordResponseHeaders( HttpJsonMetadata responseHeaders, LogData.Builder logDataBuilder) { @@ -152,18 +155,18 @@ private void recordResponsePayload(RespT message, LogData.Builder logDat } } - private void logResponse(int statusCode, LogData.Builder logDataBuilder, String requestId) { + private void logResponse(int statusCode, LogData.Builder logDataBuilder) { try { if (logger.isInfoEnabled()) { - logDataBuilder.responseStatus(String.valueOf(statusCode)).requestId(requestId); + logDataBuilder.responseStatus(String.valueOf(statusCode)); } if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { - Map responseData = logDataBuilder.build().toMap(); + Map responseData = logDataBuilder.build().toMapResponse(); LoggingUtils.logWithMDC(logger, Level.INFO, responseData, "Received HTTP response"); } if (logger.isDebugEnabled()) { - Map responsedDetailsMap = logDataBuilder.build().toMap(); + Map responsedDetailsMap = logDataBuilder.build().toMapResponse(); LoggingUtils.logWithMDC(logger, Level.DEBUG, responsedDetailsMap, "Received HTTP response"); } } catch (Exception e) { @@ -171,12 +174,11 @@ private void logResponse(int statusCode, LogData.Builder logDataBuilder, String } } - private void logRequestDetails(RespT message, String requestId) { + private void logRequestDetails(RespT message, LogData.Builder logDataBuilder) { try { if (logger.isDebugEnabled()) { - LogData.Builder logDataBuilder = LogData.builder(); - logDataBuilder.requestPayload(gson.toJson(message)).requestId(requestId); - Map requestDetailsMap = logDataBuilder.build().toMap(); + logDataBuilder.requestPayload(gson.toJson(message)); + Map requestDetailsMap = logDataBuilder.build().toMapRequest(); LoggingUtils.logWithMDC( logger, Level.DEBUG, requestDetailsMap, "Sending HTTP request: request payload"); } diff --git a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java new file mode 100644 index 0000000000..e8ca5f0c6f --- /dev/null +++ b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java @@ -0,0 +1,43 @@ +package com.google.api.gax.httpjson; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.api.gax.httpjson.ApiMethodDescriptor.MethodType; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +class HttpJsonLoggingInterceptorTest { + + @Mock private HttpJsonChannel channel; + + @Mock private HttpJsonClientCall call; + + private static final ApiMethodDescriptor method = + ApiMethodDescriptor.newBuilder() + .setType(MethodType.UNARY) + .setRequestFormatter(mock(HttpRequestFormatter.class)) + .setRequestFormatter(mock(HttpRequestFormatter.class)) + .setFullMethodName("FakeClient/fake-method") + .build(); + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + when(channel.newCall( + Mockito.>any(), any(HttpJsonCallOptions.class))) + .thenReturn(call); + } + + @Test + void testInterceptor_basic() { + + HttpJsonLoggingInterceptor interceptor = new HttpJsonLoggingInterceptor(); + // HttpJsonChannel intercepted = HttpJsonClientInterceptor.intercept() + } +} diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java index 87768c0ccf..feccab6441 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java @@ -138,4 +138,53 @@ public Map toMap() { } return map; } + + public Map toMapRequest() { + Map map = new HashMap<>(); + if (serviceName() != null) { + map.put("serviceName", serviceName()); + } + if (rpcName() != null) { + map.put("rpcName", rpcName()); + } + if (requestId() != null) { + map.put("requestId", requestId()); + } + if (requestHeaders() != null) { + map.put("request.headers", requestHeaders()); + } + if (requestPayload() != null) { + map.put("request.payload", requestPayload()); + } + if (httpMethod() != null) { + map.put("request.method", httpMethod()); + } + if (httpUrl() != null) { + map.put("request.url", httpUrl()); + } + return map; + } + + public Map toMapResponse() { + Map map = new HashMap<>(); + if (serviceName() != null) { + map.put("serviceName", serviceName()); + } + if (rpcName() != null) { + map.put("rpcName", rpcName()); + } + if (requestId() != null) { + map.put("requestId", requestId()); + } + if (responseStatus() != null) { + map.put("response.status", responseStatus()); + } + if (responseHeaders() != null) { + map.put("response.headers", responseHeaders()); + } + if (responsePayload() != null) { + map.put("response.payload", gson.toJson(responsePayload())); + } + return map; + } } From 56a2870ecce95f422cb61c2336638df5e1f67335 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Mon, 9 Dec 2024 23:18:36 -0500 Subject: [PATCH 25/92] replace txt message to json message with all fields duplicated. --- .../com/google/api/gax/logging/LoggingUtils.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java index 6eba4a3430..c738426aa9 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java @@ -33,6 +33,7 @@ import com.google.api.core.InternalApi; import com.google.api.gax.rpc.internal.EnvironmentProvider; import com.google.api.gax.rpc.internal.SystemEnvironmentProvider; +import com.google.gson.Gson; import java.util.Map; import org.slf4j.ILoggerFactory; import org.slf4j.Logger; @@ -46,6 +47,7 @@ public class LoggingUtils { private static final Logger NO_OP_LOGGER = org.slf4j.helpers.NOPLogger.NOP_LOGGER; private static boolean loggingEnabled = isLoggingEnabled(); static final String GOOGLE_SDK_JAVA_LOGGING = "GOOGLE_SDK_JAVA_LOGGING"; + private static final Gson gson = new Gson(); // expose this setter for testing purposes static void setEnvironmentProvider(EnvironmentProvider provider) { environmentProvider = provider; @@ -71,7 +73,11 @@ static Logger getLogger(Class clazz, LoggerFactoryProvider factoryProvider) { public static void logWithMDC( Logger logger, org.slf4j.event.Level level, Map contextMap, String message) { - contextMap.forEach(MDC::put); + if (!contextMap.isEmpty()) { + contextMap.forEach(MDC::put); + contextMap.put("message", message); + message = gson.toJson(contextMap); + } switch (level) { case TRACE: logger.trace(message); @@ -92,7 +98,9 @@ public static void logWithMDC( logger.info(message); // Default to INFO level } - MDC.clear(); + if (!contextMap.isEmpty()) { + MDC.clear(); + } } static boolean isLoggingEnabled() { From 83eedf07fd0ad6c19fa029cbb2b0ecca830cf3d4 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 10 Dec 2024 11:51:29 -0500 Subject: [PATCH 26/92] add try catch to logging methods --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 20 ++++++++++++----- .../httpjson/HttpJsonLoggingInterceptor.java | 22 ++++++++++++------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index f96a873312..248663635e 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -103,7 +103,7 @@ public void sendMessage(ReqT message) { } // Helper methods for logging - // some duplications with http equivalent to avoid exposing as public method + // some duplications with http equivalent to avoid exposing as public method for now void logRequestInfo( MethodDescriptor method, LogData.Builder logDataBuilder) { try { @@ -132,15 +132,23 @@ private void recordRequestHeaders(Metadata headers, LogData.Builder logDataBuild } void recordResponseHeaders(Metadata headers, LogData.Builder logDataBuilder) { - if (logger.isDebugEnabled()) { - JsonObject responseHeaders = mapHeadersToJsonObject(headers); - logDataBuilder.responseHeaders(gson.toJson(responseHeaders)); + try { + if (logger.isDebugEnabled()) { + JsonObject responseHeaders = mapHeadersToJsonObject(headers); + logDataBuilder.responseHeaders(gson.toJson(responseHeaders)); + } + } catch (Exception e) { + logger.error("Error recording response headers", e); } } void recordResponsePayload(RespT message, LogData.Builder logDataBuilder) { - if (logger.isDebugEnabled()) { - logDataBuilder.responsePayload(gson.toJsonTree(message)); + try { + if (logger.isDebugEnabled()) { + logDataBuilder.responsePayload(gson.toJsonTree(message)); + } + } catch (Exception e) { + logger.error("Error recording response payload", e); } } diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index fcaeff1fc6..b1167109eb 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -140,24 +140,30 @@ private void recordRequestHeaders(HttpJsonMetadata headers, LogData.Builder logD private void recordResponseHeaders( HttpJsonMetadata responseHeaders, LogData.Builder logDataBuilder) { + try { + if (logger.isDebugEnabled()) { - if (logger.isDebugEnabled()) { - - Map> map = new HashMap<>(); - responseHeaders.getHeaders().forEach((key, value) -> map.put(key, (List) value)); - logDataBuilder.responseHeaders(gson.toJson(map)); + Map> map = new HashMap<>(); + responseHeaders.getHeaders().forEach((key, value) -> map.put(key, (List) value)); + logDataBuilder.responseHeaders(gson.toJson(map)); + } + } catch (Exception e) { + logger.error("Error recording response headers", e); } } private void recordResponsePayload(RespT message, LogData.Builder logDataBuilder) { - if (logger.isDebugEnabled()) { - logDataBuilder.responsePayload(gson.toJsonTree(message)); + try { + if (logger.isDebugEnabled()) { + logDataBuilder.responsePayload(gson.toJsonTree(message)); + } + } catch (Exception e) { + logger.error("Error recording response payload", e); } } private void logResponse(int statusCode, LogData.Builder logDataBuilder) { try { - if (logger.isInfoEnabled()) { logDataBuilder.responseStatus(String.valueOf(statusCode)); } From d85c8487d900ac8ac942a9c1d14853ad9a470d9a Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 10 Dec 2024 17:38:42 -0500 Subject: [PATCH 27/92] add showcase tests. expose logging interceptors as public to setup tests. --- .github/workflows/ci.yaml | 9 ++ .../api/gax/grpc/GrpcLoggingInterceptor.java | 2 +- .../httpjson/HttpJsonLoggingInterceptor.java | 2 +- .../api/gax/logging/LoggingUtilsTest.java | 27 ++-- showcase/gapic-showcase/pom.xml | 24 +--- .../google/showcase/v1beta1/it/ITLogging.java | 120 +++++------------- .../showcase/v1beta1/it/ITUnaryCallable.java | 8 -- .../v1beta1/it/util/TestAppender.java | 4 +- .../src/test/resources/logback-test.xml | 39 +----- showcase/pom.xml | 44 +++++++ 10 files changed, 107 insertions(+), 172 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index cbe7bd9bd6..6af6c36f7a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -255,6 +255,15 @@ jobs: -P enable-integration-tests \ --batch-mode \ --no-transfer-progress + # The `envVarLoggingTest` profile runs tests that require an environment variable + - name: Showcase integration tests - Logging + run: | + verify -P '!showcase,enable-integration-tests,envVarLoggingTest' + --batch-mode \ + --no-transfer-progress + # Set the Env Var for this step only + env: + GOOGLE_SDK_JAVA_LOGGING: true showcase-native: runs-on: ubuntu-22.04 diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index 248663635e..e8c2f8cf15 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -47,7 +47,7 @@ import org.slf4j.Logger; import org.slf4j.event.Level; -class GrpcLoggingInterceptor implements ClientInterceptor { +public class GrpcLoggingInterceptor implements ClientInterceptor { private static final Logger logger = LoggingUtils.getLogger(GrpcLoggingInterceptor.class); private static final Gson gson = new Gson(); diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index b1167109eb..a898df79e9 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -42,7 +42,7 @@ import org.slf4j.Logger; import org.slf4j.event.Level; -class HttpJsonLoggingInterceptor implements HttpJsonClientInterceptor { +public class HttpJsonLoggingInterceptor implements HttpJsonClientInterceptor { private static final Logger logger = LoggingUtils.getLogger(HttpJsonLoggingInterceptor.class); private static final Gson gson = new Gson(); diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java index fddc565df9..486677c7ab 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java @@ -46,18 +46,19 @@ import com.google.api.gax.rpc.internal.EnvironmentProvider; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.slf4j.ILoggerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.helpers.NOPLogger; -public class LoggingUtilsTest { +class LoggingUtilsTest { private EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class); @BeforeEach - public void setup() { + void setup() { EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class); // need to setup a ConsoleAppender and attach to root logger because TestAppender @@ -83,13 +84,13 @@ public void setup() { } @AfterEach - public void tearDown() { + void tearDown() { LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).detachAppender("CONSOLE"); } - @org.junit.Test - public void testGetLogger_loggingEnabled_slf4jBindingPresent() { + @Test + void testGetLogger_loggingEnabled_slf4jBindingPresent() { Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("true"); LoggingUtils.setEnvironmentProvider(envProvider); Logger logger = LoggingUtils.getLogger(LoggingUtilsTest.class); @@ -97,8 +98,8 @@ public void testGetLogger_loggingEnabled_slf4jBindingPresent() { assertNotEquals(logger.getClass(), NOPLogger.class); } - @org.junit.Test - public void testGetLogger_loggingDisabled() { + @Test + void testGetLogger_loggingDisabled() { Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("false"); LoggingUtils.setEnvironmentProvider(envProvider); @@ -106,8 +107,8 @@ public void testGetLogger_loggingDisabled() { assertEquals(NOPLogger.class, logger.getClass()); } - @org.junit.Test - public void testGetLogger_loggingEnabled_noBinding() { + @Test + void testGetLogger_loggingEnabled_noBinding() { Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("true"); LoggingUtils.setEnvironmentProvider(envProvider); // Create a mock LoggerFactoryProvider @@ -124,8 +125,8 @@ public void testGetLogger_loggingEnabled_noBinding() { assertTrue(logger instanceof org.slf4j.helpers.NOPLogger); } - @org.junit.Test - public void testIsLoggingEnabled_true() { + @Test + void testIsLoggingEnabled_true() { Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("true"); LoggingUtils.setEnvironmentProvider(envProvider); assertTrue(LoggingUtils.isLoggingEnabled()); @@ -137,8 +138,8 @@ public void testIsLoggingEnabled_true() { assertTrue(LoggingUtils.isLoggingEnabled()); } - @org.junit.Test - public void testIsLoggingEnabled_defaultToFalse() { + @Test + void testIsLoggingEnabled_defaultToFalse() { LoggingUtils.setEnvironmentProvider(envProvider); assertFalse(LoggingUtils.isLoggingEnabled()); } diff --git a/showcase/gapic-showcase/pom.xml b/showcase/gapic-showcase/pom.xml index cbf15d5269..f4d9ade8ba 100644 --- a/showcase/gapic-showcase/pom.xml +++ b/showcase/gapic-showcase/pom.xml @@ -216,12 +216,7 @@ test - - - org.slf4j - slf4j-api - 2.0.16 - + ch.qos.logback logback-classic @@ -232,23 +227,6 @@ ch.qos.logback logback-core 1.5.11 - - - net.logstash.logback - logstash-logback-encoder - 7.2 - test - - - org.codehaus.janino - janino - 3.1.9 - test - - - org.mockito - mockito-core - 4.11.0 test diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLogging.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLogging.java index a0fb67e2fe..51e7145e99 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLogging.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLogging.java @@ -19,24 +19,32 @@ import static com.google.common.truth.Truth.assertThat; import ch.qos.logback.classic.spi.ILoggingEvent; +import com.google.api.gax.grpc.GrpcLoggingInterceptor; +import com.google.api.gax.httpjson.HttpJsonLoggingInterceptor; import com.google.showcase.v1beta1.EchoClient; import com.google.showcase.v1beta1.EchoRequest; import com.google.showcase.v1beta1.EchoResponse; import com.google.showcase.v1beta1.it.util.TestAppender; import com.google.showcase.v1beta1.it.util.TestClientInitializer; import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.slf4j.LoggerFactory; // This test needs to run with GOOGLE_SDK_JAVA_LOGGING=true public class ITLogging { private static EchoClient grpcClient; private static EchoClient httpjsonClient; - static final Logger LOGGER = Logger.getLogger(ITUnaryCallable.class.getName()); + + private TestAppender setupTestLogger(Class clazz) { + TestAppender testAppender = new TestAppender(); + testAppender.start(); + org.slf4j.Logger logger = LoggerFactory.getLogger(clazz); + ((ch.qos.logback.classic.Logger) logger).addAppender(testAppender); + return testAppender; + } @BeforeAll static void createClients() throws Exception { @@ -44,8 +52,6 @@ static void createClients() throws Exception { grpcClient = TestClientInitializer.createGrpcEchoClient(); // Create Http JSON Echo Client httpjsonClient = TestClientInitializer.createHttpJsonEchoClient(); - - LOGGER.log(Level.INFO, "This is log message directly from JUL. Clients created."); } @AfterAll @@ -60,95 +66,37 @@ static void destroyClients() throws InterruptedException { @Test void testGrpc_receiveContent_logDebug() { - LOGGER.log( - Level.INFO, - "This is log message directly from JUL. Starting test: testGrpc_receiveContent."); - - TestAppender.clearEvents(); + TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptor.class); assertThat(echoGrpc("grpc-echo?")).isEqualTo("grpc-echo?"); - assertThat(TestAppender.events.size()).isEqualTo(3); - assertThat(TestAppender.events.get(0).getMessage()).isEqualTo("Sending gRPC request"); - assertThat(TestAppender.events.get(0).getLevel()).isEqualTo(ch.qos.logback.classic.Level.DEBUG); - assertThat(TestAppender.events.get(1).getMessage()) - .isEqualTo("Sending gRPC request: request payload"); - assertThat(TestAppender.events.get(2).getMessage()).isEqualTo("Received Grpc response"); + assertThat(testAppender.events.size()).isEqualTo(2); + assertThat(testAppender.events.get(0).getMessage()) + .isEqualTo( + "{\"serviceName\":\"google.showcase.v1beta1.Echo\",\"message\":\"Sending gRPC request\",\"rpcName\":\"google.showcase.v1beta1.Echo/Echo\"}"); + assertThat(testAppender.events.get(0).getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); + assertThat(testAppender.events.get(1).getMessage()) + .isEqualTo( + "{\"serviceName\":\"google.showcase.v1beta1.Echo\",\"response.status\":\"OK\",\"message\":\"Received Grpc response\",\"rpcName\":\"google.showcase.v1beta1.Echo/Echo\"}"); + testAppender.stop(); } - // @Test - // void testGrpc_receiveContent_logInfo() { - // ch.qos.logback.classic.Logger logger = - // (ch.qos.logback.classic.Logger) LoggingUtils.getLogger(GrpcLoggingInterceptor.class); - // ch.qos.logback.classic.Level originalLevel = logger.getLevel(); - // try { - // logger.setLevel(ch.qos.logback.classic.Level.INFO); - // assertThat(logger.getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); - // - // TestAppender.clearEvents(); - // assertThat(echoGrpc("grpc-echo?")).isEqualTo("grpc-echo?"); - // assertThat(TestAppender.events.size()).isEqualTo(2); - // ILoggingEvent loggingEvent1 = TestAppender.events.get(0); - // assertThat(loggingEvent1.getMessage()).isEqualTo("Sending gRPC request"); - // assertThat(loggingEvent1.getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); - // assertThat(loggingEvent1.getMDCPropertyMap()).hasSize(3); - // assertThat(loggingEvent1.getMDCPropertyMap()) - // .containsEntry("serviceName", "google.showcase.v1beta1.Echo"); - // assertThat(loggingEvent1.getMDCPropertyMap()) - // .containsEntry("rpcName", "google.showcase.v1beta1.Echo/Echo"); - // assertThat(TestAppender.events.get(1).getMessage()).isEqualTo("Received Grpc response"); - // assertThat(TestAppender.events.get(1).getLevel()) - // .isEqualTo(ch.qos.logback.classic.Level.INFO); - // } finally { - // logger.setLevel(originalLevel); - // } - // } - @Test void testHttpJson_receiveContent_logDebug() { - TestAppender.clearEvents(); + TestAppender testAppender = setupTestLogger(HttpJsonLoggingInterceptor.class); assertThat(echoHttpJson("http-echo?")).isEqualTo("http-echo?"); - assertThat(TestAppender.events.size()).isEqualTo(3); - ILoggingEvent loggingEvent1 = TestAppender.events.get(0); - assertThat(loggingEvent1.getMessage()).isEqualTo("Sending HTTP request"); - assertThat(loggingEvent1.getLevel()).isEqualTo(ch.qos.logback.classic.Level.DEBUG); - assertThat(loggingEvent1.getMDCPropertyMap()).hasSize(5); - assertThat(loggingEvent1.getMDCPropertyMap()).containsKey("request.headers"); - assertThat(TestAppender.events.get(1).getMessage()) - .isEqualTo("Sending HTTP request: request payload"); - assertThat(TestAppender.events.get(2).getMessage()).isEqualTo("Received HTTP response"); + assertThat(testAppender.events.size()).isEqualTo(2); + ILoggingEvent loggingEvent1 = testAppender.events.get(0); + assertThat(loggingEvent1.getMessage()) + .isEqualTo( + "{\"request.method\":\"POST\",\"request.url\":\"http://localhost:7469\",\"message\":\"Sending HTTP request\",\"rpcName\":\"google.showcase.v1beta1.Echo/Echo\"}"); + assertThat(loggingEvent1.getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); + assertThat(loggingEvent1.getMDCPropertyMap()).hasSize(3); + assertThat(loggingEvent1.getMDCPropertyMap()).containsKey("rpcName"); + assertThat(testAppender.events.get(1).getMessage()) + .isEqualTo( + "{\"response.status\":\"200\",\"message\":\"Received HTTP response\",\"rpcName\":\"google.showcase.v1beta1.Echo/Echo\"}"); + testAppender.stop(); } - // @Test - // void testHttpJson_receiveContent_logInfo() { - // - // ch.qos.logback.classic.Logger logger = - // (ch.qos.logback.classic.Logger) LoggingUtils.getLogger(HttpJsonLoggingInterceptor.class); - // ch.qos.logback.classic.Level originalLevel = logger.getLevel(); - // try { - // logger.setLevel(ch.qos.logback.classic.Level.INFO); - // assertThat(logger.getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); - // - // TestAppender.clearEvents(); - // assertThat(echoHttpJson("http-echo?")).isEqualTo("http-echo?"); - // assertThat(TestAppender.events.size()).isEqualTo(2); - // ILoggingEvent loggingEvent1 = TestAppender.events.get(0); - // assertThat(loggingEvent1.getMessage()).isEqualTo("Sending HTTP request"); - // assertThat(loggingEvent1.getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); - // assertThat(loggingEvent1.getMDCPropertyMap()).hasSize(4); - // assertThat(loggingEvent1.getMDCPropertyMap()) - // .containsEntry("rpcName", "google.showcase.v1beta1.Echo/Echo"); - // assertThat(loggingEvent1.getMDCPropertyMap()).containsEntry("request.method", "POST"); - // assertThat(loggingEvent1.getMDCPropertyMap()) - // .containsEntry("request.url", "http://localhost:7469"); - // assertThat(TestAppender.events.get(1).getMessage()).isEqualTo("Received HTTP response"); - // assertThat(TestAppender.events.get(1).getLevel()) - // .isEqualTo(ch.qos.logback.classic.Level.INFO); - // assertThat(TestAppender.events.get(1).getMDCPropertyMap()) - // .containsEntry("response.status", "200"); - // } finally { - // logger.setLevel(originalLevel); - // } - // } - private String echoGrpc(String value) { EchoResponse response = grpcClient.echo(EchoRequest.newBuilder().setContent(value).build()); return response.getContent(); diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITUnaryCallable.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITUnaryCallable.java index 76a78e7ff9..4d6018f6fc 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITUnaryCallable.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITUnaryCallable.java @@ -28,8 +28,6 @@ import com.google.showcase.v1beta1.EchoResponse; import com.google.showcase.v1beta1.it.util.TestClientInitializer; import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -39,7 +37,6 @@ class ITUnaryCallable { private static EchoClient grpcClient; private static EchoClient httpjsonClient; - static final Logger LOGGER = Logger.getLogger(ITUnaryCallable.class.getName()); @BeforeAll static void createClients() throws Exception { @@ -47,8 +44,6 @@ static void createClients() throws Exception { grpcClient = TestClientInitializer.createGrpcEchoClient(); // Create Http JSON Echo Client httpjsonClient = TestClientInitializer.createHttpJsonEchoClient(); - - LOGGER.log(Level.INFO, "This is log message directly from JUL. Clients created."); } @AfterAll @@ -63,9 +58,6 @@ static void destroyClients() throws InterruptedException { @Test void testGrpc_receiveContent() { - LOGGER.log( - Level.INFO, - "This is log message directly from JUL. Starting test: testGrpc_receiveContent."); assertThat(echoGrpc("grpc-echo?")).isEqualTo("grpc-echo?"); assertThat(echoGrpc("grpc-echo!")).isEqualTo("grpc-echo!"); } diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/util/TestAppender.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/util/TestAppender.java index 4ed5439fc7..0032b09d9d 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/util/TestAppender.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/util/TestAppender.java @@ -24,7 +24,7 @@ /** Logback appender used to set up tests. */ public class TestAppender extends AppenderBase { - public static List events = new ArrayList<>(); + public List events = new ArrayList<>(); @Override protected void append(ILoggingEvent eventObject) { @@ -33,7 +33,7 @@ protected void append(ILoggingEvent eventObject) { events.add(eventObject); } - public static void clearEvents() { + public void clearEvents() { events.clear(); } } diff --git a/showcase/gapic-showcase/src/test/resources/logback-test.xml b/showcase/gapic-showcase/src/test/resources/logback-test.xml index c38d09e794..66ed2652ea 100644 --- a/showcase/gapic-showcase/src/test/resources/logback-test.xml +++ b/showcase/gapic-showcase/src/test/resources/logback-test.xml @@ -1,43 +1,6 @@ - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/showcase/pom.xml b/showcase/pom.xml index 6cbcdf92ab..b26d06521e 100644 --- a/showcase/pom.xml +++ b/showcase/pom.xml @@ -117,6 +117,50 @@ org.apache.maven.plugins maven-failsafe-plugin + + + **/ITLogging.java + + + + + + + org.apache.maven.surefire + surefire-junit-platform + ${surefire.version} + + + + + org.codehaus.mojo + flatten-maven-plugin + + + + + + + envVarLoggingTest + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.0 + + sponge_log + ${skipUnitTests} + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + **/ITLogging.java + + From 1919809ecebafc982540791480f7d12e75a59435 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 10 Dec 2024 17:40:26 -0500 Subject: [PATCH 28/92] add internal api annotation. --- .../java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java | 2 ++ .../com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index e8c2f8cf15..73f3d7e1a5 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -30,6 +30,7 @@ package com.google.api.gax.grpc; +import com.google.api.core.InternalApi; import com.google.api.gax.logging.LogData; import com.google.api.gax.logging.LoggingUtils; import com.google.gson.Gson; @@ -47,6 +48,7 @@ import org.slf4j.Logger; import org.slf4j.event.Level; +@InternalApi public class GrpcLoggingInterceptor implements ClientInterceptor { private static final Logger logger = LoggingUtils.getLogger(GrpcLoggingInterceptor.class); diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index a898df79e9..07fa52372a 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -30,6 +30,7 @@ package com.google.api.gax.httpjson; +import com.google.api.core.InternalApi; import com.google.api.gax.httpjson.ForwardingHttpJsonClientCall.SimpleForwardingHttpJsonClientCall; import com.google.api.gax.httpjson.ForwardingHttpJsonClientCallListener.SimpleForwardingHttpJsonClientCallListener; import com.google.api.gax.logging.LogData; @@ -42,6 +43,7 @@ import org.slf4j.Logger; import org.slf4j.event.Level; +@InternalApi public class HttpJsonLoggingInterceptor implements HttpJsonClientInterceptor { private static final Logger logger = LoggingUtils.getLogger(HttpJsonLoggingInterceptor.class); From 613b6c83ba2f138e4501d8ae942a44ceeb416132 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 10 Dec 2024 17:56:11 -0500 Subject: [PATCH 29/92] lint --- .../HttpJsonLoggingInterceptorTest.java | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java index e8ca5f0c6f..48bc6b9a97 100644 --- a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java +++ b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java @@ -1,6 +1,35 @@ +/* + * Copyright 2024 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.httpjson; -import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -38,6 +67,5 @@ void setUp() { void testInterceptor_basic() { HttpJsonLoggingInterceptor interceptor = new HttpJsonLoggingInterceptor(); - // HttpJsonChannel intercepted = HttpJsonClientInterceptor.intercept() } } From 1169eaaa200db35507a1131debf4151f6057665f Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 10 Dec 2024 20:33:08 -0500 Subject: [PATCH 30/92] fix typo --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6af6c36f7a..94f3ccd223 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -258,7 +258,7 @@ jobs: # The `envVarLoggingTest` profile runs tests that require an environment variable - name: Showcase integration tests - Logging run: | - verify -P '!showcase,enable-integration-tests,envVarLoggingTest' + mvn verify -P '!showcase,enable-integration-tests,envVarLoggingTest' --batch-mode \ --no-transfer-progress # Set the Env Var for this step only From a6b5433e7516386a836f757531ffce50edf0db30 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 10 Dec 2024 20:49:07 -0500 Subject: [PATCH 31/92] remove unused test class. --- .../HttpJsonLoggingInterceptorTest.java | 71 ------------------- 1 file changed, 71 deletions(-) delete mode 100644 gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java diff --git a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java deleted file mode 100644 index 48bc6b9a97..0000000000 --- a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2024 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.httpjson; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import com.google.api.gax.httpjson.ApiMethodDescriptor.MethodType; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; - -class HttpJsonLoggingInterceptorTest { - - @Mock private HttpJsonChannel channel; - - @Mock private HttpJsonClientCall call; - - private static final ApiMethodDescriptor method = - ApiMethodDescriptor.newBuilder() - .setType(MethodType.UNARY) - .setRequestFormatter(mock(HttpRequestFormatter.class)) - .setRequestFormatter(mock(HttpRequestFormatter.class)) - .setFullMethodName("FakeClient/fake-method") - .build(); - - @BeforeEach - void setUp() { - MockitoAnnotations.openMocks(this); - when(channel.newCall( - Mockito.>any(), any(HttpJsonCallOptions.class))) - .thenReturn(call); - } - - @Test - void testInterceptor_basic() { - - HttpJsonLoggingInterceptor interceptor = new HttpJsonLoggingInterceptor(); - } -} From fb0966e0b48e8023f8309a15e7c19a7c55b7f2fb Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 10 Dec 2024 20:55:43 -0500 Subject: [PATCH 32/92] try to exclude test from showcase-native. --- showcase/pom.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/showcase/pom.xml b/showcase/pom.xml index b26d06521e..bdfccf08f4 100644 --- a/showcase/pom.xml +++ b/showcase/pom.xml @@ -95,6 +95,15 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + + **/ITLogging.java + + + From 77939feaa9dc0a78647219ddfca22b855568bd67 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 10 Dec 2024 21:00:10 -0500 Subject: [PATCH 33/92] fix logback version. --- showcase/gapic-showcase/pom.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/showcase/gapic-showcase/pom.xml b/showcase/gapic-showcase/pom.xml index f4d9ade8ba..583ab17219 100644 --- a/showcase/gapic-showcase/pom.xml +++ b/showcase/gapic-showcase/pom.xml @@ -220,13 +220,14 @@ ch.qos.logback logback-classic - 1.5.11 + + 1.3.14 test ch.qos.logback logback-core - 1.5.11 + 1.3.14 test From 04ef7748e1a135857d02fb6275718acb49b69265 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 10 Dec 2024 21:05:31 -0500 Subject: [PATCH 34/92] revert accidental change. --- pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/pom.xml b/pom.xml index d4379722a8..d207468e77 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,6 @@ gapic-generator-java-bom java-shared-dependencies sdk-platform-java-config - showcase From 439e0711fd54d7083d5cc74ed9a162db069d60f6 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 10 Dec 2024 21:08:28 -0500 Subject: [PATCH 35/92] fix typo in ci. --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 94f3ccd223..10e224318a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -258,7 +258,7 @@ jobs: # The `envVarLoggingTest` profile runs tests that require an environment variable - name: Showcase integration tests - Logging run: | - mvn verify -P '!showcase,enable-integration-tests,envVarLoggingTest' + mvn verify -P '!showcase,enable-integration-tests,envVarLoggingTest' \ --batch-mode \ --no-transfer-progress # Set the Env Var for this step only From 673c9fe9b75dbdfbc1b92072d1ba53ce51674826 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 10 Dec 2024 22:21:15 -0500 Subject: [PATCH 36/92] cleanup gax pom. add tests. --- gapic-generator-java-pom-parent/pom.xml | 1 + gax-java/gax/pom.xml | 15 ---- .../com/google/api/gax/logging/LogData.java | 35 -------- .../google/api/gax/logging/LogDataTest.java | 85 +++++++++++++++++++ .../api/gax/logging/LoggingUtilsTest.java | 42 ++++++--- .../google/api/gax/logging/TestAppender.java | 52 ++++++++++++ gax-java/pom.xml | 19 +++++ 7 files changed, 185 insertions(+), 64 deletions(-) create mode 100644 gax-java/gax/src/test/java/com/google/api/gax/logging/LogDataTest.java create mode 100644 gax-java/gax/src/test/java/com/google/api/gax/logging/TestAppender.java diff --git a/gapic-generator-java-pom-parent/pom.xml b/gapic-generator-java-pom-parent/pom.xml index 1991f17d01..425dd9ed37 100644 --- a/gapic-generator-java-pom-parent/pom.xml +++ b/gapic-generator-java-pom-parent/pom.xml @@ -38,6 +38,7 @@ 3.0.0 1.7.0 5.11.2 + 2.0.16 diff --git a/gax-java/gax/pom.xml b/gax-java/gax/pom.xml index 2f86586d82..2f7080e817 100644 --- a/gax-java/gax/pom.xml +++ b/gax-java/gax/pom.xml @@ -73,21 +73,6 @@ org.slf4j slf4j-api - 2.0.16 - - - - ch.qos.logback - logback-classic - - 1.3.14 - test - - - ch.qos.logback - logback-core - 1.3.14 - test diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java index feccab6441..db4767eb20 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java @@ -104,41 +104,6 @@ public abstract static class Builder { // helper functions to convert to map for logging // todo: error handling? - public Map toMap() { - Map map = new HashMap<>(); - if (serviceName() != null) { - map.put("serviceName", serviceName()); - } - if (rpcName() != null) { - map.put("rpcName", rpcName()); - } - if (requestId() != null) { - map.put("requestId", requestId()); - } - if (requestHeaders() != null) { - map.put("request.headers", requestHeaders()); - } - if (requestPayload() != null) { - map.put("request.payload", requestPayload()); - } - if (responseStatus() != null) { - map.put("response.status", responseStatus()); - } - if (responseHeaders() != null) { - map.put("response.headers", responseHeaders()); - } - if (responsePayload() != null) { - map.put("response.payload", gson.toJson(responsePayload())); - } - if (httpMethod() != null) { - map.put("request.method", httpMethod()); - } - if (httpUrl() != null) { - map.put("request.url", httpUrl()); - } - return map; - } - public Map toMapRequest() { Map map = new HashMap<>(); if (serviceName() != null) { diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LogDataTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LogDataTest.java new file mode 100644 index 0000000000..d19c74591a --- /dev/null +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LogDataTest.java @@ -0,0 +1,85 @@ +/* + * Copyright 2024 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.logging; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonParser; +import java.util.Map; +import org.junit.jupiter.api.Test; + +class LogDataTest { + + @Test + void toMapResponse_correctlyConvertsData() { + LogData logData = + LogData.builder() + .serviceName("MyService") + .rpcName("myMethod") + .requestHeaders("fake header") + .requestId("abcd") + .responsePayload(JsonParser.parseString("{\"key\": \"value\"}")) + .build(); + + Map expectedMap = + ImmutableMap.of( + "serviceName", "MyService", + "rpcName", "myMethod", + "response.payload", "{\"key\":\"value\"}", + "requestId", "abcd"); + + assertThat(logData.toMapResponse()).isEqualTo(expectedMap); + } + + @Test + void toMapRequest_correctlyConvertsData() { + LogData logData = + LogData.builder() + .serviceName("MyService") + .rpcName("myMethod") + .requestHeaders("fake header") + .requestId("abcd") + .httpUrl("url") + .responsePayload(JsonParser.parseString("{\"key\": \"value\"}")) + .build(); + + Map expectedMap = + ImmutableMap.of( + "serviceName", "MyService", + "rpcName", "myMethod", + "request.headers", "fake header", + "requestId", "abcd", + "request.url", "url"); + + assertThat(logData.toMapRequest()).isEqualTo(expectedMap); + } +} diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java index 486677c7ab..722a2d5a22 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java @@ -44,6 +44,8 @@ import ch.qos.logback.core.ConsoleAppender; import com.google.api.gax.logging.LoggingUtils.LoggerFactoryProvider; import com.google.api.gax.rpc.internal.EnvironmentProvider; +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; @@ -55,6 +57,7 @@ class LoggingUtilsTest { + private static final Logger LOGGER = LoggerFactory.getLogger(LoggingUtilsTest.class); private EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class); @BeforeEach @@ -144,18 +147,29 @@ void testIsLoggingEnabled_defaultToFalse() { assertFalse(LoggingUtils.isLoggingEnabled()); } - // @Test - // public void testLogWithMDC_slf4jLogger() { - // TestAppender.clearEvents(); - // Map contextMap = new HashMap<>(); - // contextMap.put("key", "value"); - // LoggingUtils.logWithMDC(LOGGER, org.slf4j.event.Level.DEBUG, contextMap, "test message"); - // - // assertEquals(1, TestAppender.events.size()); - // assertEquals("test message", TestAppender.events.get(0).getFormattedMessage()); - // - // // Verify MDC content - // ILoggingEvent event = TestAppender.events.get(0); - // assertEquals("value", event.getMDCPropertyMap().get("key")); - // } + private TestAppender setupTestLogger(Class clazz) { + TestAppender testAppender = new TestAppender(); + testAppender.start(); + Logger logger = LoggerFactory.getLogger(clazz); + ((ch.qos.logback.classic.Logger) logger).addAppender(testAppender); + return testAppender; + } + + @Test + public void testLogWithMDC_slf4jLogger() { + TestAppender testAppender = setupTestLogger(LoggingUtilsTest.class); + Map contextMap = new HashMap<>(); + contextMap.put("key", "value"); + LoggingUtils.logWithMDC(LOGGER, org.slf4j.event.Level.DEBUG, contextMap, "test message"); + + assertEquals(1, testAppender.events.size()); + assertEquals( + "{\"message\":\"test message\",\"key\":\"value\"}", + testAppender.events.get(0).getFormattedMessage()); + + // Verify MDC content + ILoggingEvent event = testAppender.events.get(0); + assertEquals("value", event.getMDCPropertyMap().get("key")); + testAppender.stop(); + } } diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestAppender.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestAppender.java new file mode 100644 index 0000000000..1206553ee6 --- /dev/null +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestAppender.java @@ -0,0 +1,52 @@ +/* + * Copyright 2024 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.logging; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import java.util.ArrayList; +import java.util.List; + +/** Logback appender used to set up tests. */ +public class TestAppender extends AppenderBase { + public List events = new ArrayList<>(); + + @Override + protected void append(ILoggingEvent eventObject) { + // triggering Logback to capture the current MDC context and store it with the log event + eventObject.getMDCPropertyMap(); + events.add(eventObject); + } + + public void clearEvents() { + events.clear(); + } +} diff --git a/gax-java/pom.xml b/gax-java/pom.xml index 1963fd0b3f..007a4c099b 100644 --- a/gax-java/pom.xml +++ b/gax-java/pom.xml @@ -134,6 +134,11 @@ graal-sdk ${graal-sdk.version} + + org.slf4j + slf4j-api + ${slf4j.version} + com.google.http-client google-http-client-bom @@ -205,6 +210,20 @@ test + + + ch.qos.logback + logback-classic + + 1.3.14 + test + + + ch.qos.logback + logback-core + 1.3.14 + test + From c2b607ba654978d7d081b43ffe067af149468f7f Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 10 Dec 2024 22:43:16 -0500 Subject: [PATCH 37/92] add test. --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 4 +- .../gax/grpc/GrpcLoggingInterceptorTest.java | 29 +++++++++++ .../com/google/api/gax/grpc/TestAppender.java | 52 +++++++++++++++++++ .../src/test/resources/logback-test.xml | 6 +++ 4 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/TestAppender.java create mode 100644 gax-java/gax-grpc/src/test/resources/logback-test.xml diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index 73f3d7e1a5..4d48d7f663 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -66,7 +66,7 @@ public ClientCall interceptCall( @Override public void start(Listener responseListener, Metadata headers) { - logRequestInfo(method, logDataBuilder); + logRequestInfo(method, logDataBuilder, logger); recordRequestHeaders(headers, logDataBuilder); SimpleForwardingClientCallListener responseLoggingListener = new SimpleForwardingClientCallListener(responseListener) { @@ -107,7 +107,7 @@ public void sendMessage(ReqT message) { // Helper methods for logging // some duplications with http equivalent to avoid exposing as public method for now void logRequestInfo( - MethodDescriptor method, LogData.Builder logDataBuilder) { + MethodDescriptor method, LogData.Builder logDataBuilder, Logger logger) { try { if (logger.isInfoEnabled()) { logDataBuilder.serviceName(method.getServiceName()).rpcName(method.getFullMethodName()); diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java index d3fe5e5881..423221db2d 100644 --- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java +++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java @@ -30,6 +30,7 @@ package com.google.api.gax.grpc; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -37,6 +38,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import ch.qos.logback.classic.Level; import com.google.api.gax.grpc.testing.FakeMethodDescriptor; import com.google.api.gax.logging.LogData; import io.grpc.CallOptions; @@ -46,11 +48,14 @@ import io.grpc.Metadata; import io.grpc.MethodDescriptor; import io.grpc.Status; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; class GrpcLoggingInterceptorTest { @Mock private Channel channel; @@ -59,6 +64,7 @@ class GrpcLoggingInterceptorTest { private static final MethodDescriptor method = FakeMethodDescriptor.create(); + private static final Logger LOGGER = LoggerFactory.getLogger(GrpcLoggingInterceptorTest.class); /** Sets up mocks. */ @BeforeEach void setUp() { @@ -111,4 +117,27 @@ void testInterceptor_responseListener() { verify(interceptor).recordResponsePayload(any(), any(LogData.Builder.class)); verify(interceptor).logResponse(eq(status.getCode().toString()), any(LogData.Builder.class)); } + + @Test + void testLogRequestInfo() { + + TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptorTest.class); + GrpcLoggingInterceptor interceptor = new GrpcLoggingInterceptor(); + interceptor.logRequestInfo(method, LogData.builder(), LOGGER); + + Assertions.assertEquals(1, testAppender.events.size()); + assertEquals(Level.INFO, testAppender.events.get(0).getLevel()); + assertEquals( + "{\"serviceName\":\"FakeClient\",\"message\":\"Sending gRPC request\",\"rpcName\":\"FakeClient/fake-method\"}", + testAppender.events.get(0).getMessage()); + testAppender.stop(); + } + + private TestAppender setupTestLogger(Class clazz) { + TestAppender testAppender = new TestAppender(); + testAppender.start(); + Logger logger = LoggerFactory.getLogger(clazz); + ((ch.qos.logback.classic.Logger) logger).addAppender(testAppender); + return testAppender; + } } diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/TestAppender.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/TestAppender.java new file mode 100644 index 0000000000..0aa34ebb5a --- /dev/null +++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/TestAppender.java @@ -0,0 +1,52 @@ +/* + * Copyright 2024 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 ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import java.util.ArrayList; +import java.util.List; + +/** Logback appender used to set up tests. */ +public class TestAppender extends AppenderBase { + public List events = new ArrayList<>(); + + @Override + protected void append(ILoggingEvent eventObject) { + // triggering Logback to capture the current MDC context and store it with the log event + eventObject.getMDCPropertyMap(); + events.add(eventObject); + } + + public void clearEvents() { + events.clear(); + } +} diff --git a/gax-java/gax-grpc/src/test/resources/logback-test.xml b/gax-java/gax-grpc/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..802d0e18a7 --- /dev/null +++ b/gax-java/gax-grpc/src/test/resources/logback-test.xml @@ -0,0 +1,6 @@ + + + + + + From 4190dc72454b1b1515f76fd742640078e88cff9b Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 11 Dec 2024 09:38:49 -0500 Subject: [PATCH 38/92] rm redundant assignment. --- .../com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java | 1 - 1 file changed, 1 deletion(-) diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index 07fa52372a..fa4bcc366b 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -133,7 +133,6 @@ private void recordRequestHeaders(HttpJsonMetadata headers, LogData.Builder logD .getHeaders() .forEach((key, value) -> requestHeaders.addProperty(key, value.toString())); logDataBuilder.requestHeaders(gson.toJson(requestHeaders)); - logDataBuilder.requestHeaders(gson.toJson(requestHeaders)); } } catch (Exception e) { logger.error("Error recording request headers", e); From fefb436c3b4a0243aeed13db03a4f74c99e2fa32 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 11 Dec 2024 09:49:50 -0500 Subject: [PATCH 39/92] add logging test setup in httpjson. --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 44 +++++----- .../httpjson/HttpJsonLoggingInterceptor.java | 51 ++++++------ .../HttpJsonLoggingInterceptorTest.java | 81 +++++++++++++++++++ .../google/api/gax/httpjson/TestAppender.java | 52 ++++++++++++ .../src/test/resources/logback-test.xml | 6 ++ 5 files changed, 188 insertions(+), 46 deletions(-) create mode 100644 gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java create mode 100644 gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/TestAppender.java create mode 100644 gax-java/gax-httpjson/src/test/resources/logback-test.xml diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index 4d48d7f663..133b4dcc90 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -51,8 +51,8 @@ @InternalApi public class GrpcLoggingInterceptor implements ClientInterceptor { - private static final Logger logger = LoggingUtils.getLogger(GrpcLoggingInterceptor.class); - private static final Gson gson = new Gson(); + private static final Logger LOGGER = LoggingUtils.getLogger(GrpcLoggingInterceptor.class); + private static final Gson GSON = new Gson(); ClientCall.Listener currentListener; // expose for test setup @@ -66,7 +66,7 @@ public ClientCall interceptCall( @Override public void start(Listener responseListener, Metadata headers) { - logRequestInfo(method, logDataBuilder, logger); + logRequestInfo(method, logDataBuilder, LOGGER); recordRequestHeaders(headers, logDataBuilder); SimpleForwardingClientCallListener responseLoggingListener = new SimpleForwardingClientCallListener(responseListener) { @@ -124,65 +124,65 @@ void logRequestInfo( private void recordRequestHeaders(Metadata headers, LogData.Builder logDataBuilder) { try { - if (logger.isDebugEnabled()) { + if (LOGGER.isDebugEnabled()) { JsonObject requestHeaders = mapHeadersToJsonObject(headers); - logDataBuilder.requestHeaders(gson.toJson(requestHeaders)); + logDataBuilder.requestHeaders(GSON.toJson(requestHeaders)); } } catch (Exception e) { - logger.error("Error recording request headers", e); + LOGGER.error("Error recording request headers", e); } } void recordResponseHeaders(Metadata headers, LogData.Builder logDataBuilder) { try { - if (logger.isDebugEnabled()) { + if (LOGGER.isDebugEnabled()) { JsonObject responseHeaders = mapHeadersToJsonObject(headers); - logDataBuilder.responseHeaders(gson.toJson(responseHeaders)); + logDataBuilder.responseHeaders(GSON.toJson(responseHeaders)); } } catch (Exception e) { - logger.error("Error recording response headers", e); + LOGGER.error("Error recording response headers", e); } } void recordResponsePayload(RespT message, LogData.Builder logDataBuilder) { try { - if (logger.isDebugEnabled()) { - logDataBuilder.responsePayload(gson.toJsonTree(message)); + if (LOGGER.isDebugEnabled()) { + logDataBuilder.responsePayload(GSON.toJsonTree(message)); } } catch (Exception e) { - logger.error("Error recording response payload", e); + LOGGER.error("Error recording response payload", e); } } void logResponse(String statusCode, LogData.Builder logDataBuilder) { try { - if (logger.isInfoEnabled()) { + if (LOGGER.isInfoEnabled()) { logDataBuilder.responseStatus(statusCode); } - if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { + if (LOGGER.isInfoEnabled() && !LOGGER.isDebugEnabled()) { Map responseData = logDataBuilder.build().toMapResponse(); - LoggingUtils.logWithMDC(logger, Level.INFO, responseData, "Received Grpc response"); + LoggingUtils.logWithMDC(LOGGER, Level.INFO, responseData, "Received Grpc response"); } - if (logger.isDebugEnabled()) { + if (LOGGER.isDebugEnabled()) { Map responsedDetailsMap = logDataBuilder.build().toMapResponse(); - LoggingUtils.logWithMDC(logger, Level.DEBUG, responsedDetailsMap, "Received Grpc response"); + LoggingUtils.logWithMDC(LOGGER, Level.DEBUG, responsedDetailsMap, "Received Grpc response"); } } catch (Exception e) { - logger.error("Error logging request response", e); + LOGGER.error("Error logging request response", e); } } void logRequestDetails(RespT message, LogData.Builder logDataBuilder) { try { - if (logger.isDebugEnabled()) { - logDataBuilder.requestPayload(gson.toJson(message)); + if (LOGGER.isDebugEnabled()) { + logDataBuilder.requestPayload(GSON.toJson(message)); Map requestDetailsMap = logDataBuilder.build().toMapRequest(); LoggingUtils.logWithMDC( - logger, Level.DEBUG, requestDetailsMap, "Sending gRPC request: request payload"); + LOGGER, Level.DEBUG, requestDetailsMap, "Sending gRPC request: request payload"); } } catch (Exception e) { - logger.error("Error logging request details", e); + LOGGER.error("Error logging request details", e); } } diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index fa4bcc366b..cb21e54056 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -46,8 +46,8 @@ @InternalApi public class HttpJsonLoggingInterceptor implements HttpJsonClientInterceptor { - private static final Logger logger = LoggingUtils.getLogger(HttpJsonLoggingInterceptor.class); - private static final Gson gson = new Gson(); + private static final Logger LOGGER = LoggingUtils.getLogger(HttpJsonLoggingInterceptor.class); + private static final Gson GSON = new Gson(); @Override public HttpJsonClientCall interceptCall( @@ -65,7 +65,7 @@ public HttpJsonClientCall interceptCall( public void start( HttpJsonClientCall.Listener responseListener, HttpJsonMetadata headers) { - logRequestInfo(method, endpoint, logDataBuilder); + logRequestInfo(method, endpoint, logDataBuilder, LOGGER); recordRequestHeaders(headers, logDataBuilder); Listener forwardingResponseListener = @@ -106,8 +106,11 @@ public void sendMessage(ReqT message) { // Helper methods for logging, // some duplications with grpc equivalent to avoid exposing as public method - private void logRequestInfo( - ApiMethodDescriptor method, String endpoint, LogData.Builder logDataBuilder) { + void logRequestInfo( + ApiMethodDescriptor method, + String endpoint, + LogData.Builder logDataBuilder, + Logger logger) { try { if (logger.isInfoEnabled()) { logDataBuilder @@ -127,70 +130,70 @@ private void logRequestInfo( private void recordRequestHeaders(HttpJsonMetadata headers, LogData.Builder logDataBuilder) { try { - if (logger.isDebugEnabled()) { + if (LOGGER.isDebugEnabled()) { JsonObject requestHeaders = new JsonObject(); headers .getHeaders() .forEach((key, value) -> requestHeaders.addProperty(key, value.toString())); - logDataBuilder.requestHeaders(gson.toJson(requestHeaders)); + logDataBuilder.requestHeaders(GSON.toJson(requestHeaders)); } } catch (Exception e) { - logger.error("Error recording request headers", e); + LOGGER.error("Error recording request headers", e); } } private void recordResponseHeaders( HttpJsonMetadata responseHeaders, LogData.Builder logDataBuilder) { try { - if (logger.isDebugEnabled()) { + if (LOGGER.isDebugEnabled()) { Map> map = new HashMap<>(); responseHeaders.getHeaders().forEach((key, value) -> map.put(key, (List) value)); - logDataBuilder.responseHeaders(gson.toJson(map)); + logDataBuilder.responseHeaders(GSON.toJson(map)); } } catch (Exception e) { - logger.error("Error recording response headers", e); + LOGGER.error("Error recording response headers", e); } } private void recordResponsePayload(RespT message, LogData.Builder logDataBuilder) { try { - if (logger.isDebugEnabled()) { - logDataBuilder.responsePayload(gson.toJsonTree(message)); + if (LOGGER.isDebugEnabled()) { + logDataBuilder.responsePayload(GSON.toJsonTree(message)); } } catch (Exception e) { - logger.error("Error recording response payload", e); + LOGGER.error("Error recording response payload", e); } } private void logResponse(int statusCode, LogData.Builder logDataBuilder) { try { - if (logger.isInfoEnabled()) { + if (LOGGER.isInfoEnabled()) { logDataBuilder.responseStatus(String.valueOf(statusCode)); } - if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { + if (LOGGER.isInfoEnabled() && !LOGGER.isDebugEnabled()) { Map responseData = logDataBuilder.build().toMapResponse(); - LoggingUtils.logWithMDC(logger, Level.INFO, responseData, "Received HTTP response"); + LoggingUtils.logWithMDC(LOGGER, Level.INFO, responseData, "Received HTTP response"); } - if (logger.isDebugEnabled()) { + if (LOGGER.isDebugEnabled()) { Map responsedDetailsMap = logDataBuilder.build().toMapResponse(); - LoggingUtils.logWithMDC(logger, Level.DEBUG, responsedDetailsMap, "Received HTTP response"); + LoggingUtils.logWithMDC(LOGGER, Level.DEBUG, responsedDetailsMap, "Received HTTP response"); } } catch (Exception e) { - logger.error("Error logging request response", e); + LOGGER.error("Error logging request response", e); } } private void logRequestDetails(RespT message, LogData.Builder logDataBuilder) { try { - if (logger.isDebugEnabled()) { - logDataBuilder.requestPayload(gson.toJson(message)); + if (LOGGER.isDebugEnabled()) { + logDataBuilder.requestPayload(GSON.toJson(message)); Map requestDetailsMap = logDataBuilder.build().toMapRequest(); LoggingUtils.logWithMDC( - logger, Level.DEBUG, requestDetailsMap, "Sending HTTP request: request payload"); + LOGGER, Level.DEBUG, requestDetailsMap, "Sending HTTP request: request payload"); } } catch (Exception e) { - logger.error("Error logging request details", e); + LOGGER.error("Error logging request details", e); } } } diff --git a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java new file mode 100644 index 0000000000..76546d9d1d --- /dev/null +++ b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java @@ -0,0 +1,81 @@ +/* + * Copyright 2024 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.httpjson; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; + +import ch.qos.logback.classic.Level; +import com.google.api.gax.httpjson.ApiMethodDescriptor.MethodType; +import com.google.api.gax.logging.LogData; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class HttpJsonLoggingInterceptorTest { + + private static final Logger LOGGER = + LoggerFactory.getLogger(HttpJsonLoggingInterceptorTest.class); + + @SuppressWarnings("unchecked") + private static final ApiMethodDescriptor method = + ApiMethodDescriptor.newBuilder() + .setType(MethodType.UNARY) + .setRequestFormatter(mock(HttpRequestFormatter.class)) + .setRequestFormatter(mock(HttpRequestFormatter.class)) + .setFullMethodName("FakeClient/fake-method") + .setResponseParser(mock(HttpResponseParser.class)) + .build(); + + @Test + void testLogRequestInfo() { + + TestAppender testAppender = setupTestLogger(HttpJsonLoggingInterceptorTest.class); + HttpJsonLoggingInterceptor interceptor = new HttpJsonLoggingInterceptor(); + interceptor.logRequestInfo(method, "fake.endpoint", LogData.builder(), LOGGER); + + Assertions.assertEquals(1, testAppender.events.size()); + assertEquals(Level.INFO, testAppender.events.get(0).getLevel()); + assertEquals( + "{\"request.url\":\"fake.endpoint\",\"message\":\"Sending HTTP request\",\"rpcName\":\"FakeClient/fake-method\"}", + testAppender.events.get(0).getMessage()); + testAppender.stop(); + } + + private TestAppender setupTestLogger(Class clazz) { + TestAppender testAppender = new TestAppender(); + testAppender.start(); + Logger logger = LoggerFactory.getLogger(clazz); + ((ch.qos.logback.classic.Logger) logger).addAppender(testAppender); + return testAppender; + } +} diff --git a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/TestAppender.java b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/TestAppender.java new file mode 100644 index 0000000000..c89ebfb3b7 --- /dev/null +++ b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/TestAppender.java @@ -0,0 +1,52 @@ +/* + * Copyright 2024 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.httpjson; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import java.util.ArrayList; +import java.util.List; + +/** Logback appender used to set up tests. */ +public class TestAppender extends AppenderBase { + public List events = new ArrayList<>(); + + @Override + protected void append(ILoggingEvent eventObject) { + // triggering Logback to capture the current MDC context and store it with the log event + eventObject.getMDCPropertyMap(); + events.add(eventObject); + } + + public void clearEvents() { + events.clear(); + } +} diff --git a/gax-java/gax-httpjson/src/test/resources/logback-test.xml b/gax-java/gax-httpjson/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..8a6dc9b23d --- /dev/null +++ b/gax-java/gax-httpjson/src/test/resources/logback-test.xml @@ -0,0 +1,6 @@ + + + + + + From 7901d8a31c5d68d7736d55a25e7ce1ce9fdeed64 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 11 Dec 2024 10:01:40 -0500 Subject: [PATCH 40/92] add log response test to interceptors. --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 18 +++++++++--------- .../gax/grpc/GrpcLoggingInterceptorTest.java | 16 +++++++++++++++- .../httpjson/HttpJsonLoggingInterceptor.java | 16 ++++++++-------- .../HttpJsonLoggingInterceptorTest.java | 15 +++++++++++++++ 4 files changed, 47 insertions(+), 18 deletions(-) diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index 133b4dcc90..9756acaee1 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -85,7 +85,7 @@ public void onMessage(RespT message) { @Override public void onClose(Status status, Metadata trailers) { try { - logResponse(status.getCode().toString(), logDataBuilder); + logResponse(status, logDataBuilder, LOGGER); } finally { logDataBuilder = null; // release resource } @@ -154,22 +154,22 @@ void recordResponsePayload(RespT message, LogData.Builder logDataBuilder } } - void logResponse(String statusCode, LogData.Builder logDataBuilder) { + void logResponse(Status status, LogData.Builder logDataBuilder, Logger logger) { try { - if (LOGGER.isInfoEnabled()) { - logDataBuilder.responseStatus(statusCode); + if (logger.isInfoEnabled()) { + logDataBuilder.responseStatus(status.getCode().toString()); } - if (LOGGER.isInfoEnabled() && !LOGGER.isDebugEnabled()) { + if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { Map responseData = logDataBuilder.build().toMapResponse(); - LoggingUtils.logWithMDC(LOGGER, Level.INFO, responseData, "Received Grpc response"); + LoggingUtils.logWithMDC(logger, Level.INFO, responseData, "Received Grpc response"); } - if (LOGGER.isDebugEnabled()) { + if (logger.isDebugEnabled()) { Map responsedDetailsMap = logDataBuilder.build().toMapResponse(); - LoggingUtils.logWithMDC(LOGGER, Level.DEBUG, responsedDetailsMap, "Received Grpc response"); + LoggingUtils.logWithMDC(logger, Level.DEBUG, responsedDetailsMap, "Received Grpc response"); } } catch (Exception e) { - LOGGER.error("Error logging request response", e); + logger.error("Error logging request response", e); } } diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java index 423221db2d..e15a69db2d 100644 --- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java +++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java @@ -115,7 +115,7 @@ void testInterceptor_responseListener() { // --- Verify that the response listener's methods were called --- verify(interceptor).recordResponseHeaders(eq(responseHeaders), any(LogData.Builder.class)); verify(interceptor).recordResponsePayload(any(), any(LogData.Builder.class)); - verify(interceptor).logResponse(eq(status.getCode().toString()), any(LogData.Builder.class)); + verify(interceptor).logResponse(eq(status), any(LogData.Builder.class), any(Logger.class)); } @Test @@ -133,6 +133,20 @@ void testLogRequestInfo() { testAppender.stop(); } + @Test + void TestLogResponseInfo() { + TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptorTest.class); + GrpcLoggingInterceptor interceptor = new GrpcLoggingInterceptor(); + interceptor.logResponse(Status.CANCELLED, LogData.builder(), LOGGER); + + Assertions.assertEquals(1, testAppender.events.size()); + assertEquals(Level.INFO, testAppender.events.get(0).getLevel()); + assertEquals( + "{\"response.status\":\"CANCELLED\",\"message\":\"Received Grpc response\"}", + testAppender.events.get(0).getMessage()); + testAppender.stop(); + } + private TestAppender setupTestLogger(Class clazz) { TestAppender testAppender = new TestAppender(); testAppender.start(); diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index cb21e54056..9b44a971c8 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -86,7 +86,7 @@ public void onMessage(RespT message) { @Override public void onClose(int statusCode, HttpJsonMetadata trailers) { try { - logResponse(statusCode, logDataBuilder); + logResponse(statusCode, logDataBuilder, LOGGER); } finally { logDataBuilder = null; // release resource } @@ -166,21 +166,21 @@ private void recordResponsePayload(RespT message, LogData.Builder logDat } } - private void logResponse(int statusCode, LogData.Builder logDataBuilder) { + void logResponse(int statusCode, LogData.Builder logDataBuilder, Logger logger) { try { - if (LOGGER.isInfoEnabled()) { + if (logger.isInfoEnabled()) { logDataBuilder.responseStatus(String.valueOf(statusCode)); } - if (LOGGER.isInfoEnabled() && !LOGGER.isDebugEnabled()) { + if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { Map responseData = logDataBuilder.build().toMapResponse(); - LoggingUtils.logWithMDC(LOGGER, Level.INFO, responseData, "Received HTTP response"); + LoggingUtils.logWithMDC(logger, Level.INFO, responseData, "Received HTTP response"); } - if (LOGGER.isDebugEnabled()) { + if (logger.isDebugEnabled()) { Map responsedDetailsMap = logDataBuilder.build().toMapResponse(); - LoggingUtils.logWithMDC(LOGGER, Level.DEBUG, responsedDetailsMap, "Received HTTP response"); + LoggingUtils.logWithMDC(logger, Level.DEBUG, responsedDetailsMap, "Received HTTP response"); } } catch (Exception e) { - LOGGER.error("Error logging request response", e); + logger.error("Error logging request response", e); } } diff --git a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java index 76546d9d1d..0966407fba 100644 --- a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java +++ b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java @@ -71,6 +71,21 @@ void testLogRequestInfo() { testAppender.stop(); } + @Test + void testLogResponseInfo() { + + TestAppender testAppender = setupTestLogger(HttpJsonLoggingInterceptorTest.class); + HttpJsonLoggingInterceptor interceptor = new HttpJsonLoggingInterceptor(); + interceptor.logResponse(200, LogData.builder(), LOGGER); + + Assertions.assertEquals(1, testAppender.events.size()); + assertEquals(Level.INFO, testAppender.events.get(0).getLevel()); + assertEquals( + "{\"response.status\":\"200\",\"message\":\"Received HTTP response\"}", + testAppender.events.get(0).getMessage()); + testAppender.stop(); + } + private TestAppender setupTestLogger(Class clazz) { TestAppender testAppender = new TestAppender(); testAppender.start(); From 20f9f5a7334fd480bd8ea11746212612cefe4d0c Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 11 Dec 2024 10:09:45 -0500 Subject: [PATCH 41/92] remove redundant code. --- .../com/google/api/gax/grpc/GrpcLoggingInterceptor.java | 7 +------ .../api/gax/httpjson/HttpJsonLoggingInterceptor.java | 6 +----- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index 9756acaee1..4950478158 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -84,11 +84,7 @@ public void onMessage(RespT message) { @Override public void onClose(Status status, Metadata trailers) { - try { - logResponse(status, logDataBuilder, LOGGER); - } finally { - logDataBuilder = null; // release resource - } + logResponse(status, logDataBuilder, LOGGER); super.onClose(status, trailers); } }; @@ -156,7 +152,6 @@ void recordResponsePayload(RespT message, LogData.Builder logDataBuilder void logResponse(Status status, LogData.Builder logDataBuilder, Logger logger) { try { - if (logger.isInfoEnabled()) { logDataBuilder.responseStatus(status.getCode().toString()); } diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index 9b44a971c8..cdf4521f9c 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -85,11 +85,7 @@ public void onMessage(RespT message) { @Override public void onClose(int statusCode, HttpJsonMetadata trailers) { - try { - logResponse(statusCode, logDataBuilder, LOGGER); - } finally { - logDataBuilder = null; // release resource - } + logResponse(statusCode, logDataBuilder, LOGGER); super.onClose(statusCode, trailers); } }; From 172701dfb642312366b693a4be37cdf8b68b5efb Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 11 Dec 2024 10:19:33 -0500 Subject: [PATCH 42/92] add slf4j and test dep to dependencies.properties. --- gax-java/dependencies.properties | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gax-java/dependencies.properties b/gax-java/dependencies.properties index 12224344a4..16690f92e6 100644 --- a/gax-java/dependencies.properties +++ b/gax-java/dependencies.properties @@ -76,6 +76,7 @@ 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:graal-sdk:22.3.5 +maven_org_slf4j_slf4j_api=org.slf4j:slf4j-api:2.0.16 # Testing maven artifacts maven.junit_junit=junit:junit:4.13.2 @@ -88,3 +89,5 @@ maven.net_bytebuddy_byte_buddy=net.bytebuddy:byte-buddy:1.15.10 maven.org_objenesis_objenesis=org.objenesis:objenesis:2.6 maven.org_junit_jupiter_junit_jupiter_api=org.junit.jupiter:junit-jupiter-api:5.11.3 maven.org_junit_jupiter_junit_jupiter_params=org.junit.jupiter:junit-jupiter-params:5.11.3 +maven.ch_qos_logback_logback_classic=ch.qos.logback:logback-classic:1.3.14 +maven.ch_qos_logback_logback_core=ch.qos.logback:logback-core:1.3.14 \ No newline at end of file From a8d2f207da99876b72795ef0868ec53d836a5687 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 11 Dec 2024 13:05:38 -0500 Subject: [PATCH 43/92] make requestPayload also as JsonElement in LogData. --- .../com/google/api/gax/grpc/GrpcLoggingInterceptor.java | 2 +- .../google/api/gax/httpjson/HttpJsonLoggingInterceptor.java | 2 +- .../src/main/java/com/google/api/gax/logging/LogData.java | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index 4950478158..2a9953a91b 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -171,7 +171,7 @@ void logResponse(Status status, LogData.Builder logDataBuilder, Logger logger) { void logRequestDetails(RespT message, LogData.Builder logDataBuilder) { try { if (LOGGER.isDebugEnabled()) { - logDataBuilder.requestPayload(GSON.toJson(message)); + logDataBuilder.requestPayload(GSON.toJsonTree(message)); Map requestDetailsMap = logDataBuilder.build().toMapRequest(); LoggingUtils.logWithMDC( LOGGER, Level.DEBUG, requestDetailsMap, "Sending gRPC request: request payload"); diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index cdf4521f9c..a465ab471a 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -183,7 +183,7 @@ void logResponse(int statusCode, LogData.Builder logDataBuilder, Logger logger) private void logRequestDetails(RespT message, LogData.Builder logDataBuilder) { try { if (LOGGER.isDebugEnabled()) { - logDataBuilder.requestPayload(GSON.toJson(message)); + logDataBuilder.requestPayload(GSON.toJsonTree(message)); Map requestDetailsMap = logDataBuilder.build().toMapRequest(); LoggingUtils.logWithMDC( LOGGER, Level.DEBUG, requestDetailsMap, "Sending HTTP request: request payload"); diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java index db4767eb20..57751dc6d0 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java @@ -56,7 +56,7 @@ public abstract class LogData { public abstract String requestHeaders(); @Nullable - public abstract String requestPayload(); + public abstract JsonElement requestPayload(); @Nullable public abstract String responseStatus(); @@ -87,7 +87,7 @@ public abstract static class Builder { public abstract Builder requestHeaders(String requestHeaders); - public abstract Builder requestPayload(String requestPayload); + public abstract Builder requestPayload(JsonElement requestPayload); public abstract Builder responseStatus(String responseStatus); @@ -119,7 +119,7 @@ public Map toMapRequest() { map.put("request.headers", requestHeaders()); } if (requestPayload() != null) { - map.put("request.payload", requestPayload()); + map.put("request.payload", gson.toJson(requestPayload())); } if (httpMethod() != null) { map.put("request.method", httpMethod()); From df97834e51882bf0f9ceb946248785d07988b1b6 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Thu, 12 Dec 2024 17:38:01 -0500 Subject: [PATCH 44/92] fix typo in dependencies.properties. --- gax-java/dependencies.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gax-java/dependencies.properties b/gax-java/dependencies.properties index 16690f92e6..e1a3acdcc1 100644 --- a/gax-java/dependencies.properties +++ b/gax-java/dependencies.properties @@ -76,7 +76,7 @@ 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:graal-sdk:22.3.5 -maven_org_slf4j_slf4j_api=org.slf4j:slf4j-api:2.0.16 +maven.org_slf4j_slf4j_api=org.slf4j:slf4j-api:2.0.16 # Testing maven artifacts maven.junit_junit=junit:junit:4.13.2 From 32dbd0eeba8aa5303a508d3f45a75c2f59809df1 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Thu, 12 Dec 2024 17:45:10 -0500 Subject: [PATCH 45/92] add test assertion to verify logger not enabled at levels when env not set. also some cleanups. --- .../api/gax/logging/LoggingUtilsTest.java | 60 +++++-------------- 1 file changed, 15 insertions(+), 45 deletions(-) diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java index 722a2d5a22..d53bd0bc02 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java @@ -30,24 +30,18 @@ package com.google.api.gax.logging; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.encoder.PatternLayoutEncoder; import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.ConsoleAppender; import com.google.api.gax.logging.LoggingUtils.LoggerFactoryProvider; import com.google.api.gax.rpc.internal.EnvironmentProvider; 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.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.slf4j.ILoggerFactory; @@ -60,32 +54,6 @@ class LoggingUtilsTest { private static final Logger LOGGER = LoggerFactory.getLogger(LoggingUtilsTest.class); private EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class); - @BeforeEach - void setup() { - EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class); - - // need to setup a ConsoleAppender and attach to root logger because TestAppender - // does not correctly capture MDC info - LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); - - PatternLayoutEncoder patternLayoutEncoder = new PatternLayoutEncoder(); - patternLayoutEncoder.setPattern("%-4relative [%thread] %-5level %logger{35} - %msg%n"); - patternLayoutEncoder.setContext(loggerContext); - - patternLayoutEncoder.start(); - - ConsoleAppender consoleAppender = new ConsoleAppender<>(); - consoleAppender.setEncoder(patternLayoutEncoder); - - consoleAppender.setContext(loggerContext); - consoleAppender.setName("CONSOLE"); - - consoleAppender.start(); - - ch.qos.logback.classic.Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); - rootLogger.addAppender(consoleAppender); - } - @AfterEach void tearDown() { LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); @@ -97,8 +65,8 @@ void testGetLogger_loggingEnabled_slf4jBindingPresent() { Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("true"); LoggingUtils.setEnvironmentProvider(envProvider); Logger logger = LoggingUtils.getLogger(LoggingUtilsTest.class); - assertTrue(logger instanceof org.slf4j.Logger); - assertNotEquals(logger.getClass(), NOPLogger.class); + Assertions.assertInstanceOf(Logger.class, logger); + Assertions.assertNotEquals(NOPLogger.class, logger.getClass()); } @Test @@ -107,7 +75,9 @@ void testGetLogger_loggingDisabled() { LoggingUtils.setEnvironmentProvider(envProvider); Logger logger = LoggingUtils.getLogger(LoggingUtilsTest.class); - assertEquals(NOPLogger.class, logger.getClass()); + Assertions.assertEquals(NOPLogger.class, logger.getClass()); + Assertions.assertFalse(logger.isInfoEnabled()); + Assertions.assertFalse(logger.isDebugEnabled()); } @Test @@ -125,26 +95,26 @@ void testGetLogger_loggingEnabled_noBinding() { Logger logger = LoggingUtils.getLogger(LoggingUtilsTest.class, mockLoggerFactoryProvider); // Assert that the returned logger is a NOPLogger - assertTrue(logger instanceof org.slf4j.helpers.NOPLogger); + Assertions.assertInstanceOf(NOPLogger.class, logger); } @Test void testIsLoggingEnabled_true() { Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("true"); LoggingUtils.setEnvironmentProvider(envProvider); - assertTrue(LoggingUtils.isLoggingEnabled()); + Assertions.assertTrue(LoggingUtils.isLoggingEnabled()); Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("TRUE"); LoggingUtils.setEnvironmentProvider(envProvider); - assertTrue(LoggingUtils.isLoggingEnabled()); + Assertions.assertTrue(LoggingUtils.isLoggingEnabled()); Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("True"); LoggingUtils.setEnvironmentProvider(envProvider); - assertTrue(LoggingUtils.isLoggingEnabled()); + Assertions.assertTrue(LoggingUtils.isLoggingEnabled()); } @Test void testIsLoggingEnabled_defaultToFalse() { LoggingUtils.setEnvironmentProvider(envProvider); - assertFalse(LoggingUtils.isLoggingEnabled()); + Assertions.assertFalse(LoggingUtils.isLoggingEnabled()); } private TestAppender setupTestLogger(Class clazz) { @@ -156,20 +126,20 @@ private TestAppender setupTestLogger(Class clazz) { } @Test - public void testLogWithMDC_slf4jLogger() { + void testLogWithMDC_slf4jLogger() { TestAppender testAppender = setupTestLogger(LoggingUtilsTest.class); Map contextMap = new HashMap<>(); contextMap.put("key", "value"); LoggingUtils.logWithMDC(LOGGER, org.slf4j.event.Level.DEBUG, contextMap, "test message"); - assertEquals(1, testAppender.events.size()); - assertEquals( + Assertions.assertEquals(1, testAppender.events.size()); + Assertions.assertEquals( "{\"message\":\"test message\",\"key\":\"value\"}", testAppender.events.get(0).getFormattedMessage()); // Verify MDC content ILoggingEvent event = testAppender.events.get(0); - assertEquals("value", event.getMDCPropertyMap().get("key")); + Assertions.assertEquals("value", event.getMDCPropertyMap().get("key")); testAppender.stop(); } } From 37eb391b5fd73572c0f29f9d3202c95b70b5bf2e Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Fri, 13 Dec 2024 15:50:52 -0500 Subject: [PATCH 46/92] attempt to add slf4j to bazel BUILD. Not solving bazel build for it test run yet. --- gax-java/gax/BUILD.bazel | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gax-java/gax/BUILD.bazel b/gax-java/gax/BUILD.bazel index 5dd3ff96bd..6a7a5a4cd5 100644 --- a/gax-java/gax/BUILD.bazel +++ b/gax-java/gax/BUILD.bazel @@ -28,7 +28,8 @@ _COMPILE_DEPS = [ "@com_google_code_gson_gson//jar", "@com_google_guava_failureaccess//jar", "@javax_annotation_javax_annotation_api//jar", - "@org_graalvm_sdk//jar" + "@org_graalvm_sdk//jar", + "@maven//:org_slf4j_slf4j_api", ] _TEST_COMPILE_DEPS = [ From f95423b27d36ac01a66ea0bff8c6dfc46ec43798 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Mon, 23 Dec 2024 16:27:14 -0500 Subject: [PATCH 47/92] grpc logger interceptor: cleanup logic, add to catch NSM error. --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 68 +++++++++---------- .../gax/grpc/GrpcLoggingInterceptorTest.java | 8 ++- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index 2a9953a91b..aa48c2deeb 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -66,19 +66,18 @@ public ClientCall interceptCall( @Override public void start(Listener responseListener, Metadata headers) { - logRequestInfo(method, logDataBuilder, LOGGER); - recordRequestHeaders(headers, logDataBuilder); + recordServiceRpcAndRequestHeaders(method, headers, logDataBuilder, LOGGER); SimpleForwardingClientCallListener responseLoggingListener = new SimpleForwardingClientCallListener(responseListener) { @Override public void onHeaders(Metadata headers) { - recordResponseHeaders(headers, logDataBuilder); + recordResponseHeaders(headers, logDataBuilder, LOGGER); super.onHeaders(headers); } @Override public void onMessage(RespT message) { - recordResponsePayload(message, logDataBuilder); + recordResponsePayload(message, logDataBuilder, LOGGER); super.onMessage(message); } @@ -94,7 +93,7 @@ public void onClose(Status status, Metadata trailers) { @Override public void sendMessage(ReqT message) { - logRequestDetails(message, logDataBuilder); + logRequest(message, logDataBuilder, LOGGER); super.sendMessage(message); } }; @@ -102,51 +101,41 @@ public void sendMessage(ReqT message) { // Helper methods for logging // some duplications with http equivalent to avoid exposing as public method for now - void logRequestInfo( - MethodDescriptor method, LogData.Builder logDataBuilder, Logger logger) { + + private void recordServiceRpcAndRequestHeaders( + MethodDescriptor method, Metadata headers, LogData.Builder logDataBuilder, + Logger logger) { try { if (logger.isInfoEnabled()) { logDataBuilder.serviceName(method.getServiceName()).rpcName(method.getFullMethodName()); - - if (!logger.isDebugEnabled()) { - LoggingUtils.logWithMDC( - logger, Level.INFO, logDataBuilder.build().toMapRequest(), "Sending gRPC request"); - } } - } catch (Exception e) { - logger.error("Error logging request info (and headers)", e); - } - } - - private void recordRequestHeaders(Metadata headers, LogData.Builder logDataBuilder) { - try { - if (LOGGER.isDebugEnabled()) { + if (logger.isDebugEnabled()) { JsonObject requestHeaders = mapHeadersToJsonObject(headers); logDataBuilder.requestHeaders(GSON.toJson(requestHeaders)); } - } catch (Exception e) { - LOGGER.error("Error recording request headers", e); + } catch (Exception | NoSuchMethodError e) { + // should fail silently } } - void recordResponseHeaders(Metadata headers, LogData.Builder logDataBuilder) { + void recordResponseHeaders(Metadata headers, LogData.Builder logDataBuilder, Logger logger) { try { - if (LOGGER.isDebugEnabled()) { + if (logger.isDebugEnabled()) { JsonObject responseHeaders = mapHeadersToJsonObject(headers); logDataBuilder.responseHeaders(GSON.toJson(responseHeaders)); } - } catch (Exception e) { - LOGGER.error("Error recording response headers", e); + } catch (Exception | NoSuchMethodError e) { + // should fail silently } } - void recordResponsePayload(RespT message, LogData.Builder logDataBuilder) { + void recordResponsePayload(RespT message, LogData.Builder logDataBuilder, Logger logger) { try { - if (LOGGER.isDebugEnabled()) { + if (logger.isDebugEnabled()) { logDataBuilder.responsePayload(GSON.toJsonTree(message)); } - } catch (Exception e) { - LOGGER.error("Error recording response payload", e); + } catch (Exception | NoSuchMethodError e) { + // should fail silently } } @@ -163,21 +152,26 @@ void logResponse(Status status, LogData.Builder logDataBuilder, Logger logger) { Map responsedDetailsMap = logDataBuilder.build().toMapResponse(); LoggingUtils.logWithMDC(logger, Level.DEBUG, responsedDetailsMap, "Received Grpc response"); } - } catch (Exception e) { - logger.error("Error logging request response", e); + } catch (Exception | NoSuchMethodError e) { + // should fail silently } } - void logRequestDetails(RespT message, LogData.Builder logDataBuilder) { + void logRequest(RespT message, LogData.Builder logDataBuilder, Logger logger) { try { - if (LOGGER.isDebugEnabled()) { + + if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { + LoggingUtils.logWithMDC( + logger, Level.INFO, logDataBuilder.build().toMapRequest(), "Sending gRPC request"); + } + if (logger.isDebugEnabled()) { logDataBuilder.requestPayload(GSON.toJsonTree(message)); Map requestDetailsMap = logDataBuilder.build().toMapRequest(); LoggingUtils.logWithMDC( - LOGGER, Level.DEBUG, requestDetailsMap, "Sending gRPC request: request payload"); + logger, Level.DEBUG, requestDetailsMap, "Sending gRPC request: request payload"); } - } catch (Exception e) { - LOGGER.error("Error logging request details", e); + } catch (Exception | NoSuchMethodError e) { + // should fail silently } } diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java index e15a69db2d..93950f51d1 100644 --- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java +++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java @@ -113,8 +113,8 @@ void testInterceptor_responseListener() { interceptor.currentListener.onClose(status, new Metadata()); // --- Verify that the response listener's methods were called --- - verify(interceptor).recordResponseHeaders(eq(responseHeaders), any(LogData.Builder.class)); - verify(interceptor).recordResponsePayload(any(), any(LogData.Builder.class)); + verify(interceptor).recordResponseHeaders(eq(responseHeaders), any(LogData.Builder.class), any(Logger.class)); + verify(interceptor).recordResponsePayload(any(), any(LogData.Builder.class), any(Logger.class)); verify(interceptor).logResponse(eq(status), any(LogData.Builder.class), any(Logger.class)); } @@ -123,7 +123,9 @@ void testLogRequestInfo() { TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptorTest.class); GrpcLoggingInterceptor interceptor = new GrpcLoggingInterceptor(); - interceptor.logRequestInfo(method, LogData.builder(), LOGGER); + LogData.Builder logData = LogData.builder().serviceName("FakeClient") + .rpcName("FakeClient/fake-method"); + interceptor.logRequest(method, logData, LOGGER); Assertions.assertEquals(1, testAppender.events.size()); assertEquals(Level.INFO, testAppender.events.get(0).getLevel()); From 8e01aa024a784345ffeae88dbd0d428bc9d77ae6 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Mon, 23 Dec 2024 17:28:20 -0500 Subject: [PATCH 48/92] lint. fix binary headers. --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 130 +++++++++--------- .../gax/grpc/GrpcLoggingInterceptorTest.java | 35 ++++- 2 files changed, 100 insertions(+), 65 deletions(-) diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index aa48c2deeb..410441514a 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -103,73 +103,78 @@ public void sendMessage(ReqT message) { // some duplications with http equivalent to avoid exposing as public method for now private void recordServiceRpcAndRequestHeaders( - MethodDescriptor method, Metadata headers, LogData.Builder logDataBuilder, + MethodDescriptor method, + Metadata headers, + LogData.Builder logDataBuilder, Logger logger) { - try { - if (logger.isInfoEnabled()) { - logDataBuilder.serviceName(method.getServiceName()).rpcName(method.getFullMethodName()); - } - if (logger.isDebugEnabled()) { - JsonObject requestHeaders = mapHeadersToJsonObject(headers); - logDataBuilder.requestHeaders(GSON.toJson(requestHeaders)); - } - } catch (Exception | NoSuchMethodError e) { - // should fail silently - } + executeWithTryCatch( + () -> { + if (logger.isInfoEnabled()) { + logDataBuilder.serviceName(method.getServiceName()).rpcName(method.getFullMethodName()); + } + if (logger.isDebugEnabled()) { + JsonObject requestHeaders = mapHeadersToJsonObject(headers); + logDataBuilder.requestHeaders(GSON.toJson(requestHeaders)); + } + }); } void recordResponseHeaders(Metadata headers, LogData.Builder logDataBuilder, Logger logger) { - try { - if (logger.isDebugEnabled()) { - JsonObject responseHeaders = mapHeadersToJsonObject(headers); - logDataBuilder.responseHeaders(GSON.toJson(responseHeaders)); - } - } catch (Exception | NoSuchMethodError e) { - // should fail silently - } + executeWithTryCatch( + () -> { + if (logger.isDebugEnabled()) { + JsonObject responseHeaders = mapHeadersToJsonObject(headers); + logDataBuilder.responseHeaders(GSON.toJson(responseHeaders)); + } + }); } void recordResponsePayload(RespT message, LogData.Builder logDataBuilder, Logger logger) { - try { - if (logger.isDebugEnabled()) { - logDataBuilder.responsePayload(GSON.toJsonTree(message)); - } - } catch (Exception | NoSuchMethodError e) { - // should fail silently - } + executeWithTryCatch( + () -> { + if (logger.isDebugEnabled()) { + logDataBuilder.responsePayload(GSON.toJsonTree(message)); + } + }); } void logResponse(Status status, LogData.Builder logDataBuilder, Logger logger) { - try { - if (logger.isInfoEnabled()) { - logDataBuilder.responseStatus(status.getCode().toString()); - } - if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { - Map responseData = logDataBuilder.build().toMapResponse(); - LoggingUtils.logWithMDC(logger, Level.INFO, responseData, "Received Grpc response"); - } - if (logger.isDebugEnabled()) { - Map responsedDetailsMap = logDataBuilder.build().toMapResponse(); - LoggingUtils.logWithMDC(logger, Level.DEBUG, responsedDetailsMap, "Received Grpc response"); - } - } catch (Exception | NoSuchMethodError e) { - // should fail silently - } + executeWithTryCatch( + () -> { + if (logger.isInfoEnabled()) { + logDataBuilder.responseStatus(status.getCode().toString()); + } + if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { + Map responseData = logDataBuilder.build().toMapResponse(); + LoggingUtils.logWithMDC(logger, Level.INFO, responseData, "Received Grpc response"); + } + if (logger.isDebugEnabled()) { + Map responsedDetailsMap = logDataBuilder.build().toMapResponse(); + LoggingUtils.logWithMDC( + logger, Level.DEBUG, responsedDetailsMap, "Received Grpc response"); + } + }); } void logRequest(RespT message, LogData.Builder logDataBuilder, Logger logger) { - try { + executeWithTryCatch( + () -> { + if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { + LoggingUtils.logWithMDC( + logger, Level.INFO, logDataBuilder.build().toMapRequest(), "Sending gRPC request"); + } + if (logger.isDebugEnabled()) { + logDataBuilder.requestPayload(GSON.toJsonTree(message)); + Map requestDetailsMap = logDataBuilder.build().toMapRequest(); + LoggingUtils.logWithMDC( + logger, Level.DEBUG, requestDetailsMap, "Sending gRPC request: request payload"); + } + }); + } - if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { - LoggingUtils.logWithMDC( - logger, Level.INFO, logDataBuilder.build().toMapRequest(), "Sending gRPC request"); - } - if (logger.isDebugEnabled()) { - logDataBuilder.requestPayload(GSON.toJsonTree(message)); - Map requestDetailsMap = logDataBuilder.build().toMapRequest(); - LoggingUtils.logWithMDC( - logger, Level.DEBUG, requestDetailsMap, "Sending gRPC request: request payload"); - } + static void executeWithTryCatch(Runnable action) { + try { + action.run(); } catch (Exception | NoSuchMethodError e) { // should fail silently } @@ -177,15 +182,16 @@ void logRequest(RespT message, LogData.Builder logDataBuilder, Logger lo private static JsonObject mapHeadersToJsonObject(Metadata headers) { JsonObject jsonHeaders = new JsonObject(); - headers - .keys() - .forEach( - key -> { - Metadata.Key metadataKey = - Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER); - String headerValue = headers.get(metadataKey); - jsonHeaders.addProperty(key, headerValue); - }); + for (String key : headers.keys()) { + + if (key.endsWith(Metadata.BINARY_HEADER_SUFFIX)) { + continue; + } + Metadata.Key metadataKey = Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER); + String headerValue = headers.get(metadataKey); + + jsonHeaders.addProperty(key, headerValue); + } return jsonHeaders; } } diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java index 93950f51d1..a56af5f679 100644 --- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java +++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java @@ -113,7 +113,8 @@ void testInterceptor_responseListener() { interceptor.currentListener.onClose(status, new Metadata()); // --- Verify that the response listener's methods were called --- - verify(interceptor).recordResponseHeaders(eq(responseHeaders), any(LogData.Builder.class), any(Logger.class)); + verify(interceptor) + .recordResponseHeaders(eq(responseHeaders), any(LogData.Builder.class), any(Logger.class)); verify(interceptor).recordResponsePayload(any(), any(LogData.Builder.class), any(Logger.class)); verify(interceptor).logResponse(eq(status), any(LogData.Builder.class), any(Logger.class)); } @@ -123,8 +124,8 @@ void testLogRequestInfo() { TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptorTest.class); GrpcLoggingInterceptor interceptor = new GrpcLoggingInterceptor(); - LogData.Builder logData = LogData.builder().serviceName("FakeClient") - .rpcName("FakeClient/fake-method"); + LogData.Builder logData = + LogData.builder().serviceName("FakeClient").rpcName("FakeClient/fake-method"); interceptor.logRequest(method, logData, LOGGER); Assertions.assertEquals(1, testAppender.events.size()); @@ -156,4 +157,32 @@ private TestAppender setupTestLogger(Class clazz) { ((ch.qos.logback.classic.Logger) logger).addAppender(testAppender); return testAppender; } + + @Test + void testExecuteWithTryCatch_NoException() { + Runnable action = Mockito.mock(Runnable.class); + GrpcLoggingInterceptor.executeWithTryCatch(action); + // Verify that the action was executed + Mockito.verify(action, Mockito.times(1)).run(); + } + + @Test + void testExecuteWithTryCatch_WithException() { + Runnable action = Mockito.mock(Runnable.class); + Mockito.doThrow(new RuntimeException("Test Exception")).when(action).run(); + GrpcLoggingInterceptor.executeWithTryCatch(action); + // Verify that the action was executed (despite the exception) + Mockito.verify(action, Mockito.times(1)).run(); + // No exception should be thrown by executeWithTryCatch + } + + @Test + void testExecuteWithTryCatch_WithNoSuchMethodError() { + Runnable action = Mockito.mock(Runnable.class); + Mockito.doThrow(new NoSuchMethodError("Test Error")).when(action).run(); + GrpcLoggingInterceptor.executeWithTryCatch(action); + // Verify that the action was executed (despite the error) + Mockito.verify(action, Mockito.times(1)).run(); + // No error should be thrown by executeWithTryCatch + } } From 73210fa54dc64d6a44562d46c95e2038582870c2 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Thu, 26 Dec 2024 16:50:04 -0500 Subject: [PATCH 49/92] refactor helper methods into LoggingUtils. update to use addKeyValue. --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 120 ++++--------- .../gax/grpc/GrpcLoggingInterceptorTest.java | 158 +++++++++--------- .../httpjson/HttpJsonLoggingInterceptor.java | 123 +++----------- .../HttpJsonLoggingInterceptorTest.java | 80 +++++---- gax-java/gax/pom.xml | 4 + .../com/google/api/gax/logging/LogData.java | 29 ++-- .../google/api/gax/logging/LoggingUtils.java | 138 +++++++++++++-- .../google/api/gax/logging/LogDataTest.java | 9 +- .../api/gax/logging/LoggingUtilsTest.java | 2 +- 9 files changed, 311 insertions(+), 352 deletions(-) diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index 410441514a..6b0d55c5d7 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -30,11 +30,16 @@ 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.LoggingUtils; -import com.google.gson.Gson; -import com.google.gson.JsonObject; import io.grpc.CallOptions; import io.grpc.Channel; import io.grpc.ClientCall; @@ -44,15 +49,14 @@ import io.grpc.Metadata; import io.grpc.MethodDescriptor; import io.grpc.Status; +import java.util.HashMap; import java.util.Map; import org.slf4j.Logger; -import org.slf4j.event.Level; @InternalApi public class GrpcLoggingInterceptor implements ClientInterceptor { private static final Logger LOGGER = LoggingUtils.getLogger(GrpcLoggingInterceptor.class); - private static final Gson GSON = new Gson(); ClientCall.Listener currentListener; // expose for test setup @@ -66,12 +70,18 @@ public ClientCall interceptCall( @Override public void start(Listener responseListener, Metadata headers) { - recordServiceRpcAndRequestHeaders(method, headers, logDataBuilder, LOGGER); + recordServiceRpcAndRequestHeaders( + method.getServiceName(), + method.getFullMethodName(), + null, + metadataHeadersToMap(headers), + logDataBuilder, + LOGGER); SimpleForwardingClientCallListener responseLoggingListener = new SimpleForwardingClientCallListener(responseListener) { @Override public void onHeaders(Metadata headers) { - recordResponseHeaders(headers, logDataBuilder, LOGGER); + recordResponseHeaders(metadataHeadersToMap(headers), logDataBuilder, LOGGER); super.onHeaders(headers); } @@ -83,7 +93,7 @@ public void onMessage(RespT message) { @Override public void onClose(Status status, Metadata trailers) { - logResponse(status, logDataBuilder, LOGGER); + logResponse(status.getCode().toString(), logDataBuilder, LOGGER); super.onClose(status, trailers); } }; @@ -100,98 +110,24 @@ public void sendMessage(ReqT message) { } // Helper methods for logging - // some duplications with http equivalent to avoid exposing as public method for now + private static Map metadataHeadersToMap(Metadata headers) { - private void recordServiceRpcAndRequestHeaders( - MethodDescriptor method, - Metadata headers, - LogData.Builder logDataBuilder, - Logger logger) { + Map headersMap = new HashMap<>(); executeWithTryCatch( () -> { - if (logger.isInfoEnabled()) { - logDataBuilder.serviceName(method.getServiceName()).rpcName(method.getFullMethodName()); - } - if (logger.isDebugEnabled()) { - JsonObject requestHeaders = mapHeadersToJsonObject(headers); - logDataBuilder.requestHeaders(GSON.toJson(requestHeaders)); - } - }); - } + for (String key : headers.keys()) { - void recordResponseHeaders(Metadata headers, LogData.Builder logDataBuilder, Logger logger) { - executeWithTryCatch( - () -> { - if (logger.isDebugEnabled()) { - JsonObject responseHeaders = mapHeadersToJsonObject(headers); - logDataBuilder.responseHeaders(GSON.toJson(responseHeaders)); - } - }); - } + if (key.endsWith(Metadata.BINARY_HEADER_SUFFIX)) { + continue; + } + Metadata.Key metadataKey = + Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER); + String headerValue = headers.get(metadataKey); - void recordResponsePayload(RespT message, LogData.Builder logDataBuilder, Logger logger) { - executeWithTryCatch( - () -> { - if (logger.isDebugEnabled()) { - logDataBuilder.responsePayload(GSON.toJsonTree(message)); + headersMap.put(key, headerValue); } }); - } - - void logResponse(Status status, LogData.Builder logDataBuilder, Logger logger) { - executeWithTryCatch( - () -> { - if (logger.isInfoEnabled()) { - logDataBuilder.responseStatus(status.getCode().toString()); - } - if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { - Map responseData = logDataBuilder.build().toMapResponse(); - LoggingUtils.logWithMDC(logger, Level.INFO, responseData, "Received Grpc response"); - } - if (logger.isDebugEnabled()) { - Map responsedDetailsMap = logDataBuilder.build().toMapResponse(); - LoggingUtils.logWithMDC( - logger, Level.DEBUG, responsedDetailsMap, "Received Grpc response"); - } - }); - } - - void logRequest(RespT message, LogData.Builder logDataBuilder, Logger logger) { - executeWithTryCatch( - () -> { - if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { - LoggingUtils.logWithMDC( - logger, Level.INFO, logDataBuilder.build().toMapRequest(), "Sending gRPC request"); - } - if (logger.isDebugEnabled()) { - logDataBuilder.requestPayload(GSON.toJsonTree(message)); - Map requestDetailsMap = logDataBuilder.build().toMapRequest(); - LoggingUtils.logWithMDC( - logger, Level.DEBUG, requestDetailsMap, "Sending gRPC request: request payload"); - } - }); - } - - static void executeWithTryCatch(Runnable action) { - try { - action.run(); - } catch (Exception | NoSuchMethodError e) { - // should fail silently - } - } - - private static JsonObject mapHeadersToJsonObject(Metadata headers) { - JsonObject jsonHeaders = new JsonObject(); - for (String key : headers.keys()) { - - if (key.endsWith(Metadata.BINARY_HEADER_SUFFIX)) { - continue; - } - Metadata.Key metadataKey = Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER); - String headerValue = headers.get(metadataKey); - jsonHeaders.addProperty(key, headerValue); - } - return jsonHeaders; + return headersMap; } } diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java index a56af5f679..13384d198e 100644 --- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java +++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java @@ -30,7 +30,7 @@ package com.google.api.gax.grpc; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -39,6 +39,7 @@ import static org.mockito.Mockito.when; import ch.qos.logback.classic.Level; +// import com.google.api.gax.grpc.GrpcLoggingInterceptor.ThrowingRunnable; import com.google.api.gax.grpc.testing.FakeMethodDescriptor; import com.google.api.gax.logging.LogData; import io.grpc.CallOptions; @@ -49,14 +50,15 @@ import io.grpc.MethodDescriptor; import io.grpc.Status; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@ExtendWith(MockitoExtension.class) class GrpcLoggingInterceptorTest { @Mock private Channel channel; @@ -65,16 +67,11 @@ class GrpcLoggingInterceptorTest { private static final MethodDescriptor method = FakeMethodDescriptor.create(); private static final Logger LOGGER = LoggerFactory.getLogger(GrpcLoggingInterceptorTest.class); - /** Sets up mocks. */ - @BeforeEach - void setUp() { - MockitoAnnotations.initMocks(this); - when(channel.newCall(Mockito.>any(), any(CallOptions.class))) - .thenReturn(call); - } @Test void testInterceptor_basic() { + when(channel.newCall(Mockito.>any(), any(CallOptions.class))) + .thenReturn(call); GrpcLoggingInterceptor interceptor = new GrpcLoggingInterceptor(); Channel intercepted = ClientInterceptors.intercept(channel, interceptor); @SuppressWarnings("unchecked") @@ -94,6 +91,8 @@ void testInterceptor_basic() { @Test void testInterceptor_responseListener() { + when(channel.newCall(Mockito.>any(), any(CallOptions.class))) + .thenReturn(call); GrpcLoggingInterceptor interceptor = spy(new GrpcLoggingInterceptor()); Channel intercepted = ClientInterceptors.intercept(channel, interceptor); @SuppressWarnings("unchecked") @@ -113,76 +112,75 @@ void testInterceptor_responseListener() { interceptor.currentListener.onClose(status, new Metadata()); // --- Verify that the response listener's methods were called --- - verify(interceptor) - .recordResponseHeaders(eq(responseHeaders), any(LogData.Builder.class), any(Logger.class)); - verify(interceptor).recordResponsePayload(any(), any(LogData.Builder.class), any(Logger.class)); - verify(interceptor).logResponse(eq(status), any(LogData.Builder.class), any(Logger.class)); - } - - @Test - void testLogRequestInfo() { - - TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptorTest.class); - GrpcLoggingInterceptor interceptor = new GrpcLoggingInterceptor(); - LogData.Builder logData = - LogData.builder().serviceName("FakeClient").rpcName("FakeClient/fake-method"); - interceptor.logRequest(method, logData, LOGGER); - - Assertions.assertEquals(1, testAppender.events.size()); - assertEquals(Level.INFO, testAppender.events.get(0).getLevel()); - assertEquals( - "{\"serviceName\":\"FakeClient\",\"message\":\"Sending gRPC request\",\"rpcName\":\"FakeClient/fake-method\"}", - testAppender.events.get(0).getMessage()); - testAppender.stop(); - } - - @Test - void TestLogResponseInfo() { - TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptorTest.class); - GrpcLoggingInterceptor interceptor = new GrpcLoggingInterceptor(); - interceptor.logResponse(Status.CANCELLED, LogData.builder(), LOGGER); - - Assertions.assertEquals(1, testAppender.events.size()); - assertEquals(Level.INFO, testAppender.events.get(0).getLevel()); - assertEquals( - "{\"response.status\":\"CANCELLED\",\"message\":\"Received Grpc response\"}", - testAppender.events.get(0).getMessage()); - testAppender.stop(); - } - - private TestAppender setupTestLogger(Class clazz) { - TestAppender testAppender = new TestAppender(); - testAppender.start(); - Logger logger = LoggerFactory.getLogger(clazz); - ((ch.qos.logback.classic.Logger) logger).addAppender(testAppender); - return testAppender; - } - - @Test - void testExecuteWithTryCatch_NoException() { - Runnable action = Mockito.mock(Runnable.class); - GrpcLoggingInterceptor.executeWithTryCatch(action); - // Verify that the action was executed - Mockito.verify(action, Mockito.times(1)).run(); + // verify(interceptor) + // .recordResponseHeaders(eq(responseHeaders), any(LogData.Builder.class), any(Logger.class)); + // // verify(interceptor).recordResponsePayload(any(), any(LogData.Builder.class), + // // any(Logger.class)); + // verify(interceptor).logResponse(eq(status), any(LogData.Builder.class), any(Logger.class)); } - @Test - void testExecuteWithTryCatch_WithException() { - Runnable action = Mockito.mock(Runnable.class); - Mockito.doThrow(new RuntimeException("Test Exception")).when(action).run(); - GrpcLoggingInterceptor.executeWithTryCatch(action); - // Verify that the action was executed (despite the exception) - Mockito.verify(action, Mockito.times(1)).run(); - // No exception should be thrown by executeWithTryCatch - } - - @Test - void testExecuteWithTryCatch_WithNoSuchMethodError() { - Runnable action = Mockito.mock(Runnable.class); - Mockito.doThrow(new NoSuchMethodError("Test Error")).when(action).run(); - GrpcLoggingInterceptor.executeWithTryCatch(action); - // Verify that the action was executed (despite the error) - Mockito.verify(action, Mockito.times(1)).run(); - // No error should be thrown by executeWithTryCatch - } + // @Test + // void testLogRequestInfo() { + // + // TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptorTest.class); + // GrpcLoggingInterceptor interceptor = new GrpcLoggingInterceptor(); + // LogData.Builder logData = + // LogData.builder().serviceName("FakeClient").rpcName("FakeClient/fake-method"); + // interceptor.logRequest(method, logData, LOGGER); + // + // Assertions.assertEquals(1, testAppender.events.size()); + // Assertions.assertEquals(Level.INFO, testAppender.events.get(0).getLevel()); + // Assertions.assertEquals( + // "{\"serviceName\":\"FakeClient\",\"message\":\"Sending gRPC request\",\"rpcName\":\"FakeClient/fake-method\"}", + // testAppender.events.get(0).getMessage()); + // testAppender.stop(); + // } + // + // @Test + // void TestLogResponseInfo() { + // TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptorTest.class); + // GrpcLoggingInterceptor interceptor = new GrpcLoggingInterceptor(); + // interceptor.logResponse(Status.CANCELLED, LogData.builder(), LOGGER); + // + // Assertions.assertEquals(1, testAppender.events.size()); + // Assertions.assertEquals(Level.INFO, testAppender.events.get(0).getLevel()); + // Assertions.assertEquals( + // "{\"response.status\":\"CANCELLED\",\"message\":\"Received Grpc response\"}", + // testAppender.events.get(0).getMessage()); + // testAppender.stop(); + // } + // + // private TestAppender setupTestLogger(Class clazz) { + // TestAppender testAppender = new TestAppender(); + // testAppender.start(); + // Logger logger = LoggerFactory.getLogger(clazz); + // ((ch.qos.logback.classic.Logger) logger).addAppender(testAppender); + // return testAppender; + // } + // + // @Test + // void testExecuteWithTryCatch_NoException() throws Exception { + // ThrowingRunnable action = Mockito.mock(ThrowingRunnable.class); + // assertDoesNotThrow(() -> GrpcLoggingInterceptor.executeWithTryCatch(action)); + // // Verify that the action was executed + // Mockito.verify(action).run(); + // } + // + // @Test + // void testExecuteWithTryCatch_WithException() throws Exception { + // ThrowingRunnable action = Mockito.mock(ThrowingRunnable.class); + // Mockito.doThrow(new RuntimeException("Test Exception")).when(action).run(); + // assertDoesNotThrow(() -> GrpcLoggingInterceptor.executeWithTryCatch(action)); + // // Verify that the action was executed (despite the exception) + // Mockito.verify(action).run(); + // } + // + // @Test + // void testExecuteWithTryCatch_WithNoSuchMethodError() throws Exception { + // ThrowingRunnable action = Mockito.mock(ThrowingRunnable.class); + // Mockito.doThrow(new NoSuchMethodError("Test Error")).when(action).run(); + // assertDoesNotThrow(() -> GrpcLoggingInterceptor.executeWithTryCatch(action)); + // // Verify that the action was executed (despite the error) + // Mockito.verify(action).run(); + // } } diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index a465ab471a..399ae9dd35 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -30,24 +30,25 @@ package com.google.api.gax.httpjson; +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.httpjson.ForwardingHttpJsonClientCall.SimpleForwardingHttpJsonClientCall; import com.google.api.gax.httpjson.ForwardingHttpJsonClientCallListener.SimpleForwardingHttpJsonClientCallListener; import com.google.api.gax.logging.LogData; import com.google.api.gax.logging.LoggingUtils; -import com.google.gson.Gson; -import com.google.gson.JsonObject; import java.util.HashMap; -import java.util.List; import java.util.Map; import org.slf4j.Logger; -import org.slf4j.event.Level; @InternalApi public class HttpJsonLoggingInterceptor implements HttpJsonClientInterceptor { private static final Logger LOGGER = LoggingUtils.getLogger(HttpJsonLoggingInterceptor.class); - private static final Gson GSON = new Gson(); @Override public HttpJsonClientCall interceptCall( @@ -64,28 +65,33 @@ public HttpJsonClientCall interceptCall( @Override public void start( HttpJsonClientCall.Listener responseListener, HttpJsonMetadata headers) { - - logRequestInfo(method, endpoint, logDataBuilder, LOGGER); - recordRequestHeaders(headers, logDataBuilder); + recordServiceRpcAndRequestHeaders( + null, + method.getFullMethodName(), + endpoint, + httpJsonMetadataToMap(headers), + logDataBuilder, + LOGGER); Listener forwardingResponseListener = new SimpleForwardingHttpJsonClientCallListener(responseListener) { @Override public void onHeaders(HttpJsonMetadata responseHeaders) { - recordResponseHeaders(responseHeaders, logDataBuilder); + recordResponseHeaders( + httpJsonMetadataToMap(responseHeaders), logDataBuilder, LOGGER); super.onHeaders(responseHeaders); } @Override public void onMessage(RespT message) { - recordResponsePayload(message, logDataBuilder); + recordResponsePayload(message, logDataBuilder, LOGGER); super.onMessage(message); } @Override public void onClose(int statusCode, HttpJsonMetadata trailers) { - logResponse(statusCode, logDataBuilder, LOGGER); + logResponse(String.valueOf(statusCode), logDataBuilder, LOGGER); super.onClose(statusCode, trailers); } }; @@ -94,102 +100,17 @@ public void onClose(int statusCode, HttpJsonMetadata trailers) { @Override public void sendMessage(ReqT message) { - logRequestDetails(message, logDataBuilder); + logRequest(message, logDataBuilder, LOGGER); super.sendMessage(message); } }; } // Helper methods for logging, - // some duplications with grpc equivalent to avoid exposing as public method - void logRequestInfo( - ApiMethodDescriptor method, - String endpoint, - LogData.Builder logDataBuilder, - Logger logger) { - try { - if (logger.isInfoEnabled()) { - logDataBuilder - .rpcName(method.getFullMethodName()) - .httpMethod(method.getHttpMethod()) - .httpUrl(endpoint); - - if (!logger.isDebugEnabled()) { - LoggingUtils.logWithMDC( - logger, Level.INFO, logDataBuilder.build().toMapRequest(), "Sending HTTP request"); - } - } - } catch (Exception e) { - logger.error("Error logging request info (and headers)", e); - } - } - private void recordRequestHeaders(HttpJsonMetadata headers, LogData.Builder logDataBuilder) { - try { - if (LOGGER.isDebugEnabled()) { - JsonObject requestHeaders = new JsonObject(); - headers - .getHeaders() - .forEach((key, value) -> requestHeaders.addProperty(key, value.toString())); - logDataBuilder.requestHeaders(GSON.toJson(requestHeaders)); - } - } catch (Exception e) { - LOGGER.error("Error recording request headers", e); - } - } - - private void recordResponseHeaders( - HttpJsonMetadata responseHeaders, LogData.Builder logDataBuilder) { - try { - if (LOGGER.isDebugEnabled()) { - - Map> map = new HashMap<>(); - responseHeaders.getHeaders().forEach((key, value) -> map.put(key, (List) value)); - logDataBuilder.responseHeaders(GSON.toJson(map)); - } - } catch (Exception e) { - LOGGER.error("Error recording response headers", e); - } - } - - private void recordResponsePayload(RespT message, LogData.Builder logDataBuilder) { - try { - if (LOGGER.isDebugEnabled()) { - logDataBuilder.responsePayload(GSON.toJsonTree(message)); - } - } catch (Exception e) { - LOGGER.error("Error recording response payload", e); - } - } - - void logResponse(int statusCode, LogData.Builder logDataBuilder, Logger logger) { - try { - if (logger.isInfoEnabled()) { - logDataBuilder.responseStatus(String.valueOf(statusCode)); - } - if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { - Map responseData = logDataBuilder.build().toMapResponse(); - LoggingUtils.logWithMDC(logger, Level.INFO, responseData, "Received HTTP response"); - } - if (logger.isDebugEnabled()) { - Map responsedDetailsMap = logDataBuilder.build().toMapResponse(); - LoggingUtils.logWithMDC(logger, Level.DEBUG, responsedDetailsMap, "Received HTTP response"); - } - } catch (Exception e) { - logger.error("Error logging request response", e); - } - } - - private void logRequestDetails(RespT message, LogData.Builder logDataBuilder) { - try { - if (LOGGER.isDebugEnabled()) { - logDataBuilder.requestPayload(GSON.toJsonTree(message)); - Map requestDetailsMap = logDataBuilder.build().toMapRequest(); - LoggingUtils.logWithMDC( - LOGGER, Level.DEBUG, requestDetailsMap, "Sending HTTP request: request payload"); - } - } catch (Exception e) { - LOGGER.error("Error logging request details", e); - } + private static Map httpJsonMetadataToMap(HttpJsonMetadata headers) { + Map headersMap = new HashMap<>(); + headers.getHeaders().forEach((key, value) -> headersMap.put(key, value.toString())); + return headersMap; } } diff --git a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java index 0966407fba..d4ba0d1165 100644 --- a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java +++ b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java @@ -30,14 +30,9 @@ package com.google.api.gax.httpjson; -import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; -import ch.qos.logback.classic.Level; import com.google.api.gax.httpjson.ApiMethodDescriptor.MethodType; -import com.google.api.gax.logging.LogData; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,41 +51,42 @@ class HttpJsonLoggingInterceptorTest { .setResponseParser(mock(HttpResponseParser.class)) .build(); - @Test - void testLogRequestInfo() { - - TestAppender testAppender = setupTestLogger(HttpJsonLoggingInterceptorTest.class); - HttpJsonLoggingInterceptor interceptor = new HttpJsonLoggingInterceptor(); - interceptor.logRequestInfo(method, "fake.endpoint", LogData.builder(), LOGGER); - - Assertions.assertEquals(1, testAppender.events.size()); - assertEquals(Level.INFO, testAppender.events.get(0).getLevel()); - assertEquals( - "{\"request.url\":\"fake.endpoint\",\"message\":\"Sending HTTP request\",\"rpcName\":\"FakeClient/fake-method\"}", - testAppender.events.get(0).getMessage()); - testAppender.stop(); - } - - @Test - void testLogResponseInfo() { - - TestAppender testAppender = setupTestLogger(HttpJsonLoggingInterceptorTest.class); - HttpJsonLoggingInterceptor interceptor = new HttpJsonLoggingInterceptor(); - interceptor.logResponse(200, LogData.builder(), LOGGER); - - Assertions.assertEquals(1, testAppender.events.size()); - assertEquals(Level.INFO, testAppender.events.get(0).getLevel()); - assertEquals( - "{\"response.status\":\"200\",\"message\":\"Received HTTP response\"}", - testAppender.events.get(0).getMessage()); - testAppender.stop(); - } - - private TestAppender setupTestLogger(Class clazz) { - TestAppender testAppender = new TestAppender(); - testAppender.start(); - Logger logger = LoggerFactory.getLogger(clazz); - ((ch.qos.logback.classic.Logger) logger).addAppender(testAppender); - return testAppender; - } + // @Test + // void testLogRequestInfo() { + // + // TestAppender testAppender = setupTestLogger(HttpJsonLoggingInterceptorTest.class); + // HttpJsonLoggingInterceptor interceptor = new HttpJsonLoggingInterceptor(); + // // interceptor.logRequestInfo(method, "fake.endpoint", LogData.builder(), LOGGER); + // + // Assertions.assertEquals(1, testAppender.events.size()); + // assertEquals(Level.INFO, testAppender.events.get(0).getLevel()); + // assertEquals( + // "{\"request.url\":\"fake.endpoint\",\"message\":\"Sending HTTP + // request\",\"rpcName\":\"FakeClient/fake-method\"}", + // testAppender.events.get(0).getMessage()); + // testAppender.stop(); + // } + // + // @Test + // void testLogResponseInfo() { + // + // TestAppender testAppender = setupTestLogger(HttpJsonLoggingInterceptorTest.class); + // HttpJsonLoggingInterceptor interceptor = new HttpJsonLoggingInterceptor(); + // interceptor.logResponse(200, LogData.builder(), LOGGER); + // + // Assertions.assertEquals(1, testAppender.events.size()); + // assertEquals(Level.INFO, testAppender.events.get(0).getLevel()); + // assertEquals( + // "{\"response.status\":\"200\",\"message\":\"Received HTTP response\"}", + // testAppender.events.get(0).getMessage()); + // testAppender.stop(); + // } + // + // private TestAppender setupTestLogger(Class clazz) { + // TestAppender testAppender = new TestAppender(); + // testAppender.start(); + // Logger logger = LoggerFactory.getLogger(clazz); + // ((ch.qos.logback.classic.Logger) logger).addAppender(testAppender); + // return testAppender; + // } } diff --git a/gax-java/gax/pom.xml b/gax-java/gax/pom.xml index bfec74bab1..1d4d6a913e 100644 --- a/gax-java/gax/pom.xml +++ b/gax-java/gax/pom.xml @@ -38,6 +38,10 @@ com.google.protobuf protobuf-java + + com.google.protobuf + protobuf-java-util + org.threeten threetenbp diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java index 57751dc6d0..1bdc82ef26 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java @@ -33,7 +33,6 @@ import com.google.api.core.InternalApi; import com.google.auto.value.AutoValue; import com.google.gson.Gson; -import com.google.gson.JsonElement; import java.util.HashMap; import java.util.Map; import javax.annotation.Nullable; @@ -53,19 +52,19 @@ public abstract class LogData { public abstract String requestId(); @Nullable - public abstract String requestHeaders(); + public abstract Map requestHeaders(); @Nullable - public abstract JsonElement requestPayload(); + public abstract Map requestPayload(); @Nullable public abstract String responseStatus(); @Nullable - public abstract String responseHeaders(); + public abstract Map responseHeaders(); @Nullable - public abstract JsonElement responsePayload(); + public abstract Map responsePayload(); @Nullable public abstract String httpMethod(); @@ -85,15 +84,15 @@ public abstract static class Builder { public abstract Builder requestId(String requestId); - public abstract Builder requestHeaders(String requestHeaders); + public abstract Builder requestHeaders(Map requestHeaders); - public abstract Builder requestPayload(JsonElement requestPayload); + public abstract Builder requestPayload(Map requestPayload); public abstract Builder responseStatus(String responseStatus); - public abstract Builder responseHeaders(String responseHeaders); + public abstract Builder responseHeaders(Map responseHeaders); - public abstract Builder responsePayload(JsonElement responsePayload); + public abstract Builder responsePayload(Map responsePayload); public abstract Builder httpMethod(String httpMethod); @@ -104,8 +103,8 @@ public abstract static class Builder { // helper functions to convert to map for logging // todo: error handling? - public Map toMapRequest() { - Map map = new HashMap<>(); + public Map toMapRequest() { + Map map = new HashMap<>(); if (serviceName() != null) { map.put("serviceName", serviceName()); } @@ -119,7 +118,7 @@ public Map toMapRequest() { map.put("request.headers", requestHeaders()); } if (requestPayload() != null) { - map.put("request.payload", gson.toJson(requestPayload())); + map.put("request.payload", requestPayload()); } if (httpMethod() != null) { map.put("request.method", httpMethod()); @@ -130,8 +129,8 @@ public Map toMapRequest() { return map; } - public Map toMapResponse() { - Map map = new HashMap<>(); + public Map toMapResponse() { + Map map = new HashMap<>(); if (serviceName() != null) { map.put("serviceName", serviceName()); } @@ -148,7 +147,7 @@ public Map toMapResponse() { map.put("response.headers", responseHeaders()); } if (responsePayload() != null) { - map.put("response.payload", gson.toJson(responsePayload())); + map.put("response.payload", responsePayload()); } return map; } diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java index c738426aa9..64a8e83142 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java @@ -34,11 +34,17 @@ import com.google.api.gax.rpc.internal.EnvironmentProvider; import com.google.api.gax.rpc.internal.SystemEnvironmentProvider; import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.Message; +import com.google.protobuf.util.JsonFormat; import java.util.Map; +import java.util.function.Consumer; import org.slf4j.ILoggerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.slf4j.MDC; +import org.slf4j.event.Level; +import org.slf4j.spi.LoggingEventBuilder; @InternalApi public class LoggingUtils { @@ -72,35 +78,30 @@ static Logger getLogger(Class clazz, LoggerFactoryProvider factoryProvider) { } public static void logWithMDC( - Logger logger, org.slf4j.event.Level level, Map contextMap, String message) { - if (!contextMap.isEmpty()) { - contextMap.forEach(MDC::put); - contextMap.put("message", message); - message = gson.toJson(contextMap); - } + Logger logger, org.slf4j.event.Level level, Map contextMap, String message) { + LoggingEventBuilder loggingEventBuilder; switch (level) { case TRACE: - logger.trace(message); + loggingEventBuilder = logger.atTrace(); break; case DEBUG: - logger.debug(message); + loggingEventBuilder = logger.atDebug(); break; case INFO: - logger.info(message); + loggingEventBuilder = logger.atInfo(); break; case WARN: - logger.warn(message); + loggingEventBuilder = logger.atWarn(); break; case ERROR: - logger.error(message); + loggingEventBuilder = logger.atError(); break; default: - logger.info(message); + loggingEventBuilder = logger.atInfo(); // Default to INFO level } - if (!contextMap.isEmpty()) { - MDC.clear(); - } + contextMap.forEach(loggingEventBuilder::addKeyValue); + loggingEventBuilder.log(message); } static boolean isLoggingEnabled() { @@ -118,4 +119,109 @@ public ILoggerFactory getLoggerFactory() { return LoggerFactory.getILoggerFactory(); } } + + // logging helper methods + public static Map messageToMapWithGson(Message message) + throws InvalidProtocolBufferException { + String json = JsonFormat.printer().print(message); + return gson.fromJson(json, new TypeToken>() {}.getType()); + } + + public static void recordServiceRpcAndRequestHeaders( + String serviceName, + String rpcName, + String endpoint, + Map requestHeaders, + LogData.Builder logDataBuilder, + Logger logger) { + executeWithTryCatch( + () -> { + if (logger.isInfoEnabled()) { + addIfNotEmpty(logDataBuilder::serviceName, serviceName); + addIfNotEmpty(logDataBuilder::rpcName, rpcName); + addIfNotEmpty(logDataBuilder::httpUrl, endpoint); + } + if (logger.isDebugEnabled()) { + logDataBuilder.requestHeaders(requestHeaders); + } + }); + } + + private static void addIfNotEmpty(Consumer setter, String value) { + if (value != null && !value.isEmpty()) { + setter.accept(value); + } + } + public static void recordResponseHeaders( + Map headers, LogData.Builder logDataBuilder, Logger logger) { + executeWithTryCatch( + () -> { + if (logger.isDebugEnabled()) { + logDataBuilder.responseHeaders(headers); + } + }); + } + + public static void recordResponsePayload( + RespT message, LogData.Builder logDataBuilder, Logger logger) { + executeWithTryCatch( + () -> { + if (logger.isInfoEnabled()) { + Map messageToMapWithGson = + LoggingUtils.messageToMapWithGson((Message) message); + + logDataBuilder.responsePayload(messageToMapWithGson); + } + }); + } + + public static void logResponse(String status, LogData.Builder logDataBuilder, Logger logger) { + executeWithTryCatch( + () -> { + if (logger.isInfoEnabled()) { + logDataBuilder.responseStatus(status); + } + if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { + Map responseData = logDataBuilder.build().toMapResponse(); + LoggingUtils.logWithMDC(logger, Level.INFO, responseData, "Received Grpc response"); + } + if (logger.isDebugEnabled()) { + Map responsedDetailsMap = logDataBuilder.build().toMapResponse(); + LoggingUtils.logWithMDC( + logger, Level.DEBUG, responsedDetailsMap, "Received Grpc response"); + } + }); + } + + public static void logRequest( + RespT message, LogData.Builder logDataBuilder, Logger logger) { + executeWithTryCatch( + () -> { + if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { + LoggingUtils.logWithMDC( + logger, Level.INFO, logDataBuilder.build().toMapRequest(), "Sending gRPC request"); + } + if (logger.isDebugEnabled()) { + Map messageToMapWithGson = + LoggingUtils.messageToMapWithGson((Message) message); + + logDataBuilder.requestPayload(messageToMapWithGson); + Map requestDetailsMap = logDataBuilder.build().toMapRequest(); + LoggingUtils.logWithMDC(logger, Level.DEBUG, requestDetailsMap, "Sending gRPC request"); + } + }); + } + + public static void executeWithTryCatch(ThrowingRunnable action) { + try { + action.run(); + } catch (Exception | NoSuchMethodError e) { + // should fail silently + } + } + + @FunctionalInterface + public interface ThrowingRunnable { + void run() throws Exception; + } } diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LogDataTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LogDataTest.java index d19c74591a..7ea868afbb 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LogDataTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LogDataTest.java @@ -33,7 +33,6 @@ import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.ImmutableMap; -import com.google.gson.JsonParser; import java.util.Map; import org.junit.jupiter.api.Test; @@ -45,9 +44,9 @@ void toMapResponse_correctlyConvertsData() { LogData.builder() .serviceName("MyService") .rpcName("myMethod") - .requestHeaders("fake header") + .requestHeaders(ImmutableMap.of("fake header", "item")) .requestId("abcd") - .responsePayload(JsonParser.parseString("{\"key\": \"value\"}")) + // .responsePayload(JsonParser.parseString("{\"key\": \"value\"}")) .build(); Map expectedMap = @@ -66,10 +65,10 @@ void toMapRequest_correctlyConvertsData() { LogData.builder() .serviceName("MyService") .rpcName("myMethod") - .requestHeaders("fake header") + .requestHeaders(ImmutableMap.of("fake header", "item")) .requestId("abcd") .httpUrl("url") - .responsePayload(JsonParser.parseString("{\"key\": \"value\"}")) + // .responsePayload(JsonParser.parseString("{\"key\": \"value\"}")) .build(); Map expectedMap = diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java index d53bd0bc02..c176137326 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java @@ -128,7 +128,7 @@ private TestAppender setupTestLogger(Class clazz) { @Test void testLogWithMDC_slf4jLogger() { TestAppender testAppender = setupTestLogger(LoggingUtilsTest.class); - Map contextMap = new HashMap<>(); + Map contextMap = new HashMap<>(); contextMap.put("key", "value"); LoggingUtils.logWithMDC(LOGGER, org.slf4j.event.Level.DEBUG, contextMap, "test message"); From b1386cb80a8e6f2c10d6447e12a5760dff35310b Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Thu, 26 Dec 2024 17:00:03 -0500 Subject: [PATCH 50/92] update LogDataTest, fix typo in log levels. --- .../google/api/gax/logging/LoggingUtils.java | 2 +- .../google/api/gax/logging/LogDataTest.java | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java index 64a8e83142..0e62344c76 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java @@ -166,7 +166,7 @@ public static void recordResponsePayload( RespT message, LogData.Builder logDataBuilder, Logger logger) { executeWithTryCatch( () -> { - if (logger.isInfoEnabled()) { + if (logger.isDebugEnabled()) { Map messageToMapWithGson = LoggingUtils.messageToMapWithGson((Message) message); diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LogDataTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LogDataTest.java index 7ea868afbb..bef3c56ca2 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LogDataTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LogDataTest.java @@ -40,20 +40,22 @@ class LogDataTest { @Test void toMapResponse_correctlyConvertsData() { + Map responsePayload= ImmutableMap.of("key", + "value", "key2", 123); LogData logData = LogData.builder() .serviceName("MyService") .rpcName("myMethod") .requestHeaders(ImmutableMap.of("fake header", "item")) .requestId("abcd") - // .responsePayload(JsonParser.parseString("{\"key\": \"value\"}")) + .responsePayload(responsePayload) .build(); - Map expectedMap = + Map expectedMap = ImmutableMap.of( "serviceName", "MyService", "rpcName", "myMethod", - "response.payload", "{\"key\":\"value\"}", + "response.payload", responsePayload, "requestId", "abcd"); assertThat(logData.toMapResponse()).isEqualTo(expectedMap); @@ -61,21 +63,24 @@ void toMapResponse_correctlyConvertsData() { @Test void toMapRequest_correctlyConvertsData() { + Map header= + ImmutableMap.of("fake header", "item"); LogData logData = LogData.builder() .serviceName("MyService") .rpcName("myMethod") - .requestHeaders(ImmutableMap.of("fake header", "item")) + .requestHeaders(header) .requestId("abcd") .httpUrl("url") - // .responsePayload(JsonParser.parseString("{\"key\": \"value\"}")) + .responsePayload(ImmutableMap.of("key", + "value", "key2", 123)) .build(); - Map expectedMap = + Map expectedMap = ImmutableMap.of( "serviceName", "MyService", "rpcName", "myMethod", - "request.headers", "fake header", + "request.headers", header, "requestId", "abcd", "request.url", "url"); From ef26d3039c8a6e0c4b1e5ec9d0e168a67b60f379 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 4 Feb 2025 14:28:21 -0500 Subject: [PATCH 51/92] logic to switch according to slf4j 1.x or 2.x --- gax-java/gax/pom.xml | 2 + .../google/api/gax/logging/LoggingUtils.java | 86 ++++++++++++++++--- gax-java/pom.xml | 32 ++++++- 3 files changed, 107 insertions(+), 13 deletions(-) diff --git a/gax-java/gax/pom.xml b/gax-java/gax/pom.xml index 1d4d6a913e..422723840e 100644 --- a/gax-java/gax/pom.xml +++ b/gax-java/gax/pom.xml @@ -77,6 +77,7 @@ org.slf4j slf4j-api + true @@ -142,5 +143,6 @@ + \ No newline at end of file diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java index 0e62344c76..4469a4784c 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java @@ -39,10 +39,12 @@ import com.google.protobuf.Message; import com.google.protobuf.util.JsonFormat; import java.util.Map; +import java.util.Map.Entry; import java.util.function.Consumer; import org.slf4j.ILoggerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.slf4j.MDC; import org.slf4j.event.Level; import org.slf4j.spi.LoggingEventBuilder; @@ -61,6 +63,20 @@ static void setEnvironmentProvider(EnvironmentProvider provider) { loggingEnabled = isLoggingEnabled(); } + private static boolean hasAddKeyValue; + + static { + try { + // try to find this class + Class.forName("org.slf4j.event.KeyValuePair"); + // If no exception, it means SLF4j 2.x or later is present + hasAddKeyValue = true; + } catch (ClassNotFoundException e) { + // If ClassNotFoundException, it's likely SLF4j 1.x + hasAddKeyValue = false; + } + } + private LoggingUtils() {} public static Logger getLogger(Class clazz) { @@ -77,7 +93,52 @@ static Logger getLogger(Class clazz, LoggerFactoryProvider factoryProvider) { } } - public static void logWithMDC( + public static void log( + Logger logger, org.slf4j.event.Level level, Map contextMap, String message) { + if (hasAddKeyValue) { + logWithKeyValuePair(logger, level, contextMap, message); + } else { + logWithMDC(logger, level, contextMap, message); + } + } + + private static void logWithMDC( + Logger logger, org.slf4j.event.Level level, Map contextMap, String message) { + if (!contextMap.isEmpty()) { + for (Entry entry : contextMap.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + MDC.put(key, value.toString()); + } + // contextMap.put("message", message); + // message = gson.toJson(contextMap); + } + switch (level) { + case TRACE: + logger.trace(message); + break; + case DEBUG: + logger.debug(message); + break; + case INFO: + logger.info(message); + break; + case WARN: + logger.warn(message); + break; + case ERROR: + logger.error(message); + break; + default: + logger.debug(message); + // Default to DEBUG level + } + if (!contextMap.isEmpty()) { + MDC.clear(); + } + } + + private static void logWithKeyValuePair( Logger logger, org.slf4j.event.Level level, Map contextMap, String message) { LoggingEventBuilder loggingEventBuilder; switch (level) { @@ -97,8 +158,8 @@ public static void logWithMDC( loggingEventBuilder = logger.atError(); break; default: - loggingEventBuilder = logger.atInfo(); - // Default to INFO level + loggingEventBuilder = logger.atDebug(); + // Default to DEBUG level } contextMap.forEach(loggingEventBuilder::addKeyValue); loggingEventBuilder.log(message); @@ -141,7 +202,7 @@ public static void recordServiceRpcAndRequestHeaders( addIfNotEmpty(logDataBuilder::rpcName, rpcName); addIfNotEmpty(logDataBuilder::httpUrl, endpoint); } - if (logger.isDebugEnabled()) { + if (logger.isInfoEnabled()) { logDataBuilder.requestHeaders(requestHeaders); } }); @@ -152,6 +213,7 @@ private static void addIfNotEmpty(Consumer setter, String value) { setter.accept(value); } } + public static void recordResponseHeaders( Map headers, LogData.Builder logDataBuilder, Logger logger) { executeWithTryCatch( @@ -166,7 +228,7 @@ public static void recordResponsePayload( RespT message, LogData.Builder logDataBuilder, Logger logger) { executeWithTryCatch( () -> { - if (logger.isDebugEnabled()) { + if (logger.isInfoEnabled()) { Map messageToMapWithGson = LoggingUtils.messageToMapWithGson((Message) message); @@ -183,11 +245,11 @@ public static void logResponse(String status, LogData.Builder logDataBuilder, Lo } if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { Map responseData = logDataBuilder.build().toMapResponse(); - LoggingUtils.logWithMDC(logger, Level.INFO, responseData, "Received Grpc response"); + LoggingUtils.log(logger, Level.INFO, responseData, "Received Grpc response"); } - if (logger.isDebugEnabled()) { + if (logger.isInfoEnabled()) { Map responsedDetailsMap = logDataBuilder.build().toMapResponse(); - LoggingUtils.logWithMDC( + LoggingUtils.log( logger, Level.DEBUG, responsedDetailsMap, "Received Grpc response"); } }); @@ -198,7 +260,7 @@ public static void logRequest( executeWithTryCatch( () -> { if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { - LoggingUtils.logWithMDC( + LoggingUtils.log( logger, Level.INFO, logDataBuilder.build().toMapRequest(), "Sending gRPC request"); } if (logger.isDebugEnabled()) { @@ -207,7 +269,7 @@ public static void logRequest( logDataBuilder.requestPayload(messageToMapWithGson); Map requestDetailsMap = logDataBuilder.build().toMapRequest(); - LoggingUtils.logWithMDC(logger, Level.DEBUG, requestDetailsMap, "Sending gRPC request"); + LoggingUtils.log(logger, Level.DEBUG, requestDetailsMap, "Sending gRPC request"); } }); } @@ -215,8 +277,8 @@ public static void logRequest( public static void executeWithTryCatch(ThrowingRunnable action) { try { action.run(); - } catch (Exception | NoSuchMethodError e) { - // should fail silently + } catch (Exception e) { + // let logging exceptions fail silently } } diff --git a/gax-java/pom.xml b/gax-java/pom.xml index 1b27682da0..a3c8a427ff 100644 --- a/gax-java/pom.xml +++ b/gax-java/pom.xml @@ -170,8 +170,15 @@ - + + + + org.slf4j + slf4j-api + true + + org.junit.jupiter junit-jupiter-engine @@ -234,6 +241,29 @@ + + org.codehaus.mojo + animal-sniffer-maven-plugin + 1.24 + + + java8 + + check + + + + org.codehaus.mojo.signature + java18 + 1.0 + + + org.slf4j.* + + + + + maven-compiler-plugin From 0c466ebc92bbbe3ea3a1c7fd4429c4a30edecbb2 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 4 Feb 2025 17:44:47 -0500 Subject: [PATCH 52/92] rework LoggingUtilsTest to use custom implementation of Logger, remove dep on TestAppender. --- gax-java/gax/pom.xml | 6 - .../google/api/gax/logging/LoggingUtils.java | 31 +- .../google/api/gax/logging/LogDataTest.java | 11 +- .../api/gax/logging/LoggingUtilsTest.java | 83 ++++-- .../google/api/gax/logging/TestLogger.java | 278 ++++++++++++++++++ gax-java/pom.xml | 60 +++- 6 files changed, 416 insertions(+), 53 deletions(-) create mode 100644 gax-java/gax/src/test/java/com/google/api/gax/logging/TestLogger.java diff --git a/gax-java/gax/pom.xml b/gax-java/gax/pom.xml index 422723840e..a9c1b0c882 100644 --- a/gax-java/gax/pom.xml +++ b/gax-java/gax/pom.xml @@ -73,12 +73,6 @@ opentelemetry-api true - - - org.slf4j - slf4j-api - true - diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java index 4469a4784c..15079b0da8 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java @@ -102,7 +102,8 @@ public static void log( } } - private static void logWithMDC( + // exposed for testing + static void logWithMDC( Logger logger, org.slf4j.event.Level level, Map contextMap, String message) { if (!contextMap.isEmpty()) { for (Entry entry : contextMap.entrySet()) { @@ -249,8 +250,7 @@ public static void logResponse(String status, LogData.Builder logDataBuilder, Lo } if (logger.isInfoEnabled()) { Map responsedDetailsMap = logDataBuilder.build().toMapResponse(); - LoggingUtils.log( - logger, Level.DEBUG, responsedDetailsMap, "Received Grpc response"); + LoggingUtils.log(logger, Level.DEBUG, responsedDetailsMap, "Received Grpc response"); } }); } @@ -286,4 +286,29 @@ public static void executeWithTryCatch(ThrowingRunnable action) { public interface ThrowingRunnable { void run() throws Exception; } + + // public interface loggerHelper { + // void log( + // Logger logger, org.slf4j.event.Level level, Map contextMap, + // String message) ; + // } + // + // public class Slf4j1xLogger implements loggerHelper { + // + // @Override + // public void log(Logger logger, Level level, Map contextMap, + // String message) { + // logWithMDC(logger, level, contextMap, message); + // } + // } + // + // + // public class Slf4j2xLogger implements loggerHelper { + // + // @Override + // public void log(Logger logger, Level level, Map contextMap, + // String message) { + // logWithKeyValuePair(logger, level, contextMap, message); + // } + // } } diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LogDataTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LogDataTest.java index bef3c56ca2..ff19dcd958 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LogDataTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LogDataTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -40,8 +40,7 @@ class LogDataTest { @Test void toMapResponse_correctlyConvertsData() { - Map responsePayload= ImmutableMap.of("key", - "value", "key2", 123); + Map responsePayload = ImmutableMap.of("key", "value", "key2", 123); LogData logData = LogData.builder() .serviceName("MyService") @@ -63,8 +62,7 @@ void toMapResponse_correctlyConvertsData() { @Test void toMapRequest_correctlyConvertsData() { - Map header= - ImmutableMap.of("fake header", "item"); + Map header = ImmutableMap.of("fake header", "item"); LogData logData = LogData.builder() .serviceName("MyService") @@ -72,8 +70,7 @@ void toMapRequest_correctlyConvertsData() { .requestHeaders(header) .requestId("abcd") .httpUrl("url") - .responsePayload(ImmutableMap.of("key", - "value", "key2", 123)) + .responsePayload(ImmutableMap.of("key", "value", "key2", 123)) .build(); Map expectedMap = diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java index c176137326..74a5b582ba 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -30,14 +30,20 @@ package com.google.api.gax.logging; +import static com.google.api.gax.logging.LoggingUtils.messageToMapWithGson; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.spi.ILoggingEvent; import com.google.api.gax.logging.LoggingUtils.LoggerFactoryProvider; import com.google.api.gax.rpc.internal.EnvironmentProvider; +import com.google.protobuf.Field; +import com.google.protobuf.Field.Cardinality; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.Option; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.AfterEach; @@ -47,6 +53,8 @@ import org.slf4j.ILoggerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.slf4j.event.Level; import org.slf4j.helpers.NOPLogger; class LoggingUtilsTest { @@ -75,7 +83,7 @@ void testGetLogger_loggingDisabled() { LoggingUtils.setEnvironmentProvider(envProvider); Logger logger = LoggingUtils.getLogger(LoggingUtilsTest.class); - Assertions.assertEquals(NOPLogger.class, logger.getClass()); + assertEquals(NOPLogger.class, logger.getClass()); Assertions.assertFalse(logger.isInfoEnabled()); Assertions.assertFalse(logger.isDebugEnabled()); } @@ -117,29 +125,58 @@ void testIsLoggingEnabled_defaultToFalse() { Assertions.assertFalse(LoggingUtils.isLoggingEnabled()); } - private TestAppender setupTestLogger(Class clazz) { - TestAppender testAppender = new TestAppender(); - testAppender.start(); - Logger logger = LoggerFactory.getLogger(clazz); - ((ch.qos.logback.classic.Logger) logger).addAppender(testAppender); - return testAppender; + // run with slf4j2_logback profile + @Test + void testLog_slf4JLogger() { + Map contextMap = new HashMap<>(); + contextMap.put("key1", "value"); + contextMap.put("key2", 123); + String message = "Test message"; + TestLogger testLogger = new TestLogger("test-logger"); + LoggingUtils.log(testLogger, org.slf4j.event.Level.DEBUG, contextMap, message); + + assertEquals(message, testLogger.messageList.get(0)); + + assertEquals("value", testLogger.keyValuePairsMap.get("key1")); + assertEquals(123, testLogger.keyValuePairsMap.get("key2")); } + // run with slf4j1_logback profile @Test - void testLogWithMDC_slf4jLogger() { - TestAppender testAppender = setupTestLogger(LoggingUtilsTest.class); + void testLogWithMDC_InfoLevel_VerifyMDC() { + TestLogger testLogger = new TestLogger("test-logger"); Map contextMap = new HashMap<>(); - contextMap.put("key", "value"); - LoggingUtils.logWithMDC(LOGGER, org.slf4j.event.Level.DEBUG, contextMap, "test message"); - - Assertions.assertEquals(1, testAppender.events.size()); - Assertions.assertEquals( - "{\"message\":\"test message\",\"key\":\"value\"}", - testAppender.events.get(0).getFormattedMessage()); - - // Verify MDC content - ILoggingEvent event = testAppender.events.get(0); - Assertions.assertEquals("value", event.getMDCPropertyMap().get("key")); - testAppender.stop(); + contextMap.put("key1", "value1"); + contextMap.put("key2", 123); + String message = "Test message"; + + LoggingUtils.logWithMDC(testLogger, Level.INFO, contextMap, message); + + Map mdcMap = testLogger.MDCMap; + + assertEquals(2, mdcMap.size()); + assertEquals("value1", mdcMap.get("key1")); + assertEquals("123", mdcMap.get("key2")); + + assertEquals(message, testLogger.messageList.get(0)); + // verify MDC is cleared + assertNull(MDC.getCopyOfContextMap()); + } + + @Test + void testMessageToMap_ValidMessage() throws InvalidProtocolBufferException { + Field field = + Field.newBuilder() + .setNumber(2) + .setName("field_name1") + .addOptions(Option.newBuilder().setName("opt_name1").build()) + .addOptions(Option.newBuilder().setName("opt_name2").build()) + .setCardinality(Cardinality.CARDINALITY_OPTIONAL) + .build(); + + Map map = messageToMapWithGson(field); + + assertEquals("field_name1", map.get("name")); + assertEquals(2.0, map.get("number")); // Gson converts ints to doubles by default } } diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestLogger.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestLogger.java new file mode 100644 index 0000000000..833033f501 --- /dev/null +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestLogger.java @@ -0,0 +1,278 @@ +/* + * 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.logging; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.MDC; +import org.slf4j.Marker; +import org.slf4j.event.KeyValuePair; +import org.slf4j.event.LoggingEvent; +import org.slf4j.spi.LoggingEventAware; + +/** Logger implementation for testing purposes only. Only implemented methods used in tests. */ +public class TestLogger implements Logger, LoggingEventAware { + Map MDCMap = new HashMap<>(); + List messageList = new ArrayList<>(); + + Map keyValuePairsMap = new HashMap<>(); + + private String loggerName; + + public TestLogger(String name) { + loggerName = name; + } + + @Override + public String getName() { + return loggerName; + } + + @Override + public boolean isTraceEnabled() { + return false; + } + + @Override + public void trace(String msg) {} + + @Override + public void trace(String format, Object arg) {} + + @Override + public void trace(String format, Object arg1, Object arg2) {} + + @Override + public void trace(String format, Object... arguments) {} + + @Override + public void trace(String msg, Throwable t) {} + + @Override + public boolean isTraceEnabled(Marker marker) { + return false; + } + + @Override + public void trace(Marker marker, String msg) {} + + @Override + public void trace(Marker marker, String format, Object arg) {} + + @Override + public void trace(Marker marker, String format, Object arg1, Object arg2) {} + + @Override + public void trace(Marker marker, String format, Object... argArray) {} + + @Override + public void trace(Marker marker, String msg, Throwable t) {} + + @Override + public boolean isDebugEnabled() { + return true; + } + + @Override + public void debug(String msg) { + if (isDebugEnabled()) { + Map currentMDC = MDC.getCopyOfContextMap(); + MDCMap.putAll(currentMDC); + messageList.add(msg); + } + } + + @Override + public void debug(String format, Object arg) {} + + @Override + public void debug(String format, Object arg1, Object arg2) {} + + @Override + public void debug(String format, Object... arguments) {} + + @Override + public void debug(String msg, Throwable t) {} + + @Override + public boolean isDebugEnabled(Marker marker) { + return false; + } + + @Override + public void debug(Marker marker, String msg) {} + + @Override + public void debug(Marker marker, String format, Object arg) {} + + @Override + public void debug(Marker marker, String format, Object arg1, Object arg2) {} + + @Override + public void debug(Marker marker, String format, Object... arguments) {} + + @Override + public void debug(Marker marker, String msg, Throwable t) {} + + @Override + public boolean isInfoEnabled() { + return false; + } + + @Override + public void info(String msg) { + Map currentMDC = MDC.getCopyOfContextMap(); + MDCMap.putAll(currentMDC); + messageList.add(msg); + } + + @Override + public void info(String format, Object arg) {} + + @Override + public void info(String format, Object arg1, Object arg2) {} + + @Override + public void info(String format, Object... arguments) {} + + @Override + public void info(String msg, Throwable t) {} + + @Override + public boolean isInfoEnabled(Marker marker) {} + + @Override + public void info(Marker marker, String msg) {} + + @Override + public void info(Marker marker, String format, Object arg) {} + + @Override + public void info(Marker marker, String format, Object arg1, Object arg2) {} + + @Override + public void info(Marker marker, String format, Object... arguments) {} + + @Override + public void info(Marker marker, String msg, Throwable t) {} + + @Override + public boolean isWarnEnabled() { + return false; + } + + @Override + public void warn(String msg) {} + + @Override + public void warn(String format, Object arg) {} + + @Override + public void warn(String format, Object... arguments) {} + + @Override + public void warn(String format, Object arg1, Object arg2) {} + + @Override + public void warn(String msg, Throwable t) {} + + @Override + public boolean isWarnEnabled(Marker marker) { + return false; + } + + @Override + public void warn(Marker marker, String msg) {} + + @Override + public void warn(Marker marker, String format, Object arg) {} + + @Override + public void warn(Marker marker, String format, Object arg1, Object arg2) {} + + @Override + public void warn(Marker marker, String format, Object... arguments) {} + + @Override + public void warn(Marker marker, String msg, Throwable t) {} + + @Override + public boolean isErrorEnabled() { + return false; + } + + @Override + public void error(String msg) {} + + @Override + public void error(String format, Object arg) {} + + @Override + public void error(String format, Object arg1, Object arg2) {} + + @Override + public void error(String format, Object... arguments) {} + + @Override + public void error(String msg, Throwable t) {} + + @Override + public boolean isErrorEnabled(Marker marker) { + return false; + } + + @Override + public void error(Marker marker, String msg) {} + + @Override + public void error(Marker marker, String format, Object arg) {} + + @Override + public void error(Marker marker, String format, Object arg1, Object arg2) {} + + @Override + public void error(Marker marker, String format, Object... arguments) {} + + @Override + public void error(Marker marker, String msg, Throwable t) {} + + @Override + public void log(LoggingEvent event) { + messageList.add(event.getMessage()); + List keyValuePairs = event.getKeyValuePairs(); + for (KeyValuePair pair : keyValuePairs) { + keyValuePairsMap.put(pair.key, pair.value); + } + } +} diff --git a/gax-java/pom.xml b/gax-java/pom.xml index a3c8a427ff..be99688758 100644 --- a/gax-java/pom.xml +++ b/gax-java/pom.xml @@ -217,20 +217,6 @@ test - - - ch.qos.logback - logback-classic - - 1.3.14 - test - - - ch.qos.logback - logback-core - 1.3.14 - test - @@ -331,5 +317,51 @@ + + slf4j2-logback + + true + + + + + ch.qos.logback + logback-classic + + 1.3.14 + test + + + ch.qos.logback + logback-core + 1.3.14 + test + + + + + slf4j1-logback + + + + org.slf4j + slf4j-api + 1.7.36 + + + ch.qos.logback + logback-classic + + 1.2.13 + test + + + ch.qos.logback + logback-core + 1.2.13 + test + + + From 7a23e2ddb5ac4189c6193bfce8722cd266df528f Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 4 Feb 2025 17:54:23 -0500 Subject: [PATCH 53/92] clean ups. --- .../api/gax/logging/LoggingUtilsTest.java | 8 --- .../google/api/gax/logging/TestAppender.java | 52 ------------------- .../google/api/gax/logging/TestLogger.java | 4 +- 3 files changed, 3 insertions(+), 61 deletions(-) delete mode 100644 gax-java/gax/src/test/java/com/google/api/gax/logging/TestAppender.java diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java index 74a5b582ba..cfcb511795 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java @@ -37,7 +37,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import ch.qos.logback.classic.LoggerContext; import com.google.api.gax.logging.LoggingUtils.LoggerFactoryProvider; import com.google.api.gax.rpc.internal.EnvironmentProvider; import com.google.protobuf.Field; @@ -46,7 +45,6 @@ import com.google.protobuf.Option; import java.util.HashMap; import java.util.Map; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -62,12 +60,6 @@ class LoggingUtilsTest { private static final Logger LOGGER = LoggerFactory.getLogger(LoggingUtilsTest.class); private EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class); - @AfterEach - void tearDown() { - LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); - loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).detachAppender("CONSOLE"); - } - @Test void testGetLogger_loggingEnabled_slf4jBindingPresent() { Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("true"); diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestAppender.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestAppender.java deleted file mode 100644 index 1206553ee6..0000000000 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestAppender.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2024 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.logging; - -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.AppenderBase; -import java.util.ArrayList; -import java.util.List; - -/** Logback appender used to set up tests. */ -public class TestAppender extends AppenderBase { - public List events = new ArrayList<>(); - - @Override - protected void append(ILoggingEvent eventObject) { - // triggering Logback to capture the current MDC context and store it with the log event - eventObject.getMDCPropertyMap(); - events.add(eventObject); - } - - public void clearEvents() { - events.clear(); - } -} diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestLogger.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestLogger.java index 833033f501..61aea98c4b 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestLogger.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestLogger.java @@ -170,7 +170,9 @@ public void info(String format, Object... arguments) {} public void info(String msg, Throwable t) {} @Override - public boolean isInfoEnabled(Marker marker) {} + public boolean isInfoEnabled(Marker marker) { + return false; + } @Override public void info(Marker marker, String msg) {} From 3821ec3bdda0693b1a730bf1fe84112f1fd18529 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 5 Feb 2025 14:11:21 -0500 Subject: [PATCH 54/92] remove dep on logback for gax tests. --- .../google/api/gax/logging/LoggingUtils.java | 34 +--- .../api/gax/logging/LoggingUtilsTest.java | 165 ++++++++++++++++-- .../google/api/gax/logging/TestLogger.java | 25 ++- .../api/gax/logging/TestMDCAdapter.java | 52 ++++++ .../api/gax/logging/TestServiceProvider.java | 43 +++++ .../org.slf4j.spi.SLF4JServiceProvider | 1 + gax-java/pom.xml | 11 +- 7 files changed, 276 insertions(+), 55 deletions(-) create mode 100644 gax-java/gax/src/test/java/com/google/api/gax/logging/TestMDCAdapter.java create mode 100644 gax-java/gax/src/test/java/com/google/api/gax/logging/TestServiceProvider.java create mode 100644 gax-java/gax/src/test/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java index 15079b0da8..5d4629409c 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java @@ -79,6 +79,10 @@ static void setEnvironmentProvider(EnvironmentProvider provider) { private LoggingUtils() {} + static boolean isHasAddKeyValue() { + return hasAddKeyValue; + } + public static Logger getLogger(Class clazz) { return getLogger(clazz, new DefaultLoggerFactoryProvider()); } @@ -113,6 +117,7 @@ static void logWithMDC( } // contextMap.put("message", message); // message = gson.toJson(contextMap); + MDC.getMDCAdapter(); } switch (level) { case TRACE: @@ -229,7 +234,7 @@ public static void recordResponsePayload( RespT message, LogData.Builder logDataBuilder, Logger logger) { executeWithTryCatch( () -> { - if (logger.isInfoEnabled()) { + if (logger.isDebugEnabled()) { Map messageToMapWithGson = LoggingUtils.messageToMapWithGson((Message) message); @@ -248,7 +253,7 @@ public static void logResponse(String status, LogData.Builder logDataBuilder, Lo Map responseData = logDataBuilder.build().toMapResponse(); LoggingUtils.log(logger, Level.INFO, responseData, "Received Grpc response"); } - if (logger.isInfoEnabled()) { + if (logger.isDebugEnabled()) { Map responsedDetailsMap = logDataBuilder.build().toMapResponse(); LoggingUtils.log(logger, Level.DEBUG, responsedDetailsMap, "Received Grpc response"); } @@ -286,29 +291,4 @@ public static void executeWithTryCatch(ThrowingRunnable action) { public interface ThrowingRunnable { void run() throws Exception; } - - // public interface loggerHelper { - // void log( - // Logger logger, org.slf4j.event.Level level, Map contextMap, - // String message) ; - // } - // - // public class Slf4j1xLogger implements loggerHelper { - // - // @Override - // public void log(Logger logger, Level level, Map contextMap, - // String message) { - // logWithMDC(logger, level, contextMap, message); - // } - // } - // - // - // public class Slf4j2xLogger implements loggerHelper { - // - // @Override - // public void log(Logger logger, Level level, Map contextMap, - // String message) { - // logWithKeyValuePair(logger, level, contextMap, message); - // } - // } } diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java index cfcb511795..5f8b416eb7 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java @@ -31,9 +31,8 @@ package com.google.api.gax.logging; import static com.google.api.gax.logging.LoggingUtils.messageToMapWithGson; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -50,27 +49,27 @@ import org.mockito.Mockito; import org.slf4j.ILoggerFactory; import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.MDC; import org.slf4j.event.Level; import org.slf4j.helpers.NOPLogger; +import org.slf4j.helpers.NOPLoggerFactory; class LoggingUtilsTest { - private static final Logger LOGGER = LoggerFactory.getLogger(LoggingUtilsTest.class); + // private static final Logger LOGGER = LoggerFactory.getLogger(LoggingUtilsTest.class); private EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class); @Test void testGetLogger_loggingEnabled_slf4jBindingPresent() { Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("true"); LoggingUtils.setEnvironmentProvider(envProvider); + // should get ILoggerFactory from TestServiceProvider Logger logger = LoggingUtils.getLogger(LoggingUtilsTest.class); Assertions.assertInstanceOf(Logger.class, logger); Assertions.assertNotEquals(NOPLogger.class, logger.getClass()); } @Test - void testGetLogger_loggingDisabled() { + void testGetLogger_loggingDisabled_shouldGetNOPLogger() { Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("false"); LoggingUtils.setEnvironmentProvider(envProvider); @@ -81,15 +80,14 @@ void testGetLogger_loggingDisabled() { } @Test - void testGetLogger_loggingEnabled_noBinding() { + void testGetLogger_loggingEnabled_noBinding_shouldGetNOPLogger() { Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("true"); LoggingUtils.setEnvironmentProvider(envProvider); - // Create a mock LoggerFactoryProvider + // Create a mock LoggerFactoryProvider, mimic SLF4J's behavior to return NOPLoggerFactory when + // no binding LoggerFactoryProvider mockLoggerFactoryProvider = mock(LoggerFactoryProvider.class); - ILoggerFactory mockLoggerFactory = mock(ILoggerFactory.class); - when(mockLoggerFactoryProvider.getLoggerFactory()).thenReturn(mockLoggerFactory); - when(mockLoggerFactory.getLogger(anyString())) - .thenReturn(org.slf4j.helpers.NOPLogger.NOP_LOGGER); + ILoggerFactory nopLoggerFactory = new NOPLoggerFactory(); + when(mockLoggerFactoryProvider.getLoggerFactory()).thenReturn(nopLoggerFactory); // Use the mock LoggerFactoryProvider in getLogger() Logger logger = LoggingUtils.getLogger(LoggingUtilsTest.class, mockLoggerFactoryProvider); @@ -117,9 +115,8 @@ void testIsLoggingEnabled_defaultToFalse() { Assertions.assertFalse(LoggingUtils.isLoggingEnabled()); } - // run with slf4j2_logback profile @Test - void testLog_slf4JLogger() { + void testLog_slf4J2xLogger() { Map contextMap = new HashMap<>(); contextMap.put("key1", "value"); contextMap.put("key2", 123); @@ -133,15 +130,16 @@ void testLog_slf4JLogger() { assertEquals(123, testLogger.keyValuePairsMap.get("key2")); } - // run with slf4j1_logback profile @Test void testLogWithMDC_InfoLevel_VerifyMDC() { + // this test replies on TestMDCApapter and TestServiceProvider TestLogger testLogger = new TestLogger("test-logger"); Map contextMap = new HashMap<>(); contextMap.put("key1", "value1"); contextMap.put("key2", 123); String message = "Test message"; + // need a service provider LoggingUtils.logWithMDC(testLogger, Level.INFO, contextMap, message); Map mdcMap = testLogger.MDCMap; @@ -151,8 +149,6 @@ void testLogWithMDC_InfoLevel_VerifyMDC() { assertEquals("123", mdcMap.get("key2")); assertEquals(message, testLogger.messageList.get(0)); - // verify MDC is cleared - assertNull(MDC.getCopyOfContextMap()); } @Test @@ -171,4 +167,139 @@ void testMessageToMap_ValidMessage() throws InvalidProtocolBufferException { assertEquals("field_name1", map.get("name")); assertEquals(2.0, map.get("number")); // Gson converts ints to doubles by default } + + @Test + void testRecordServiceRpcAndRequestHeaders_infoEnabled() { + String serviceName = "testService"; + String rpcName = "testRpc"; + String endpoint = "http://test.com/endpoint"; + Map requestHeaders = new HashMap<>(); + requestHeaders.put("header1", "value1"); + requestHeaders.put("header2", "value2"); + + LogData.Builder logDataBuilder = LogData.builder(); + + TestLogger testLogger = new TestLogger("test-logger", true, true); + + LoggingUtils.recordServiceRpcAndRequestHeaders( + serviceName, rpcName, endpoint, requestHeaders, logDataBuilder, testLogger); + + LogData logData = logDataBuilder.build(); + assertEquals(serviceName, logData.serviceName()); + assertEquals(rpcName, logData.rpcName()); + assertEquals(endpoint, logData.httpUrl()); + assertEquals(requestHeaders, logData.requestHeaders()); + } + + @Test + void testRecordServiceRpcAndRequestHeaders_infoDisabled() { + String serviceName = "testService"; + String rpcName = "testRpc"; + String endpoint = "http://test.com/endpoint"; + Map requestHeaders = new HashMap<>(); + requestHeaders.put("header1", "value1"); + requestHeaders.put("header2", "value2"); + + LogData.Builder logDataBuilder = LogData.builder(); + + TestLogger testLogger = new TestLogger("test-logger", false, false); + + LoggingUtils.recordServiceRpcAndRequestHeaders( + serviceName, rpcName, endpoint, requestHeaders, logDataBuilder, testLogger); + + LogData logData = logDataBuilder.build(); + assertEquals(null, logData.serviceName()); + assertEquals(null, logData.rpcName()); + assertEquals(null, logData.httpUrl()); + assertEquals(null, logData.requestHeaders()); + } + + @Test + void testRecordResponseHeaders_debugEnabled() { + Map responseHeaders = new HashMap<>(); + responseHeaders.put("header1", "value1"); + responseHeaders.put("header2", "value2"); + + LogData.Builder logDataBuilder = LogData.builder(); + TestLogger testLogger = new TestLogger("test-logger", true, true); + + LoggingUtils.recordResponseHeaders(responseHeaders, logDataBuilder, testLogger); + + LogData logData = logDataBuilder.build(); + assertEquals(responseHeaders, logData.responseHeaders()); + } + + @Test + void testRecordResponseHeaders_debugDisabled() { + Map responseHeaders = new HashMap<>(); + responseHeaders.put("header1", "value1"); + responseHeaders.put("header2", "value2"); + + LogData.Builder logDataBuilder = LogData.builder(); + TestLogger testLogger = new TestLogger("test-logger", true, false); + + LoggingUtils.recordResponseHeaders(responseHeaders, logDataBuilder, testLogger); + + LogData logData = logDataBuilder.build(); + assertEquals(null, logData.responseHeaders()); + } + + @Test + void testRecordResponsePayload_debugEnabled() { + + Field field = + Field.newBuilder() + .setName("field_name1") + .addOptions(Option.newBuilder().setName("opt_name1").build()) + .addOptions(Option.newBuilder().setName("opt_name2").build()) + .build(); + + LogData.Builder logDataBuilder = LogData.builder(); + TestLogger testLogger = new TestLogger("test-logger", true, true); + + LoggingUtils.recordResponsePayload(field, logDataBuilder, testLogger); + + LogData logData = logDataBuilder.build(); + assertEquals(2, logData.responsePayload().size()); + assertEquals("field_name1", logData.responsePayload().get("name")); + assertEquals( + "[{name=opt_name1}, {name=opt_name2}]", + logData.responsePayload().get("options").toString()); + } + + @Test + void testExecuteWithTryCatch_noException() { + assertDoesNotThrow( + () -> + LoggingUtils.executeWithTryCatch( + () -> { + // Some code that should not throw an exception + int x = 5; + int y = 10; + int z = x + y; + assertEquals(15, z); // Example assertion + })); + } + + @Test + void testExecuteWithTryCatch_exceptionThrown() { + assertDoesNotThrow( + () -> + LoggingUtils.executeWithTryCatch( + () -> { + // Code that throws an exception + throw new Exception("Test Exception"); + })); + } + + @Test + void testExecuteWithTryCatch_runtimeExceptionThrown() { + assertDoesNotThrow( + () -> + LoggingUtils.executeWithTryCatch( + () -> { + // Code that throws a RuntimeException + throw new RuntimeException("Test RuntimeException"); + })); + } } diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestLogger.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestLogger.java index 61aea98c4b..6bf4ddeba9 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestLogger.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestLogger.java @@ -49,9 +49,19 @@ public class TestLogger implements Logger, LoggingEventAware { Map keyValuePairsMap = new HashMap<>(); private String loggerName; + private boolean infoEnabled; + private boolean debugEnabled; public TestLogger(String name) { loggerName = name; + infoEnabled = true; + debugEnabled = true; + } + + public TestLogger(String name, boolean info, boolean debug) { + loggerName = name; + infoEnabled = info; + debugEnabled = debug; } @Override @@ -101,16 +111,14 @@ public void trace(Marker marker, String msg, Throwable t) {} @Override public boolean isDebugEnabled() { - return true; + return debugEnabled; } @Override public void debug(String msg) { - if (isDebugEnabled()) { - Map currentMDC = MDC.getCopyOfContextMap(); - MDCMap.putAll(currentMDC); - messageList.add(msg); - } + Map currentMDC = MDC.getCopyOfContextMap(); + MDCMap.putAll(currentMDC); + messageList.add(msg); } @Override @@ -147,11 +155,14 @@ public void debug(Marker marker, String msg, Throwable t) {} @Override public boolean isInfoEnabled() { - return false; + return infoEnabled; } @Override public void info(String msg) { + // access MDC content here before it is cleared. + // TestMDCAdapter is set up via TestServiceProvider + // to allow MDC values recorded and copied for testing here Map currentMDC = MDC.getCopyOfContextMap(); MDCMap.putAll(currentMDC); messageList.add(msg); diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestMDCAdapter.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestMDCAdapter.java new file mode 100644 index 0000000000..bbe038e85d --- /dev/null +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestMDCAdapter.java @@ -0,0 +1,52 @@ +package com.google.api.gax.logging; + +import java.util.Deque; +import java.util.HashMap; +import java.util.Map; +import org.slf4j.spi.MDCAdapter; + +public class TestMDCAdapter implements MDCAdapter { + Map mdcValues = new HashMap<>(); + + @Override + public void put(String key, String val) { + mdcValues.put(key, val); + } + + @Override + public String get(String key) { + return ""; + } + + @Override + public void remove(String key) {} + + @Override + public void clear() { + mdcValues.clear(); + } + + @Override + public Map getCopyOfContextMap() { + return mdcValues; + } + + @Override + public void setContextMap(Map contextMap) {} + + @Override + public void pushByKey(String key, String value) {} + + @Override + public String popByKey(String key) { + return ""; + } + + @Override + public Deque getCopyOfDequeByKey(String key) { + return null; + } + + @Override + public void clearDequeByKey(String key) {} +} diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestServiceProvider.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestServiceProvider.java new file mode 100644 index 0000000000..6576c80bfc --- /dev/null +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestServiceProvider.java @@ -0,0 +1,43 @@ +package com.google.api.gax.logging; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.slf4j.ILoggerFactory; +import org.slf4j.IMarkerFactory; +import org.slf4j.spi.MDCAdapter; +import org.slf4j.spi.SLF4JServiceProvider; + +/** + * This provider is made discoverable to SFL4J's LoggerFactory in + * resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider + */ +public class TestServiceProvider implements SLF4JServiceProvider { + + @Override + public ILoggerFactory getLoggerFactory() { + // mock behavior when provider present + ILoggerFactory mockLoggerFactory = mock(ILoggerFactory.class); + when(mockLoggerFactory.getLogger(anyString())).thenReturn(new TestLogger("test-logger")); + return mockLoggerFactory; + } + + @Override + public IMarkerFactory getMarkerFactory() { + return null; + } + + @Override + public MDCAdapter getMDCAdapter() { + return new TestMDCAdapter(); + } + + @Override + public String getRequestedApiVersion() { + return ""; + } + + @Override + public void initialize() {} +} diff --git a/gax-java/gax/src/test/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider b/gax-java/gax/src/test/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider new file mode 100644 index 0000000000..de8b02c7be --- /dev/null +++ b/gax-java/gax/src/test/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider @@ -0,0 +1 @@ +com.google.api.gax.logging.TestServiceProvider \ No newline at end of file diff --git a/gax-java/pom.xml b/gax-java/pom.xml index be99688758..3abfc27da2 100644 --- a/gax-java/pom.xml +++ b/gax-java/pom.xml @@ -193,7 +193,7 @@ org.mockito - mockito-core + mockito-inline test @@ -319,9 +319,9 @@ slf4j2-logback - - true - + + + @@ -341,6 +341,9 @@ slf4j1-logback + + + From 23f00e89a1dcfd939a1dee85d6aa1028d0240bf6 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 5 Feb 2025 14:58:39 -0500 Subject: [PATCH 55/92] add tests. --- .../api/gax/logging/LoggingUtilsTest.java | 48 +++++++++++++++++++ gax-java/pom.xml | 2 +- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java index 5f8b416eb7..121000ecff 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java @@ -33,7 +33,10 @@ import static com.google.api.gax.logging.LoggingUtils.messageToMapWithGson; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.anyMap; 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 com.google.api.gax.logging.LoggingUtils.LoggerFactoryProvider; @@ -267,6 +270,51 @@ void testRecordResponsePayload_debugEnabled() { logData.responsePayload().get("options").toString()); } + @Test + void testLogRequest_infoEnabled_debugDisabled() { + Object message = new Object(); // not used in info path + LogData.Builder logDataBuilder = Mockito.mock(LogData.Builder.class); + + LogData.Builder testLogDataBuilder = + LogData.builder().serviceName("service-name").rpcName("rpc-name"); + when(logDataBuilder.build()).thenReturn(testLogDataBuilder.build()); + + Logger logger = new TestLogger("test", true, false); + LoggingUtils.logRequest(message, logDataBuilder, logger); + + assertEquals(2, ((TestLogger) logger).keyValuePairsMap.size()); + assertEquals("Sending gRPC request", ((TestLogger) logger).messageList.get(0)); + verify(logDataBuilder, never()).requestPayload(anyMap()); // Ensure debug path is not taken + } + + @Test + void testLogRequest_debugEnabled() throws InvalidProtocolBufferException { + Field field = + Field.newBuilder() + .setName("field_name1") + .addOptions(Option.newBuilder().setName("opt_name1").build()) + .addOptions(Option.newBuilder().setName("opt_name2").build()) + .build(); + + LogData.Builder logDataBuilder = Mockito.mock(LogData.Builder.class); + LogData.Builder testLogDataBuilder = + LogData.builder() + .serviceName("service-name") + .rpcName("rpc-name") + .requestPayload(LoggingUtils.messageToMapWithGson(field)); + when(logDataBuilder.build()).thenReturn(testLogDataBuilder.build()); + + TestLogger logger = new TestLogger("test-logger", false, true); + + LoggingUtils.logRequest(field, logDataBuilder, logger); + + verify(logDataBuilder).requestPayload(LoggingUtils.messageToMapWithGson(field)); + + assertEquals(3, ((TestLogger) logger).keyValuePairsMap.size()); + assertEquals(2, ((Map) ((TestLogger) logger).keyValuePairsMap.get("request.payload")).size()); + assertEquals("Sending gRPC request", ((TestLogger) logger).messageList.get(0)); + } + @Test void testExecuteWithTryCatch_noException() { assertDoesNotThrow( diff --git a/gax-java/pom.xml b/gax-java/pom.xml index 3abfc27da2..d94402a387 100644 --- a/gax-java/pom.xml +++ b/gax-java/pom.xml @@ -193,7 +193,7 @@ org.mockito - mockito-inline + mockito-core test From 9f7d77c39ee2be7aa28e817ef471fbff29249cdd Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 5 Feb 2025 16:56:23 -0500 Subject: [PATCH 56/92] add tests. --- .../google/api/gax/logging/LoggingUtils.java | 2 - .../api/gax/logging/LoggingUtilsTest.java | 58 +++++++++++++++++-- .../google/api/gax/logging/TestLogger.java | 3 + 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java index 5d4629409c..d2900bbd61 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java @@ -115,8 +115,6 @@ static void logWithMDC( Object value = entry.getValue(); MDC.put(key, value.toString()); } - // contextMap.put("message", message); - // message = gson.toJson(contextMap); MDC.getMDCAdapter(); } switch (level) { diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java index 121000ecff..583814fbe8 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java @@ -33,6 +33,7 @@ import static com.google.api.gax.logging.LoggingUtils.messageToMapWithGson; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -285,6 +286,8 @@ void testLogRequest_infoEnabled_debugDisabled() { assertEquals(2, ((TestLogger) logger).keyValuePairsMap.size()); assertEquals("Sending gRPC request", ((TestLogger) logger).messageList.get(0)); verify(logDataBuilder, never()).requestPayload(anyMap()); // Ensure debug path is not taken + + assertEquals(Level.INFO, ((TestLogger) logger).level); } @Test @@ -304,7 +307,7 @@ void testLogRequest_debugEnabled() throws InvalidProtocolBufferException { .requestPayload(LoggingUtils.messageToMapWithGson(field)); when(logDataBuilder.build()).thenReturn(testLogDataBuilder.build()); - TestLogger logger = new TestLogger("test-logger", false, true); + TestLogger logger = new TestLogger("test-logger", true, true); LoggingUtils.logRequest(field, logDataBuilder, logger); @@ -313,6 +316,56 @@ void testLogRequest_debugEnabled() throws InvalidProtocolBufferException { assertEquals(3, ((TestLogger) logger).keyValuePairsMap.size()); assertEquals(2, ((Map) ((TestLogger) logger).keyValuePairsMap.get("request.payload")).size()); assertEquals("Sending gRPC request", ((TestLogger) logger).messageList.get(0)); + + assertEquals(Level.DEBUG, ((TestLogger) logger).level); + } + + @Test + void testLogResponse_infoEnabled_debugDisabled() { + String status = "OK"; + Map responseData = new HashMap<>(); + + LogData.Builder logDataBuilder = Mockito.mock(LogData.Builder.class); + LogData.Builder testLogDataBuilder = + LogData.builder() + .serviceName("service-name") + .rpcName("rpc-name") + .responsePayload(responseData); + when(logDataBuilder.build()).thenReturn(testLogDataBuilder.build()); + TestLogger logger = new TestLogger("test-logger", true, false); + + LoggingUtils.logResponse(status, logDataBuilder, logger); + + verify(logDataBuilder).responseStatus(status); + assertEquals("Received Grpc response", ((TestLogger) logger).messageList.get(0)); + assertEquals(3, ((TestLogger) logger).keyValuePairsMap.size()); + assertTrue(((TestLogger) logger).keyValuePairsMap.containsKey("response.payload")); + assertEquals(Level.INFO, ((TestLogger) logger).level); + Map keyValuePairsMap = ((TestLogger) logger).keyValuePairsMap; + } + + @Test + void testLogResponse_infoEnabled_debugEnabled() { + String status = "OK"; + Map responseData = new HashMap<>(); + + LogData.Builder logDataBuilder = Mockito.mock(LogData.Builder.class); + LogData.Builder testLogDataBuilder = + LogData.builder() + .serviceName("service-name") + .rpcName("rpc-name") + .responsePayload(responseData); + when(logDataBuilder.build()).thenReturn(testLogDataBuilder.build()); + TestLogger logger = new TestLogger("test-logger", true, true); + + LoggingUtils.logResponse(status, logDataBuilder, logger); + + verify(logDataBuilder).responseStatus(status); + assertEquals("Received Grpc response", ((TestLogger) logger).messageList.get(0)); + assertEquals(3, ((TestLogger) logger).keyValuePairsMap.size()); + assertTrue(((TestLogger) logger).keyValuePairsMap.containsKey("response.payload")); + + assertEquals(Level.DEBUG, ((TestLogger) logger).level); } @Test @@ -325,7 +378,6 @@ void testExecuteWithTryCatch_noException() { int x = 5; int y = 10; int z = x + y; - assertEquals(15, z); // Example assertion })); } @@ -335,7 +387,6 @@ void testExecuteWithTryCatch_exceptionThrown() { () -> LoggingUtils.executeWithTryCatch( () -> { - // Code that throws an exception throw new Exception("Test Exception"); })); } @@ -346,7 +397,6 @@ void testExecuteWithTryCatch_runtimeExceptionThrown() { () -> LoggingUtils.executeWithTryCatch( () -> { - // Code that throws a RuntimeException throw new RuntimeException("Test RuntimeException"); })); } diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestLogger.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestLogger.java index 6bf4ddeba9..7c496383f4 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestLogger.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestLogger.java @@ -38,6 +38,7 @@ import org.slf4j.MDC; import org.slf4j.Marker; import org.slf4j.event.KeyValuePair; +import org.slf4j.event.Level; import org.slf4j.event.LoggingEvent; import org.slf4j.spi.LoggingEventAware; @@ -45,6 +46,7 @@ public class TestLogger implements Logger, LoggingEventAware { Map MDCMap = new HashMap<>(); List messageList = new ArrayList<>(); + Level level; Map keyValuePairsMap = new HashMap<>(); @@ -283,6 +285,7 @@ public void error(Marker marker, String msg, Throwable t) {} @Override public void log(LoggingEvent event) { messageList.add(event.getMessage()); + level = event.getLevel(); List keyValuePairs = event.getKeyValuePairs(); for (KeyValuePair pair : keyValuePairs) { keyValuePairsMap.put(pair.key, pair.value); From 86cc4d2cbc0e53b6ec8f904453f926b1f0246599 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Thu, 6 Feb 2025 15:19:39 -0500 Subject: [PATCH 57/92] add test, rm logback test profiles from gax. --- .../google/api/gax/logging/LoggingUtils.java | 17 +++---- .../api/gax/logging/LoggingUtilsTest.java | 13 +++-- gax-java/pom.xml | 49 ------------------- 3 files changed, 17 insertions(+), 62 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java index d2900bbd61..51fa2e50ba 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java @@ -66,23 +66,20 @@ static void setEnvironmentProvider(EnvironmentProvider provider) { private static boolean hasAddKeyValue; static { + hasAddKeyValue = checkIfClazzAvailable("org.slf4j.event.KeyValuePair"); + } + + static boolean checkIfClazzAvailable(String clazzName) { try { - // try to find this class - Class.forName("org.slf4j.event.KeyValuePair"); - // If no exception, it means SLF4j 2.x or later is present - hasAddKeyValue = true; + Class.forName(clazzName); + return true; // SLF4j 2.x or later } catch (ClassNotFoundException e) { - // If ClassNotFoundException, it's likely SLF4j 1.x - hasAddKeyValue = false; + return false; // SLF4j 1.x or earlier } } private LoggingUtils() {} - static boolean isHasAddKeyValue() { - return hasAddKeyValue; - } - public static Logger getLogger(Class clazz) { return getLogger(clazz, new DefaultLoggerFactoryProvider()); } diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java index 583814fbe8..a36ec494ec 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java @@ -33,6 +33,7 @@ import static com.google.api.gax.logging.LoggingUtils.messageToMapWithGson; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.Mockito.mock; @@ -79,8 +80,8 @@ void testGetLogger_loggingDisabled_shouldGetNOPLogger() { Logger logger = LoggingUtils.getLogger(LoggingUtilsTest.class); assertEquals(NOPLogger.class, logger.getClass()); - Assertions.assertFalse(logger.isInfoEnabled()); - Assertions.assertFalse(logger.isDebugEnabled()); + assertFalse(logger.isInfoEnabled()); + assertFalse(logger.isDebugEnabled()); } @Test @@ -116,7 +117,7 @@ void testIsLoggingEnabled_true() { @Test void testIsLoggingEnabled_defaultToFalse() { LoggingUtils.setEnvironmentProvider(envProvider); - Assertions.assertFalse(LoggingUtils.isLoggingEnabled()); + assertFalse(LoggingUtils.isLoggingEnabled()); } @Test @@ -400,4 +401,10 @@ void testExecuteWithTryCatch_runtimeExceptionThrown() { throw new RuntimeException("Test RuntimeException"); })); } + + @Test + void testCheckIfClazzAvailable() { + assertFalse(LoggingUtils.checkIfClazzAvailable("fake.class.should.not.be.in.classpath")); + assertTrue(LoggingUtils.checkIfClazzAvailable("org.slf4j.event.KeyValuePair")); + } } diff --git a/gax-java/pom.xml b/gax-java/pom.xml index d94402a387..cd4b876f7d 100644 --- a/gax-java/pom.xml +++ b/gax-java/pom.xml @@ -317,54 +317,5 @@ - - slf4j2-logback - - - - - - - ch.qos.logback - logback-classic - - 1.3.14 - test - - - ch.qos.logback - logback-core - 1.3.14 - test - - - - - slf4j1-logback - - - - - - - org.slf4j - slf4j-api - 1.7.36 - - - ch.qos.logback - logback-classic - - 1.2.13 - test - - - ch.qos.logback - logback-core - 1.2.13 - test - - - From de01f370463e2790ba9ab5303ae3e42c180b4fa4 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Thu, 6 Feb 2025 15:56:13 -0500 Subject: [PATCH 58/92] also capture errors with try-catch to fail silently. --- .../google/api/gax/logging/LoggingUtils.java | 4 +-- .../api/gax/logging/LoggingUtilsTest.java | 28 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java index 51fa2e50ba..4ae5580201 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java @@ -277,13 +277,13 @@ public static void logRequest( public static void executeWithTryCatch(ThrowingRunnable action) { try { action.run(); - } catch (Exception e) { + } catch (Throwable t) { // let logging exceptions fail silently } } @FunctionalInterface public interface ThrowingRunnable { - void run() throws Exception; + void run() throws Throwable; } } diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java index a36ec494ec..ded2e6bf58 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java @@ -42,6 +42,7 @@ import static org.mockito.Mockito.when; import com.google.api.gax.logging.LoggingUtils.LoggerFactoryProvider; +import com.google.api.gax.logging.LoggingUtils.ThrowingRunnable; import com.google.api.gax.rpc.internal.EnvironmentProvider; import com.google.protobuf.Field; import com.google.protobuf.Field.Cardinality; @@ -379,27 +380,26 @@ void testExecuteWithTryCatch_noException() { int x = 5; int y = 10; int z = x + y; + assertEquals(15, z); })); } @Test - void testExecuteWithTryCatch_exceptionThrown() { - assertDoesNotThrow( - () -> - LoggingUtils.executeWithTryCatch( - () -> { - throw new Exception("Test Exception"); - })); + void testExecuteWithTryCatch_WithException() throws Throwable { + ThrowingRunnable action = Mockito.mock(ThrowingRunnable.class); + Mockito.doThrow(new RuntimeException("Test Exception")).when(action).run(); + assertDoesNotThrow(() -> LoggingUtils.executeWithTryCatch(action)); + // Verify that the action was executed (despite the exception) + Mockito.verify(action).run(); } @Test - void testExecuteWithTryCatch_runtimeExceptionThrown() { - assertDoesNotThrow( - () -> - LoggingUtils.executeWithTryCatch( - () -> { - throw new RuntimeException("Test RuntimeException"); - })); + void testExecuteWithTryCatch_WithNoSuchMethodError() throws Throwable { + ThrowingRunnable action = Mockito.mock(ThrowingRunnable.class); + Mockito.doThrow(new NoSuchMethodError("Test Error")).when(action).run(); + assertDoesNotThrow(() -> LoggingUtils.executeWithTryCatch(action)); + // Verify that the action was executed (despite the error) + Mockito.verify(action).run(); } @Test From e3862a8a787a62cfdba643fa58db0002732c8648 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Thu, 6 Feb 2025 16:19:06 -0500 Subject: [PATCH 59/92] cleanup un-necessary tests in gax-grpc, gax-httpjson --- .../gax/grpc/GrpcLoggingInterceptorTest.java | 74 +------------------ .../com/google/api/gax/grpc/TestAppender.java | 52 ------------- .../src/test/resources/logback-test.xml | 6 -- .../HttpJsonLoggingInterceptorTest.java | 39 ---------- .../google/api/gax/httpjson/TestAppender.java | 52 ------------- .../src/test/resources/logback-test.xml | 6 -- 6 files changed, 2 insertions(+), 227 deletions(-) delete mode 100644 gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/TestAppender.java delete mode 100644 gax-java/gax-grpc/src/test/resources/logback-test.xml delete mode 100644 gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/TestAppender.java delete mode 100644 gax-java/gax-httpjson/src/test/resources/logback-test.xml diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java index 13384d198e..00dca46390 100644 --- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java +++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java @@ -30,18 +30,13 @@ package com.google.api.gax.grpc; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; 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 ch.qos.logback.classic.Level; -// import com.google.api.gax.grpc.GrpcLoggingInterceptor.ThrowingRunnable; import com.google.api.gax.grpc.testing.FakeMethodDescriptor; -import com.google.api.gax.logging.LogData; import io.grpc.CallOptions; import io.grpc.Channel; import io.grpc.ClientCall; @@ -49,7 +44,6 @@ import io.grpc.Metadata; import io.grpc.MethodDescriptor; import io.grpc.Status; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -113,74 +107,10 @@ void testInterceptor_responseListener() { // --- Verify that the response listener's methods were called --- // verify(interceptor) - // .recordResponseHeaders(eq(responseHeaders), any(LogData.Builder.class), any(Logger.class)); + // .recordResponseHeaders(eq(responseHeaders), any(LogData.Builder.class), + // any(Logger.class)); // // verify(interceptor).recordResponsePayload(any(), any(LogData.Builder.class), // // any(Logger.class)); // verify(interceptor).logResponse(eq(status), any(LogData.Builder.class), any(Logger.class)); } - - // @Test - // void testLogRequestInfo() { - // - // TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptorTest.class); - // GrpcLoggingInterceptor interceptor = new GrpcLoggingInterceptor(); - // LogData.Builder logData = - // LogData.builder().serviceName("FakeClient").rpcName("FakeClient/fake-method"); - // interceptor.logRequest(method, logData, LOGGER); - // - // Assertions.assertEquals(1, testAppender.events.size()); - // Assertions.assertEquals(Level.INFO, testAppender.events.get(0).getLevel()); - // Assertions.assertEquals( - // "{\"serviceName\":\"FakeClient\",\"message\":\"Sending gRPC request\",\"rpcName\":\"FakeClient/fake-method\"}", - // testAppender.events.get(0).getMessage()); - // testAppender.stop(); - // } - // - // @Test - // void TestLogResponseInfo() { - // TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptorTest.class); - // GrpcLoggingInterceptor interceptor = new GrpcLoggingInterceptor(); - // interceptor.logResponse(Status.CANCELLED, LogData.builder(), LOGGER); - // - // Assertions.assertEquals(1, testAppender.events.size()); - // Assertions.assertEquals(Level.INFO, testAppender.events.get(0).getLevel()); - // Assertions.assertEquals( - // "{\"response.status\":\"CANCELLED\",\"message\":\"Received Grpc response\"}", - // testAppender.events.get(0).getMessage()); - // testAppender.stop(); - // } - // - // private TestAppender setupTestLogger(Class clazz) { - // TestAppender testAppender = new TestAppender(); - // testAppender.start(); - // Logger logger = LoggerFactory.getLogger(clazz); - // ((ch.qos.logback.classic.Logger) logger).addAppender(testAppender); - // return testAppender; - // } - // - // @Test - // void testExecuteWithTryCatch_NoException() throws Exception { - // ThrowingRunnable action = Mockito.mock(ThrowingRunnable.class); - // assertDoesNotThrow(() -> GrpcLoggingInterceptor.executeWithTryCatch(action)); - // // Verify that the action was executed - // Mockito.verify(action).run(); - // } - // - // @Test - // void testExecuteWithTryCatch_WithException() throws Exception { - // ThrowingRunnable action = Mockito.mock(ThrowingRunnable.class); - // Mockito.doThrow(new RuntimeException("Test Exception")).when(action).run(); - // assertDoesNotThrow(() -> GrpcLoggingInterceptor.executeWithTryCatch(action)); - // // Verify that the action was executed (despite the exception) - // Mockito.verify(action).run(); - // } - // - // @Test - // void testExecuteWithTryCatch_WithNoSuchMethodError() throws Exception { - // ThrowingRunnable action = Mockito.mock(ThrowingRunnable.class); - // Mockito.doThrow(new NoSuchMethodError("Test Error")).when(action).run(); - // assertDoesNotThrow(() -> GrpcLoggingInterceptor.executeWithTryCatch(action)); - // // Verify that the action was executed (despite the error) - // Mockito.verify(action).run(); - // } } diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/TestAppender.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/TestAppender.java deleted file mode 100644 index 0aa34ebb5a..0000000000 --- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/TestAppender.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2024 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 ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.AppenderBase; -import java.util.ArrayList; -import java.util.List; - -/** Logback appender used to set up tests. */ -public class TestAppender extends AppenderBase { - public List events = new ArrayList<>(); - - @Override - protected void append(ILoggingEvent eventObject) { - // triggering Logback to capture the current MDC context and store it with the log event - eventObject.getMDCPropertyMap(); - events.add(eventObject); - } - - public void clearEvents() { - events.clear(); - } -} diff --git a/gax-java/gax-grpc/src/test/resources/logback-test.xml b/gax-java/gax-grpc/src/test/resources/logback-test.xml deleted file mode 100644 index 802d0e18a7..0000000000 --- a/gax-java/gax-grpc/src/test/resources/logback-test.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java index d4ba0d1165..8c83a0d550 100644 --- a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java +++ b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java @@ -50,43 +50,4 @@ class HttpJsonLoggingInterceptorTest { .setFullMethodName("FakeClient/fake-method") .setResponseParser(mock(HttpResponseParser.class)) .build(); - - // @Test - // void testLogRequestInfo() { - // - // TestAppender testAppender = setupTestLogger(HttpJsonLoggingInterceptorTest.class); - // HttpJsonLoggingInterceptor interceptor = new HttpJsonLoggingInterceptor(); - // // interceptor.logRequestInfo(method, "fake.endpoint", LogData.builder(), LOGGER); - // - // Assertions.assertEquals(1, testAppender.events.size()); - // assertEquals(Level.INFO, testAppender.events.get(0).getLevel()); - // assertEquals( - // "{\"request.url\":\"fake.endpoint\",\"message\":\"Sending HTTP - // request\",\"rpcName\":\"FakeClient/fake-method\"}", - // testAppender.events.get(0).getMessage()); - // testAppender.stop(); - // } - // - // @Test - // void testLogResponseInfo() { - // - // TestAppender testAppender = setupTestLogger(HttpJsonLoggingInterceptorTest.class); - // HttpJsonLoggingInterceptor interceptor = new HttpJsonLoggingInterceptor(); - // interceptor.logResponse(200, LogData.builder(), LOGGER); - // - // Assertions.assertEquals(1, testAppender.events.size()); - // assertEquals(Level.INFO, testAppender.events.get(0).getLevel()); - // assertEquals( - // "{\"response.status\":\"200\",\"message\":\"Received HTTP response\"}", - // testAppender.events.get(0).getMessage()); - // testAppender.stop(); - // } - // - // private TestAppender setupTestLogger(Class clazz) { - // TestAppender testAppender = new TestAppender(); - // testAppender.start(); - // Logger logger = LoggerFactory.getLogger(clazz); - // ((ch.qos.logback.classic.Logger) logger).addAppender(testAppender); - // return testAppender; - // } } diff --git a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/TestAppender.java b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/TestAppender.java deleted file mode 100644 index c89ebfb3b7..0000000000 --- a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/TestAppender.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2024 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.httpjson; - -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.AppenderBase; -import java.util.ArrayList; -import java.util.List; - -/** Logback appender used to set up tests. */ -public class TestAppender extends AppenderBase { - public List events = new ArrayList<>(); - - @Override - protected void append(ILoggingEvent eventObject) { - // triggering Logback to capture the current MDC context and store it with the log event - eventObject.getMDCPropertyMap(); - events.add(eventObject); - } - - public void clearEvents() { - events.clear(); - } -} diff --git a/gax-java/gax-httpjson/src/test/resources/logback-test.xml b/gax-java/gax-httpjson/src/test/resources/logback-test.xml deleted file mode 100644 index 8a6dc9b23d..0000000000 --- a/gax-java/gax-httpjson/src/test/resources/logback-test.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - From 427e820a1abcddcb46c699cd992edd99a3dc911e Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Fri, 7 Feb 2025 17:20:04 -0500 Subject: [PATCH 60/92] add showcase tests for logging with slf4j 1x, 2x separately, setup profiles for each. ci not setup yet. --- showcase/gapic-showcase/pom.xml | 156 +++++++++++- .../google/showcase/v1beta1/it/ITLogging.java | 109 --------- .../v1beta1/it/logging/ITLogging.java | 222 ++++++++++++++++++ .../v1beta1/it/logging/ITLogging1x.java | 194 +++++++++++++++ .../v1beta1/it/logging/ITLoggingExtra.java | 3 + .../it/{util => logging}/TestAppender.java | 2 +- .../src/test/resources/logback-test.xml | 17 +- 7 files changed, 577 insertions(+), 126 deletions(-) delete mode 100644 showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLogging.java create mode 100644 showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java create mode 100644 showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java create mode 100644 showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLoggingExtra.java rename showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/{util => logging}/TestAppender.java (96%) diff --git a/showcase/gapic-showcase/pom.xml b/showcase/gapic-showcase/pom.xml index 583ab17219..3d0e892268 100644 --- a/showcase/gapic-showcase/pom.xml +++ b/showcase/gapic-showcase/pom.xml @@ -73,6 +73,26 @@ + + + + + + + + + + + + + + + + + + + + @@ -217,18 +237,128 @@ - - ch.qos.logback - logback-classic - - 1.3.14 - test - - - ch.qos.logback - logback-core - 1.3.14 - test - + + + + jul + + + org.slf4j + slf4j-jdk14 + 2.0.7 + + + + + slf4j2_logback + + + + + + slf4j2_logback + + + + + org.slf4j + slf4j-api + 2.0.16 + + + ch.qos.logback + logback-classic + + 1.3.14 + test + + + ch.qos.logback + logback-core + 1.3.14 + test + + + net.logstash.logback + logstash-logback-encoder + 8.0 + + + com.fasterxml.jackson.core + jackson-databind + 2.14.2 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + **/ITLogging.java + + + + + + + + slf4j1_logback + + + + + + + slf4j1_logback + + + + + org.slf4j + slf4j-api + 1.7.36 + + + ch.qos.logback + logback-classic + 1.2.13 + test + + + ch.qos.logback + logback-core + 1.2.13 + test + + + net.logstash.logback + logstash-logback-encoder + 7.3 + + + com.fasterxml.jackson.core + jackson-databind + 2.14.2 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + **/ITLogging.java + + + + + + + + diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLogging.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLogging.java deleted file mode 100644 index 51e7145e99..0000000000 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLogging.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.showcase.v1beta1.it; - -import static com.google.common.truth.Truth.assertThat; - -import ch.qos.logback.classic.spi.ILoggingEvent; -import com.google.api.gax.grpc.GrpcLoggingInterceptor; -import com.google.api.gax.httpjson.HttpJsonLoggingInterceptor; -import com.google.showcase.v1beta1.EchoClient; -import com.google.showcase.v1beta1.EchoRequest; -import com.google.showcase.v1beta1.EchoResponse; -import com.google.showcase.v1beta1.it.util.TestAppender; -import com.google.showcase.v1beta1.it.util.TestClientInitializer; -import java.util.concurrent.TimeUnit; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.slf4j.LoggerFactory; - -// This test needs to run with GOOGLE_SDK_JAVA_LOGGING=true -public class ITLogging { - private static EchoClient grpcClient; - - private static EchoClient httpjsonClient; - - private TestAppender setupTestLogger(Class clazz) { - TestAppender testAppender = new TestAppender(); - testAppender.start(); - org.slf4j.Logger logger = LoggerFactory.getLogger(clazz); - ((ch.qos.logback.classic.Logger) logger).addAppender(testAppender); - return testAppender; - } - - @BeforeAll - static void createClients() throws Exception { - // Create gRPC Echo Client - grpcClient = TestClientInitializer.createGrpcEchoClient(); - // Create Http JSON Echo Client - httpjsonClient = TestClientInitializer.createHttpJsonEchoClient(); - } - - @AfterAll - static void destroyClients() throws InterruptedException { - grpcClient.close(); - httpjsonClient.close(); - - grpcClient.awaitTermination(TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS); - httpjsonClient.awaitTermination( - TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS); - } - - @Test - void testGrpc_receiveContent_logDebug() { - TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptor.class); - assertThat(echoGrpc("grpc-echo?")).isEqualTo("grpc-echo?"); - assertThat(testAppender.events.size()).isEqualTo(2); - assertThat(testAppender.events.get(0).getMessage()) - .isEqualTo( - "{\"serviceName\":\"google.showcase.v1beta1.Echo\",\"message\":\"Sending gRPC request\",\"rpcName\":\"google.showcase.v1beta1.Echo/Echo\"}"); - assertThat(testAppender.events.get(0).getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); - assertThat(testAppender.events.get(1).getMessage()) - .isEqualTo( - "{\"serviceName\":\"google.showcase.v1beta1.Echo\",\"response.status\":\"OK\",\"message\":\"Received Grpc response\",\"rpcName\":\"google.showcase.v1beta1.Echo/Echo\"}"); - testAppender.stop(); - } - - @Test - void testHttpJson_receiveContent_logDebug() { - TestAppender testAppender = setupTestLogger(HttpJsonLoggingInterceptor.class); - assertThat(echoHttpJson("http-echo?")).isEqualTo("http-echo?"); - assertThat(testAppender.events.size()).isEqualTo(2); - ILoggingEvent loggingEvent1 = testAppender.events.get(0); - assertThat(loggingEvent1.getMessage()) - .isEqualTo( - "{\"request.method\":\"POST\",\"request.url\":\"http://localhost:7469\",\"message\":\"Sending HTTP request\",\"rpcName\":\"google.showcase.v1beta1.Echo/Echo\"}"); - assertThat(loggingEvent1.getLevel()).isEqualTo(ch.qos.logback.classic.Level.INFO); - assertThat(loggingEvent1.getMDCPropertyMap()).hasSize(3); - assertThat(loggingEvent1.getMDCPropertyMap()).containsKey("rpcName"); - assertThat(testAppender.events.get(1).getMessage()) - .isEqualTo( - "{\"response.status\":\"200\",\"message\":\"Received HTTP response\",\"rpcName\":\"google.showcase.v1beta1.Echo/Echo\"}"); - testAppender.stop(); - } - - private String echoGrpc(String value) { - EchoResponse response = grpcClient.echo(EchoRequest.newBuilder().setContent(value).build()); - return response.getContent(); - } - - private String echoHttpJson(String value) { - EchoResponse response = httpjsonClient.echo(EchoRequest.newBuilder().setContent(value).build()); - return response.getContent(); - } -} diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java new file mode 100644 index 0000000000..de939b9b42 --- /dev/null +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java @@ -0,0 +1,222 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.showcase.v1beta1.it.logging; + +import static com.google.common.truth.Truth.assertThat; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; +import com.google.api.gax.grpc.GrpcLoggingInterceptor; +import com.google.api.gax.httpjson.HttpJsonLoggingInterceptor; +import com.google.common.collect.ImmutableMap; +import com.google.showcase.v1beta1.EchoClient; +import com.google.showcase.v1beta1.EchoRequest; +import com.google.showcase.v1beta1.EchoResponse; +import com.google.showcase.v1beta1.it.util.TestClientInitializer; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.event.KeyValuePair; + +// This test needs to run with GOOGLE_SDK_JAVA_LOGGING=true +// mvn clean test -Dslf4j2_logback -Dtest=com.google.showcase.v1beta1.it.logging.ITLogging +public class ITLogging { + private static EchoClient grpcClient; + + private static EchoClient httpjsonClient; + + private static final KeyValuePair SERVICE_NAME_KEY_VALUE_PAIR = + new KeyValuePair("serviceName", "google.showcase.v1beta1.Echo"); + private static final KeyValuePair RPC_NAME_KEY_VALUE_PAIR = + new KeyValuePair("rpcName", "google.showcase.v1beta1.Echo/Echo"); + private static final KeyValuePair RESPONSE_STATUS_KEY_VALUE_PAIR = + new KeyValuePair("response.status", "OK"); + private static final KeyValuePair RESPONSE_STATUS_KEY_VALUE_PAIR_HTTP = + new KeyValuePair("response.status", "200"); + + private static final KeyValuePair RESPONSE_HEADERS_KEY_VALUE_PAIR = + new KeyValuePair("response.headers", ImmutableMap.of("content-type", "application/grpc")); + + private static final String ECHO_STRING = "echo?"; + + private static Logger logger = LoggerFactory.getLogger(ITLogging.class); + private TestAppender setupTestLogger(Class clazz) { + TestAppender testAppender = new TestAppender(); + testAppender.start(); + ListAppender listAppender = new ListAppender<>(); + listAppender.start(); + org.slf4j.Logger logger = LoggerFactory.getLogger(clazz); + ((ch.qos.logback.classic.Logger) logger).setLevel(Level.DEBUG); + ((ch.qos.logback.classic.Logger) logger).addAppender(testAppender); + return testAppender; + } + + @BeforeAll + static void createClients() throws Exception { + // Create gRPC Echo Client + grpcClient = TestClientInitializer.createGrpcEchoClient(); + // Create Http JSON Echo Client + httpjsonClient = TestClientInitializer.createHttpJsonEchoClient(); + } + + @AfterAll + static void destroyClients() throws InterruptedException { + grpcClient.close(); + httpjsonClient.close(); + + grpcClient.awaitTermination(TestClientInitializer.AWAIT_TERMINATION_SECONDS, +TimeUnit.SECONDS); + httpjsonClient.awaitTermination( + TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS); + } + + // only run when GOOGLE_SDK_JAVA_LOGGING!=true + @Test + void testloggingDisabled() { + TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptor.class); + assertThat(echoGrpc(ECHO_STRING)).isEqualTo(ECHO_STRING); + + assertThat(testAppender.events.size()).isEqualTo(0); + } + + @Test + void testGrpc_receiveContent_logDebug() { + TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptor.class); + assertThat(echoGrpc(ECHO_STRING)).isEqualTo(ECHO_STRING); + + assertThat(true).isTrue(); + + assertThat(testAppender.events.size()).isEqualTo(2); + // logging event for request + ILoggingEvent loggingEvent1 = testAppender.events.get(0); + assertThat(loggingEvent1.getMessage()).isEqualTo("Sending gRPC request"); + assertThat(loggingEvent1.getLevel()).isEqualTo(Level.DEBUG); + List keyValuePairs = loggingEvent1.getKeyValuePairs(); + assertThat(keyValuePairs.size()).isEqualTo(4); + assertThat(keyValuePairs) + .containsAtLeast( + SERVICE_NAME_KEY_VALUE_PAIR, RPC_NAME_KEY_VALUE_PAIR); + + for (KeyValuePair kvp : keyValuePairs) { + if (kvp.key.equals("request.payload")) { + Map payload = (Map) kvp.value; + assertThat(payload.get("content")).isEqualTo(ECHO_STRING); + assertThat(payload.get("requestId")).isNotNull(); + assertThat(payload.get("otherRequestId")).isNotNull(); + } + if (kvp.key.equals("request.headers")) { + Map headers = (Map) kvp.value; + assertThat(headers).containsKey("x-goog-api-version"); + assertThat(headers).containsKey("x-goog-api-client"); + } + } + + // logging event for response + ILoggingEvent loggingEvent2 = testAppender.events.get(1); + assertThat(loggingEvent2.getMessage()).isEqualTo("Received Grpc response"); + + assertThat(loggingEvent2.getLevel()).isEqualTo(Level.DEBUG); + List keyValuePairs2 = loggingEvent2.getKeyValuePairs(); + assertThat(keyValuePairs2.size()).isEqualTo(5); + assertThat(keyValuePairs2) + .containsAtLeast( + RESPONSE_STATUS_KEY_VALUE_PAIR, + RESPONSE_HEADERS_KEY_VALUE_PAIR, + SERVICE_NAME_KEY_VALUE_PAIR, + RPC_NAME_KEY_VALUE_PAIR); + for (KeyValuePair kvp : keyValuePairs2) { + if (kvp.key.equals("response.payload")) { + Map payload = (Map) kvp.value; + assertThat(payload.get("content")).isEqualTo(ECHO_STRING); + assertThat(payload.get("requestId")).isNotNull(); + assertThat(payload.get("otherRequestId")).isNotNull(); + } + } + testAppender.stop(); + } + + @Test + void testHttpJson_receiveContent_logDebug() { + TestAppender testAppender = setupTestLogger(HttpJsonLoggingInterceptor.class); + assertThat(echoHttpJson(ECHO_STRING)).isEqualTo(ECHO_STRING); + assertThat(testAppender.events.size()).isEqualTo(2); + // logging event for request + ILoggingEvent loggingEvent1 = testAppender.events.get(0); + assertThat(loggingEvent1.getMessage()).isEqualTo("Sending gRPC request"); + assertThat(loggingEvent1.getLevel()).isEqualTo(Level.DEBUG); + List keyValuePairs = loggingEvent1.getKeyValuePairs(); + assertThat(keyValuePairs.size()).isEqualTo(4); + assertThat(keyValuePairs) + .contains( + RPC_NAME_KEY_VALUE_PAIR); + + for (KeyValuePair kvp : keyValuePairs) { + if (kvp.key.equals("request.payload")) { + Map payload = (Map) kvp.value; + assertThat(payload.get("content")).isEqualTo(ECHO_STRING); + assertThat(payload.get("requestId")).isNotNull(); + assertThat(payload.get("otherRequestId")).isNotNull(); + } + if (kvp.key.equals("request.headers")) { + Map headers = (Map) kvp.value; + assertThat(headers).containsKey("x-goog-api-version"); + assertThat(headers).containsKey("x-goog-api-client"); + } + } + + // logging event for response + ILoggingEvent loggingEvent2 = testAppender.events.get(1); + assertThat(loggingEvent2.getMessage()).isEqualTo("Received Grpc response"); + + assertThat(loggingEvent2.getLevel()).isEqualTo(Level.DEBUG); + List keyValuePairs2 = loggingEvent2.getKeyValuePairs(); + assertThat(keyValuePairs2.size()).isEqualTo(4); + assertThat(keyValuePairs2) + .containsAtLeast( + RESPONSE_STATUS_KEY_VALUE_PAIR_HTTP, + RPC_NAME_KEY_VALUE_PAIR); + for (KeyValuePair kvp : keyValuePairs2) { + if (kvp.key.equals("response.payload")) { + Map payload = (Map) kvp.value; + assertThat(payload.get("content")).isEqualTo(ECHO_STRING); + assertThat(payload.get("requestId")).isNotNull(); + assertThat(payload.get("otherRequestId")).isNotNull(); + } + if (kvp.key.equals("response.headers")) { + Map headers = (Map) kvp.value; + assertThat(headers.size()).isEqualTo(3); + } + } testAppender.stop(); + } + + private String echoGrpc(String value) { + EchoResponse response = grpcClient.echo(EchoRequest.newBuilder().setContent(value).build()); + return response.getContent(); + } + + private String echoHttpJson(String value) { + EchoResponse response = +httpjsonClient.echo(EchoRequest.newBuilder().setContent(value).build()); + return response.getContent(); + } +} diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java new file mode 100644 index 0000000000..93f2b0f288 --- /dev/null +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java @@ -0,0 +1,194 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.showcase.v1beta1.it.logging; + +import static com.google.common.truth.Truth.assertThat; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; +import com.google.api.gax.grpc.GrpcLoggingInterceptor; +import com.google.api.gax.httpjson.HttpJsonLoggingInterceptor; +import com.google.common.collect.ImmutableMap; +import com.google.showcase.v1beta1.EchoClient; +import com.google.showcase.v1beta1.EchoRequest; +import com.google.showcase.v1beta1.EchoResponse; +import com.google.showcase.v1beta1.it.util.TestClientInitializer; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +// import org.slf4j.event.KeyValuePair; + +// This test needs to run with GOOGLE_SDK_JAVA_LOGGING=true +public class ITLogging1x { + private static EchoClient grpcClient; + + private static EchoClient httpjsonClient; + + // private static final KeyValuePair SERVICE_NAME_KEY_VALUE_PAIR = + // new KeyValuePair("serviceName", "google.showcase.v1beta1.Echo"); + // private static final KeyValuePair RPC_NAME_KEY_VALUE_PAIR = + // new KeyValuePair("rpcName", "google.showcase.v1beta1.Echo/Echo"); + // private static final KeyValuePair RESPONSE_STATUS_KEY_VALUE_PAIR = + // new KeyValuePair("response.status", "OK"); + // private static final KeyValuePair RESPONSE_STATUS_KEY_VALUE_PAIR_HTTP = + // new KeyValuePair("response.status", "200"); + // + // private static final KeyValuePair RESPONSE_HEADERS_KEY_VALUE_PAIR = + // new KeyValuePair("response.headers", ImmutableMap.of("content-type", "application/grpc")); + + private static final String ECHO_STRING = "echo?"; + + private static Logger logger = LoggerFactory.getLogger(ITLogging1x.class); + + private TestAppender setupTestLogger(Class clazz) { + TestAppender testAppender = new TestAppender(); + testAppender.start(); + ListAppender listAppender = new ListAppender<>(); + listAppender.start(); + Logger logger = LoggerFactory.getLogger(clazz); + ((ch.qos.logback.classic.Logger) logger).setLevel(Level.DEBUG); + ((ch.qos.logback.classic.Logger) logger).addAppender(testAppender); + return testAppender; + } + + @BeforeAll + static void createClients() throws Exception { + // Create gRPC Echo Client + grpcClient = TestClientInitializer.createGrpcEchoClient(); + // Create Http JSON Echo Client + httpjsonClient = TestClientInitializer.createHttpJsonEchoClient(); + } + + @AfterAll + static void destroyClients() throws InterruptedException { + grpcClient.close(); + httpjsonClient.close(); + + grpcClient.awaitTermination(TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS); + httpjsonClient.awaitTermination( + TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS); + } + + @Test + void test() { + assertThat(logger.isInfoEnabled()).isTrue(); + assertThat(logger.isDebugEnabled()).isTrue(); + } + + // // only run when GOOGLE_SDK_JAVA_LOGGING!=true + // @Test + // void testloggingDisabled() { + // TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptor.class); + // assertThat(echoGrpc(ECHO_STRING)).isEqualTo(ECHO_STRING); + // + // assertThat(testAppender.events.size()).isEqualTo(0); + // } + + @Test + void testGrpc_receiveContent_logDebug() { + TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptor.class); + assertThat(echoGrpc(ECHO_STRING)).isEqualTo(ECHO_STRING); + + assertThat(testAppender.events.size()).isEqualTo(2); + // logging event for request + ILoggingEvent loggingEvent1 = testAppender.events.get(0); + assertThat(loggingEvent1.getMessage()).isEqualTo("Sending gRPC request"); + assertThat(loggingEvent1.getLevel()).isEqualTo(Level.DEBUG); + Map mdcPropertyMap = loggingEvent1.getMDCPropertyMap(); + assertThat(mdcPropertyMap) + .containsAtLeastEntriesIn( + ImmutableMap.of( + "serviceName", + "google.showcase.v1beta1.Echo", + "rpcName", + "google.showcase.v1beta1.Echo/Echo")); + assertThat(mdcPropertyMap).containsKey("request.headers"); + assertThat(mdcPropertyMap.get("request.headers")).startsWith("{\"x-goog-api-"); + + assertThat(mdcPropertyMap).containsKey("request.payload"); + assertThat(mdcPropertyMap.get("request.payload")) + .startsWith("{\"content\":\"echo?\",\"requestId\":"); + + // logging event for response + ILoggingEvent loggingEvent2 = testAppender.events.get(1); + assertThat(loggingEvent2.getMessage()).isEqualTo("Received Grpc response"); + assertThat(loggingEvent2.getLevel()).isEqualTo(Level.DEBUG); + Map responseMdcPropertyMap = loggingEvent2.getMDCPropertyMap(); + assertThat(responseMdcPropertyMap) + .containsAtLeastEntriesIn( + ImmutableMap.of( + "serviceName", + "google.showcase.v1beta1.Echo", + "rpcName", + "google.showcase.v1beta1.Echo/Echo", + "response.status", + "OK")); + assertThat(responseMdcPropertyMap).containsKey("response.payload"); + assertThat(responseMdcPropertyMap).containsKey("response.headers"); + + testAppender.stop(); + } + + @Test + void testHttpJson_receiveContent_logDebug() { + TestAppender testAppender = setupTestLogger(HttpJsonLoggingInterceptor.class); + assertThat(echoHttpJson(ECHO_STRING)).isEqualTo(ECHO_STRING); + assertThat(testAppender.events.size()).isEqualTo(2); + // logging event for request + ILoggingEvent loggingEvent1 = testAppender.events.get(0); + assertThat(loggingEvent1.getMessage()).isEqualTo("Sending gRPC request"); + assertThat(loggingEvent1.getLevel()).isEqualTo(Level.DEBUG); + Map mdcPropertyMap = loggingEvent1.getMDCPropertyMap(); + assertThat(mdcPropertyMap).containsEntry("rpcName", "google.showcase.v1beta1.Echo/Echo"); + assertThat(mdcPropertyMap).containsKey("request.url"); + assertThat(mdcPropertyMap).containsKey("request.headers"); + assertThat(mdcPropertyMap.get("request.headers")).startsWith("{\"x-goog-api-"); + assertThat(mdcPropertyMap).containsKey("request.payload"); + assertThat(mdcPropertyMap.get("request.payload")) + .startsWith("{\"content\":\"echo?\",\"requestId\":"); + + // logging event for response + ILoggingEvent loggingEvent2 = testAppender.events.get(1); + assertThat(loggingEvent2.getMessage()).isEqualTo("Received Grpc response"); + assertThat(loggingEvent2.getLevel()).isEqualTo(Level.DEBUG); + Map responseMdcPropertyMap = loggingEvent2.getMDCPropertyMap(); + assertThat(responseMdcPropertyMap) + .containsAtLeastEntriesIn( + ImmutableMap.of( + "rpcName", "google.showcase.v1beta1.Echo/Echo", + "response.status", "200")); + assertThat(responseMdcPropertyMap).containsKey("response.payload"); + assertThat(responseMdcPropertyMap).containsKey("response.headers"); + testAppender.stop(); + } + + private String echoGrpc(String value) { + EchoResponse response = grpcClient.echo(EchoRequest.newBuilder().setContent(value).build()); + return response.getContent(); + } + + private String echoHttpJson(String value) { + EchoResponse response = httpjsonClient.echo(EchoRequest.newBuilder().setContent(value).build()); + return response.getContent(); + } +} diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLoggingExtra.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLoggingExtra.java new file mode 100644 index 0000000000..1234b2a2c7 --- /dev/null +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLoggingExtra.java @@ -0,0 +1,3 @@ +package com.google.showcase.v1beta1.it.logging; + +public class ITLoggingExtra {} diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/util/TestAppender.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/TestAppender.java similarity index 96% rename from showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/util/TestAppender.java rename to showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/TestAppender.java index 0032b09d9d..eb9a6c47f7 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/util/TestAppender.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/TestAppender.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.showcase.v1beta1.it.util; +package com.google.showcase.v1beta1.it.logging; import ch.qos.logback.classic.spi.ILoggingEvent; diff --git a/showcase/gapic-showcase/src/test/resources/logback-test.xml b/showcase/gapic-showcase/src/test/resources/logback-test.xml index 66ed2652ea..25f8d63aca 100644 --- a/showcase/gapic-showcase/src/test/resources/logback-test.xml +++ b/showcase/gapic-showcase/src/test/resources/logback-test.xml @@ -1,6 +1,17 @@ - - - + + + + + + + + + + + + + + From 6ff54796c5fe73ea2c97206526c7f63c313c2965 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Fri, 7 Feb 2025 17:39:05 -0500 Subject: [PATCH 61/92] headings --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 2 +- .../httpjson/HttpJsonLoggingInterceptor.java | 2 +- .../com/google/api/gax/logging/LogData.java | 2 +- .../google/api/gax/logging/LoggingUtils.java | 9 +++--- .../api/gax/logging/TestMDCAdapter.java | 30 +++++++++++++++++++ .../api/gax/logging/TestServiceProvider.java | 30 +++++++++++++++++++ .../v1beta1/it/logging/ITLogging.java | 2 +- .../v1beta1/it/logging/ITLogging1x.java | 2 +- .../v1beta1/it/logging/TestAppender.java | 2 +- 9 files changed, 71 insertions(+), 10 deletions(-) diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index 6b0d55c5d7..ed6ee72c31 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index 399ae9dd35..4c3ff5674e 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java index 1bdc82ef26..d7b5b9d18c 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java index 4ae5580201..9c644e0675 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -110,9 +110,10 @@ static void logWithMDC( for (Entry entry : contextMap.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); - MDC.put(key, value.toString()); + // MDC.put(key, value.toString()); + MDC.put(key, value instanceof String ? (String) value : gson.toJson(value)); } - MDC.getMDCAdapter(); + // MDC.getMDCAdapter(); } switch (level) { case TRACE: @@ -203,7 +204,7 @@ public static void recordServiceRpcAndRequestHeaders( addIfNotEmpty(logDataBuilder::rpcName, rpcName); addIfNotEmpty(logDataBuilder::httpUrl, endpoint); } - if (logger.isInfoEnabled()) { + if (logger.isDebugEnabled()) { logDataBuilder.requestHeaders(requestHeaders); } }); diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestMDCAdapter.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestMDCAdapter.java index bbe038e85d..03319a8df7 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestMDCAdapter.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestMDCAdapter.java @@ -1,3 +1,33 @@ +/* + * 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.logging; import java.util.Deque; diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestServiceProvider.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestServiceProvider.java index 6576c80bfc..69cc43ba2a 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestServiceProvider.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestServiceProvider.java @@ -1,3 +1,33 @@ +/* + * 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.logging; import static org.mockito.ArgumentMatchers.anyString; diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java index de939b9b42..21d3a8bc6b 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java index 93f2b0f288..310c5969ea 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/TestAppender.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/TestAppender.java index eb9a6c47f7..cad4d9427a 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/TestAppender.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/TestAppender.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 82ead2b2caa8923be39f20a2410afe77684b5334 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Mon, 10 Feb 2025 16:44:56 -0500 Subject: [PATCH 62/92] refactor to isolate slf4j classes. --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 29 ++-- .../httpjson/HttpJsonLoggingInterceptor.java | 28 +-- gax-java/gax/pom.xml | 2 + .../LoggerProvider.java} | 30 ++-- .../api/gax/logging/LoggingHelpers.java | 98 +++++++++++ .../google/api/gax/logging/LoggingUtils.java | 76 +++----- .../api/gax/logging/LoggingEnabledTest.java | 77 ++++++++ .../api/gax/logging/LoggingHelpersTest.java | 93 ++++++++++ .../api/gax/logging/LoggingUtilsTest.java | 164 +++++------------- .../api/gax/logging/TestMDCAdapter.java | 4 + 10 files changed, 392 insertions(+), 209 deletions(-) rename gax-java/gax/src/main/java/com/google/api/gax/{rpc/internal/SystemEnvironmentProvider.java => logging/LoggerProvider.java} (73%) create mode 100644 gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingHelpers.java create mode 100644 gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingEnabledTest.java create mode 100644 gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingHelpersTest.java diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index ed6ee72c31..cc7c5aca83 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -30,16 +30,16 @@ 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 static com.google.api.gax.logging.LoggingHelpers.executeWithTryCatch; +import static com.google.api.gax.logging.LoggingHelpers.logRequest; +import static com.google.api.gax.logging.LoggingHelpers.logResponse; +import static com.google.api.gax.logging.LoggingHelpers.recordResponseHeaders; +import static com.google.api.gax.logging.LoggingHelpers.recordResponsePayload; +import static com.google.api.gax.logging.LoggingHelpers.recordServiceRpcAndRequestHeaders; import com.google.api.core.InternalApi; import com.google.api.gax.logging.LogData; -import com.google.api.gax.logging.LoggingUtils; +import com.google.api.gax.logging.LoggerProvider; import io.grpc.CallOptions; import io.grpc.Channel; import io.grpc.ClientCall; @@ -51,12 +51,12 @@ import io.grpc.Status; import java.util.HashMap; import java.util.Map; -import org.slf4j.Logger; @InternalApi public class GrpcLoggingInterceptor implements ClientInterceptor { - private static final Logger LOGGER = LoggingUtils.getLogger(GrpcLoggingInterceptor.class); + private static final LoggerProvider LOGGER_PROVIDER = + LoggerProvider.setLogger(GrpcLoggingInterceptor.class); ClientCall.Listener currentListener; // expose for test setup @@ -76,24 +76,25 @@ public void start(Listener responseListener, Metadata headers) { null, metadataHeadersToMap(headers), logDataBuilder, - LOGGER); + LOGGER_PROVIDER); SimpleForwardingClientCallListener responseLoggingListener = new SimpleForwardingClientCallListener(responseListener) { @Override public void onHeaders(Metadata headers) { - recordResponseHeaders(metadataHeadersToMap(headers), logDataBuilder, LOGGER); + recordResponseHeaders( + metadataHeadersToMap(headers), logDataBuilder, LOGGER_PROVIDER); super.onHeaders(headers); } @Override public void onMessage(RespT message) { - recordResponsePayload(message, logDataBuilder, LOGGER); + recordResponsePayload(message, logDataBuilder, LOGGER_PROVIDER); super.onMessage(message); } @Override public void onClose(Status status, Metadata trailers) { - logResponse(status.getCode().toString(), logDataBuilder, LOGGER); + logResponse(status.getCode().toString(), logDataBuilder, LOGGER_PROVIDER); super.onClose(status, trailers); } }; @@ -103,7 +104,7 @@ public void onClose(Status status, Metadata trailers) { @Override public void sendMessage(ReqT message) { - logRequest(message, logDataBuilder, LOGGER); + logRequest(message, logDataBuilder, LOGGER_PROVIDER); super.sendMessage(message); } }; diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index 4c3ff5674e..b880b4020d 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -30,25 +30,27 @@ package com.google.api.gax.httpjson; -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 static com.google.api.gax.logging.LoggingHelpers.logRequest; +import static com.google.api.gax.logging.LoggingHelpers.logResponse; +import static com.google.api.gax.logging.LoggingHelpers.recordResponseHeaders; +import static com.google.api.gax.logging.LoggingHelpers.recordResponsePayload; +import static com.google.api.gax.logging.LoggingHelpers.recordServiceRpcAndRequestHeaders; import com.google.api.core.InternalApi; import com.google.api.gax.httpjson.ForwardingHttpJsonClientCall.SimpleForwardingHttpJsonClientCall; import com.google.api.gax.httpjson.ForwardingHttpJsonClientCallListener.SimpleForwardingHttpJsonClientCallListener; import com.google.api.gax.logging.LogData; -import com.google.api.gax.logging.LoggingUtils; +import com.google.api.gax.logging.LoggerProvider; import java.util.HashMap; import java.util.Map; -import org.slf4j.Logger; @InternalApi public class HttpJsonLoggingInterceptor implements HttpJsonClientInterceptor { - private static final Logger LOGGER = LoggingUtils.getLogger(HttpJsonLoggingInterceptor.class); + // private static final Logger LOGGER = LoggingUtils.getLogger(HttpJsonLoggingInterceptor.class); + + private static final LoggerProvider LOGGER_PROVIDER = + LoggerProvider.setLogger(HttpJsonLoggingInterceptor.class); @Override public HttpJsonClientCall interceptCall( @@ -71,7 +73,7 @@ public void start( endpoint, httpJsonMetadataToMap(headers), logDataBuilder, - LOGGER); + LOGGER_PROVIDER); Listener forwardingResponseListener = new SimpleForwardingHttpJsonClientCallListener(responseListener) { @@ -79,19 +81,19 @@ public void start( @Override public void onHeaders(HttpJsonMetadata responseHeaders) { recordResponseHeaders( - httpJsonMetadataToMap(responseHeaders), logDataBuilder, LOGGER); + httpJsonMetadataToMap(responseHeaders), logDataBuilder, LOGGER_PROVIDER); super.onHeaders(responseHeaders); } @Override public void onMessage(RespT message) { - recordResponsePayload(message, logDataBuilder, LOGGER); + recordResponsePayload(message, logDataBuilder, LOGGER_PROVIDER); super.onMessage(message); } @Override public void onClose(int statusCode, HttpJsonMetadata trailers) { - logResponse(String.valueOf(statusCode), logDataBuilder, LOGGER); + logResponse(String.valueOf(statusCode), logDataBuilder, LOGGER_PROVIDER); super.onClose(statusCode, trailers); } }; @@ -100,7 +102,7 @@ public void onClose(int statusCode, HttpJsonMetadata trailers) { @Override public void sendMessage(ReqT message) { - logRequest(message, logDataBuilder, LOGGER); + logRequest(message, logDataBuilder, LOGGER_PROVIDER); super.sendMessage(message); } }; diff --git a/gax-java/gax/pom.xml b/gax-java/gax/pom.xml index 3ee706477d..4c199371cc 100644 --- a/gax-java/gax/pom.xml +++ b/gax-java/gax/pom.xml @@ -117,6 +117,7 @@ @{argLine} -Djava.util.logging.SimpleFormatter.format="%1$tY %1$tl:%1$tM:%1$tS.%1$tL %2$s %4$s: %5$s%6$s%n" !EndpointContextTest#endpointContextBuild_universeDomainEnvVarSet+endpointContextBuild_multipleUniverseDomainConfigurations_clientSettingsHasPriority + !LoggingEnabledTest @@ -132,6 +133,7 @@ maven-surefire-plugin EndpointContextTest#endpointContextBuild_universeDomainEnvVarSet+endpointContextBuild_multipleUniverseDomainConfigurations_clientSettingsHasPriority + !LoggingEnabledTest diff --git a/gax-java/gax/src/main/java/com/google/api/gax/rpc/internal/SystemEnvironmentProvider.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggerProvider.java similarity index 73% rename from gax-java/gax/src/main/java/com/google/api/gax/rpc/internal/SystemEnvironmentProvider.java rename to gax-java/gax/src/main/java/com/google/api/gax/logging/LoggerProvider.java index 29d45ba3c5..ac265f7105 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/rpc/internal/SystemEnvironmentProvider.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggerProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -28,23 +28,27 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.google.api.gax.rpc.internal; +package com.google.api.gax.logging; -import com.google.api.core.InternalApi; +import org.slf4j.Logger; -/** Represents the default system environment provider. */ -@InternalApi -public class SystemEnvironmentProvider implements EnvironmentProvider { - static final SystemEnvironmentProvider INSTANCE = new SystemEnvironmentProvider(); +public class LoggerProvider { - private SystemEnvironmentProvider() {} + private Logger logger; + private final Class clazz; - @Override - public String getenv(String name) { - return System.getenv(name); + private LoggerProvider(Class clazz) { + this.clazz = clazz; } - public static SystemEnvironmentProvider getInstance() { - return INSTANCE; + public static LoggerProvider setLogger(Class clazz) { + return new LoggerProvider(clazz); + } + + public Logger getLogger() { + if (logger == null) { + this.logger = LoggingUtils.getLogger(clazz); + } + return logger; } } diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingHelpers.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingHelpers.java new file mode 100644 index 0000000000..67e0b9876a --- /dev/null +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingHelpers.java @@ -0,0 +1,98 @@ +/* + * 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.logging; + +import java.util.Map; + +public class LoggingHelpers { + + private static boolean loggingEnabled = isLoggingEnabled(); + static final String GOOGLE_SDK_JAVA_LOGGING = "GOOGLE_SDK_JAVA_LOGGING"; + + static boolean isLoggingEnabled() { + String enableLogging = System.getenv(GOOGLE_SDK_JAVA_LOGGING); + return "true".equalsIgnoreCase(enableLogging); + } + + public static void recordServiceRpcAndRequestHeaders( + String serviceName, + String rpcName, + String endpoint, + Map requestHeaders, + LogData.Builder logDataBuilder, + LoggerProvider loggerProvider) { + if (loggingEnabled) { + LoggingUtils.recordServiceRpcAndRequestHeaders( + serviceName, rpcName, endpoint, requestHeaders, logDataBuilder, loggerProvider); + } + } + + public static void recordResponseHeaders( + Map headers, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { + if (loggingEnabled) { + LoggingUtils.recordResponseHeaders(headers, logDataBuilder, loggerProvider); + } + } + + public static void recordResponsePayload( + RespT message, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { + if (loggingEnabled) { + LoggingUtils.recordResponsePayload(message, logDataBuilder, loggerProvider); + } + } + + public static void logResponse( + String status, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { + if (loggingEnabled) { + LoggingUtils.logResponse(status, logDataBuilder, loggerProvider); + } + } + + public static void logRequest( + RespT message, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { + if (loggingEnabled) { + LoggingUtils.logRequest(message, logDataBuilder, loggerProvider); + } + } + + public static void executeWithTryCatch(ThrowingRunnable action) { + try { + action.run(); + } catch (Throwable t) { + // let logging exceptions fail silently + } + } + + @FunctionalInterface + public interface ThrowingRunnable { + void run() throws Throwable; + } +} diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java index 9c644e0675..e798a0c59c 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java @@ -31,8 +31,6 @@ package com.google.api.gax.logging; import com.google.api.core.InternalApi; -import com.google.api.gax.rpc.internal.EnvironmentProvider; -import com.google.api.gax.rpc.internal.SystemEnvironmentProvider; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import com.google.protobuf.InvalidProtocolBufferException; @@ -49,19 +47,11 @@ import org.slf4j.spi.LoggingEventBuilder; @InternalApi -public class LoggingUtils { +class LoggingUtils { - private static EnvironmentProvider environmentProvider = SystemEnvironmentProvider.getInstance(); private static final Logger NO_OP_LOGGER = org.slf4j.helpers.NOPLogger.NOP_LOGGER; - private static boolean loggingEnabled = isLoggingEnabled(); - static final String GOOGLE_SDK_JAVA_LOGGING = "GOOGLE_SDK_JAVA_LOGGING"; + private static boolean loggingEnabled = LoggingHelpers.isLoggingEnabled(); private static final Gson gson = new Gson(); - // expose this setter for testing purposes - static void setEnvironmentProvider(EnvironmentProvider provider) { - environmentProvider = provider; - // Recalculate LOGGING_ENABLED after setting the new provider - loggingEnabled = isLoggingEnabled(); - } private static boolean hasAddKeyValue; @@ -80,21 +70,22 @@ static boolean checkIfClazzAvailable(String clazzName) { private LoggingUtils() {} - public static Logger getLogger(Class clazz) { + static Logger getLogger(Class clazz) { return getLogger(clazz, new DefaultLoggerFactoryProvider()); } // constructor with LoggerFactoryProvider to make testing easier static Logger getLogger(Class clazz, LoggerFactoryProvider factoryProvider) { if (loggingEnabled) { - return factoryProvider.getLoggerFactory().getLogger(clazz.getName()); + ILoggerFactory loggerFactory = factoryProvider.getLoggerFactory(); + return loggerFactory.getLogger(clazz.getName()); } else { // use SLF4j's NOP logger regardless of bindings return NO_OP_LOGGER; } } - public static void log( + static void log( Logger logger, org.slf4j.event.Level level, Map contextMap, String message) { if (hasAddKeyValue) { logWithKeyValuePair(logger, level, contextMap, message); @@ -113,7 +104,6 @@ static void logWithMDC( // MDC.put(key, value.toString()); MDC.put(key, value instanceof String ? (String) value : gson.toJson(value)); } - // MDC.getMDCAdapter(); } switch (level) { case TRACE: @@ -167,11 +157,6 @@ private static void logWithKeyValuePair( loggingEventBuilder.log(message); } - static boolean isLoggingEnabled() { - String enableLogging = environmentProvider.getenv(GOOGLE_SDK_JAVA_LOGGING); - return "true".equalsIgnoreCase(enableLogging); - } - interface LoggerFactoryProvider { ILoggerFactory getLoggerFactory(); } @@ -184,21 +169,22 @@ public ILoggerFactory getLoggerFactory() { } // logging helper methods - public static Map messageToMapWithGson(Message message) + static Map messageToMapWithGson(Message message) throws InvalidProtocolBufferException { String json = JsonFormat.printer().print(message); return gson.fromJson(json, new TypeToken>() {}.getType()); } - public static void recordServiceRpcAndRequestHeaders( + static void recordServiceRpcAndRequestHeaders( String serviceName, String rpcName, String endpoint, Map requestHeaders, LogData.Builder logDataBuilder, - Logger logger) { - executeWithTryCatch( + LoggerProvider loggerProvider) { + LoggingHelpers.executeWithTryCatch( () -> { + Logger logger = loggerProvider.getLogger(); if (logger.isInfoEnabled()) { addIfNotEmpty(logDataBuilder::serviceName, serviceName); addIfNotEmpty(logDataBuilder::rpcName, rpcName); @@ -216,20 +202,22 @@ private static void addIfNotEmpty(Consumer setter, String value) { } } - public static void recordResponseHeaders( - Map headers, LogData.Builder logDataBuilder, Logger logger) { - executeWithTryCatch( + static void recordResponseHeaders( + Map headers, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { + LoggingHelpers.executeWithTryCatch( () -> { + Logger logger = loggerProvider.getLogger(); if (logger.isDebugEnabled()) { logDataBuilder.responseHeaders(headers); } }); } - public static void recordResponsePayload( - RespT message, LogData.Builder logDataBuilder, Logger logger) { - executeWithTryCatch( + static void recordResponsePayload( + RespT message, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { + LoggingHelpers.executeWithTryCatch( () -> { + Logger logger = loggerProvider.getLogger(); if (logger.isDebugEnabled()) { Map messageToMapWithGson = LoggingUtils.messageToMapWithGson((Message) message); @@ -239,9 +227,11 @@ public static void recordResponsePayload( }); } - public static void logResponse(String status, LogData.Builder logDataBuilder, Logger logger) { - executeWithTryCatch( + static void logResponse( + String status, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { + LoggingHelpers.executeWithTryCatch( () -> { + Logger logger = loggerProvider.getLogger(); if (logger.isInfoEnabled()) { logDataBuilder.responseStatus(status); } @@ -256,10 +246,11 @@ public static void logResponse(String status, LogData.Builder logDataBuilder, Lo }); } - public static void logRequest( - RespT message, LogData.Builder logDataBuilder, Logger logger) { - executeWithTryCatch( + static void logRequest( + RespT message, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { + LoggingHelpers.executeWithTryCatch( () -> { + Logger logger = loggerProvider.getLogger(); if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { LoggingUtils.log( logger, Level.INFO, logDataBuilder.build().toMapRequest(), "Sending gRPC request"); @@ -274,17 +265,4 @@ public static void logRequest( } }); } - - public static void executeWithTryCatch(ThrowingRunnable action) { - try { - action.run(); - } catch (Throwable t) { - // let logging exceptions fail silently - } - } - - @FunctionalInterface - public interface ThrowingRunnable { - void run() throws Throwable; - } } diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingEnabledTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingEnabledTest.java new file mode 100644 index 0000000000..4352d13eb4 --- /dev/null +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingEnabledTest.java @@ -0,0 +1,77 @@ +/* + * 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.logging; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.api.gax.logging.LoggingUtils.LoggerFactoryProvider; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.slf4j.ILoggerFactory; +import org.slf4j.Logger; +import org.slf4j.helpers.NOPLogger; +import org.slf4j.helpers.NOPLoggerFactory; + +// This test class should only run when env GOOGLE_SDK_JAVA_LOGGING = true +// it is excluded by default +class LoggingEnabledTest { + + @Test + void testIsLoggingEnabled_true() { + Assertions.assertTrue(LoggingHelpers.isLoggingEnabled()); + } + + // GOOGLE_SDK_JAVA_LOGGING = true + @Test + void testGetLogger_loggingEnabled_slf4jBindingPresent() { + // should get ILoggerFactory from TestServiceProvider + Logger logger = LoggingUtils.getLogger(LoggingUtilsTest.class); + Assertions.assertInstanceOf(Logger.class, logger); + Assertions.assertNotEquals(NOPLogger.class, logger.getClass()); + } + + // GOOGLE_SDK_JAVA_LOGGING = true + @Test + void testGetLogger_loggingEnabled_noBinding_shouldGetNOPLogger() { + // Create a mock LoggerFactoryProvider, mimic SLF4J's behavior to return NOPLoggerFactory when + // no binding + LoggerFactoryProvider mockLoggerFactoryProvider = mock(LoggerFactoryProvider.class); + ILoggerFactory nopLoggerFactory = new NOPLoggerFactory(); + when(mockLoggerFactoryProvider.getLoggerFactory()).thenReturn(nopLoggerFactory); + + // Use the mock LoggerFactoryProvider in getLogger() + Logger logger = LoggingUtils.getLogger(LoggingUtilsTest.class, mockLoggerFactoryProvider); + + // Assert that the returned logger is a NOPLogger + Assertions.assertInstanceOf(NOPLogger.class, logger); + } +} diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingHelpersTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingHelpersTest.java new file mode 100644 index 0000000000..e96063b269 --- /dev/null +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingHelpersTest.java @@ -0,0 +1,93 @@ +/* + * 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.logging; + +import static org.junit.jupiter.api.Assertions.*; + +import com.google.api.gax.logging.LoggingHelpers.ThrowingRunnable; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class LoggingHelpersTest { + + @Test + void testIsLoggingEnabled_defaultToFalse() { + // LoggingHelpers.setEnvironmentProvider(envProvider); + assertFalse(LoggingHelpers.isLoggingEnabled()); + } + + @Test + void testExecuteWithTryCatch_noException() { + assertDoesNotThrow( + () -> + LoggingHelpers.executeWithTryCatch( + () -> { + // Some code that should not throw an exception + int x = 5; + int y = 10; + int z = x + y; + assertEquals(15, z); + })); + } + + @Test + void testExecuteWithTryCatch_WithException() throws Throwable { + ThrowingRunnable action = Mockito.mock(ThrowingRunnable.class); + Mockito.doThrow(new RuntimeException("Test Exception")).when(action).run(); + assertDoesNotThrow(() -> LoggingHelpers.executeWithTryCatch(action)); + // Verify that the action was executed (despite the exception) + Mockito.verify(action).run(); + } + + @Test + void testExecuteWithTryCatch_WithNoSuchMethodError() throws Throwable { + ThrowingRunnable action = Mockito.mock(ThrowingRunnable.class); + Mockito.doThrow(new NoSuchMethodError("Test Error")).when(action).run(); + assertDoesNotThrow(() -> LoggingHelpers.executeWithTryCatch(action)); + // Verify that the action was executed (despite the error) + Mockito.verify(action).run(); + } + + @Test + void recordServiceRpcAndRequestHeaders() {} + + @Test + void recordResponseHeaders() {} + + @Test + void recordResponsePayload() {} + + @Test + void logResponse() {} + + @Test + void logRequest() {} +} diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java index ded2e6bf58..88ba11a160 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java @@ -31,7 +31,6 @@ package com.google.api.gax.logging; import static com.google.api.gax.logging.LoggingUtils.messageToMapWithGson; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -41,86 +40,29 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.google.api.gax.logging.LoggingUtils.LoggerFactoryProvider; -import com.google.api.gax.logging.LoggingUtils.ThrowingRunnable; -import com.google.api.gax.rpc.internal.EnvironmentProvider; import com.google.protobuf.Field; import com.google.protobuf.Field.Cardinality; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.Option; import java.util.HashMap; import java.util.Map; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import org.slf4j.ILoggerFactory; import org.slf4j.Logger; import org.slf4j.event.Level; import org.slf4j.helpers.NOPLogger; -import org.slf4j.helpers.NOPLoggerFactory; class LoggingUtilsTest { - // private static final Logger LOGGER = LoggerFactory.getLogger(LoggingUtilsTest.class); - private EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class); - - @Test - void testGetLogger_loggingEnabled_slf4jBindingPresent() { - Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("true"); - LoggingUtils.setEnvironmentProvider(envProvider); - // should get ILoggerFactory from TestServiceProvider - Logger logger = LoggingUtils.getLogger(LoggingUtilsTest.class); - Assertions.assertInstanceOf(Logger.class, logger); - Assertions.assertNotEquals(NOPLogger.class, logger.getClass()); - } - + // GOOGLE_SDK_JAVA_LOGGING = false @Test void testGetLogger_loggingDisabled_shouldGetNOPLogger() { - Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("false"); - LoggingUtils.setEnvironmentProvider(envProvider); - Logger logger = LoggingUtils.getLogger(LoggingUtilsTest.class); assertEquals(NOPLogger.class, logger.getClass()); assertFalse(logger.isInfoEnabled()); assertFalse(logger.isDebugEnabled()); } - @Test - void testGetLogger_loggingEnabled_noBinding_shouldGetNOPLogger() { - Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("true"); - LoggingUtils.setEnvironmentProvider(envProvider); - // Create a mock LoggerFactoryProvider, mimic SLF4J's behavior to return NOPLoggerFactory when - // no binding - LoggerFactoryProvider mockLoggerFactoryProvider = mock(LoggerFactoryProvider.class); - ILoggerFactory nopLoggerFactory = new NOPLoggerFactory(); - when(mockLoggerFactoryProvider.getLoggerFactory()).thenReturn(nopLoggerFactory); - - // Use the mock LoggerFactoryProvider in getLogger() - Logger logger = LoggingUtils.getLogger(LoggingUtilsTest.class, mockLoggerFactoryProvider); - - // Assert that the returned logger is a NOPLogger - Assertions.assertInstanceOf(NOPLogger.class, logger); - } - - @Test - void testIsLoggingEnabled_true() { - Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("true"); - LoggingUtils.setEnvironmentProvider(envProvider); - Assertions.assertTrue(LoggingUtils.isLoggingEnabled()); - Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("TRUE"); - LoggingUtils.setEnvironmentProvider(envProvider); - Assertions.assertTrue(LoggingUtils.isLoggingEnabled()); - Mockito.when(envProvider.getenv(LoggingUtils.GOOGLE_SDK_JAVA_LOGGING)).thenReturn("True"); - LoggingUtils.setEnvironmentProvider(envProvider); - Assertions.assertTrue(LoggingUtils.isLoggingEnabled()); - } - - @Test - void testIsLoggingEnabled_defaultToFalse() { - LoggingUtils.setEnvironmentProvider(envProvider); - assertFalse(LoggingUtils.isLoggingEnabled()); - } - @Test void testLog_slf4J2xLogger() { Map contextMap = new HashMap<>(); @@ -188,7 +130,12 @@ void testRecordServiceRpcAndRequestHeaders_infoEnabled() { TestLogger testLogger = new TestLogger("test-logger", true, true); LoggingUtils.recordServiceRpcAndRequestHeaders( - serviceName, rpcName, endpoint, requestHeaders, logDataBuilder, testLogger); + serviceName, + rpcName, + endpoint, + requestHeaders, + logDataBuilder, + setUpLoggerProviderMock(testLogger)); LogData logData = logDataBuilder.build(); assertEquals(serviceName, logData.serviceName()); @@ -197,6 +144,12 @@ void testRecordServiceRpcAndRequestHeaders_infoEnabled() { assertEquals(requestHeaders, logData.requestHeaders()); } + LoggerProvider setUpLoggerProviderMock(TestLogger testLogger) { + LoggerProvider loggerProvider = mock(LoggerProvider.class); + when(loggerProvider.getLogger()).thenReturn(testLogger); + return loggerProvider; + } + @Test void testRecordServiceRpcAndRequestHeaders_infoDisabled() { String serviceName = "testService"; @@ -210,8 +163,9 @@ void testRecordServiceRpcAndRequestHeaders_infoDisabled() { TestLogger testLogger = new TestLogger("test-logger", false, false); + LoggerProvider loggerProvider = setUpLoggerProviderMock(testLogger); LoggingUtils.recordServiceRpcAndRequestHeaders( - serviceName, rpcName, endpoint, requestHeaders, logDataBuilder, testLogger); + serviceName, rpcName, endpoint, requestHeaders, logDataBuilder, loggerProvider); LogData logData = logDataBuilder.build(); assertEquals(null, logData.serviceName()); @@ -229,7 +183,8 @@ void testRecordResponseHeaders_debugEnabled() { LogData.Builder logDataBuilder = LogData.builder(); TestLogger testLogger = new TestLogger("test-logger", true, true); - LoggingUtils.recordResponseHeaders(responseHeaders, logDataBuilder, testLogger); + LoggingUtils.recordResponseHeaders( + responseHeaders, logDataBuilder, setUpLoggerProviderMock(testLogger)); LogData logData = logDataBuilder.build(); assertEquals(responseHeaders, logData.responseHeaders()); @@ -244,7 +199,8 @@ void testRecordResponseHeaders_debugDisabled() { LogData.Builder logDataBuilder = LogData.builder(); TestLogger testLogger = new TestLogger("test-logger", true, false); - LoggingUtils.recordResponseHeaders(responseHeaders, logDataBuilder, testLogger); + LoggingUtils.recordResponseHeaders( + responseHeaders, logDataBuilder, setUpLoggerProviderMock(testLogger)); LogData logData = logDataBuilder.build(); assertEquals(null, logData.responseHeaders()); @@ -263,7 +219,7 @@ void testRecordResponsePayload_debugEnabled() { LogData.Builder logDataBuilder = LogData.builder(); TestLogger testLogger = new TestLogger("test-logger", true, true); - LoggingUtils.recordResponsePayload(field, logDataBuilder, testLogger); + LoggingUtils.recordResponsePayload(field, logDataBuilder, setUpLoggerProviderMock(testLogger)); LogData logData = logDataBuilder.build(); assertEquals(2, logData.responsePayload().size()); @@ -282,14 +238,14 @@ void testLogRequest_infoEnabled_debugDisabled() { LogData.builder().serviceName("service-name").rpcName("rpc-name"); when(logDataBuilder.build()).thenReturn(testLogDataBuilder.build()); - Logger logger = new TestLogger("test", true, false); - LoggingUtils.logRequest(message, logDataBuilder, logger); + TestLogger testLogger = new TestLogger("test", true, false); + LoggingUtils.logRequest(message, logDataBuilder, setUpLoggerProviderMock(testLogger)); - assertEquals(2, ((TestLogger) logger).keyValuePairsMap.size()); - assertEquals("Sending gRPC request", ((TestLogger) logger).messageList.get(0)); + assertEquals(2, testLogger.keyValuePairsMap.size()); + assertEquals("Sending gRPC request", testLogger.messageList.get(0)); verify(logDataBuilder, never()).requestPayload(anyMap()); // Ensure debug path is not taken - assertEquals(Level.INFO, ((TestLogger) logger).level); + assertEquals(Level.INFO, testLogger.level); } @Test @@ -309,17 +265,17 @@ void testLogRequest_debugEnabled() throws InvalidProtocolBufferException { .requestPayload(LoggingUtils.messageToMapWithGson(field)); when(logDataBuilder.build()).thenReturn(testLogDataBuilder.build()); - TestLogger logger = new TestLogger("test-logger", true, true); + TestLogger testLogger = new TestLogger("test-logger", true, true); - LoggingUtils.logRequest(field, logDataBuilder, logger); + LoggingUtils.logRequest(field, logDataBuilder, setUpLoggerProviderMock(testLogger)); verify(logDataBuilder).requestPayload(LoggingUtils.messageToMapWithGson(field)); - assertEquals(3, ((TestLogger) logger).keyValuePairsMap.size()); - assertEquals(2, ((Map) ((TestLogger) logger).keyValuePairsMap.get("request.payload")).size()); - assertEquals("Sending gRPC request", ((TestLogger) logger).messageList.get(0)); + assertEquals(3, testLogger.keyValuePairsMap.size()); + assertEquals(2, ((Map) testLogger.keyValuePairsMap.get("request.payload")).size()); + assertEquals("Sending gRPC request", testLogger.messageList.get(0)); - assertEquals(Level.DEBUG, ((TestLogger) logger).level); + assertEquals(Level.DEBUG, testLogger.level); } @Test @@ -334,16 +290,16 @@ void testLogResponse_infoEnabled_debugDisabled() { .rpcName("rpc-name") .responsePayload(responseData); when(logDataBuilder.build()).thenReturn(testLogDataBuilder.build()); - TestLogger logger = new TestLogger("test-logger", true, false); + TestLogger testLogger = new TestLogger("test-logger", true, false); - LoggingUtils.logResponse(status, logDataBuilder, logger); + LoggingUtils.logResponse(status, logDataBuilder, setUpLoggerProviderMock(testLogger)); verify(logDataBuilder).responseStatus(status); - assertEquals("Received Grpc response", ((TestLogger) logger).messageList.get(0)); - assertEquals(3, ((TestLogger) logger).keyValuePairsMap.size()); - assertTrue(((TestLogger) logger).keyValuePairsMap.containsKey("response.payload")); - assertEquals(Level.INFO, ((TestLogger) logger).level); - Map keyValuePairsMap = ((TestLogger) logger).keyValuePairsMap; + assertEquals("Received Grpc response", ((TestLogger) testLogger).messageList.get(0)); + assertEquals(3, ((TestLogger) testLogger).keyValuePairsMap.size()); + assertTrue(((TestLogger) testLogger).keyValuePairsMap.containsKey("response.payload")); + assertEquals(Level.INFO, ((TestLogger) testLogger).level); + Map keyValuePairsMap = ((TestLogger) testLogger).keyValuePairsMap; } @Test @@ -358,48 +314,16 @@ void testLogResponse_infoEnabled_debugEnabled() { .rpcName("rpc-name") .responsePayload(responseData); when(logDataBuilder.build()).thenReturn(testLogDataBuilder.build()); - TestLogger logger = new TestLogger("test-logger", true, true); + TestLogger testLogger = new TestLogger("test-logger", true, true); - LoggingUtils.logResponse(status, logDataBuilder, logger); + LoggingUtils.logResponse(status, logDataBuilder, setUpLoggerProviderMock(testLogger)); verify(logDataBuilder).responseStatus(status); - assertEquals("Received Grpc response", ((TestLogger) logger).messageList.get(0)); - assertEquals(3, ((TestLogger) logger).keyValuePairsMap.size()); - assertTrue(((TestLogger) logger).keyValuePairsMap.containsKey("response.payload")); - - assertEquals(Level.DEBUG, ((TestLogger) logger).level); - } + assertEquals("Received Grpc response", ((TestLogger) testLogger).messageList.get(0)); + assertEquals(3, ((TestLogger) testLogger).keyValuePairsMap.size()); + assertTrue(((TestLogger) testLogger).keyValuePairsMap.containsKey("response.payload")); - @Test - void testExecuteWithTryCatch_noException() { - assertDoesNotThrow( - () -> - LoggingUtils.executeWithTryCatch( - () -> { - // Some code that should not throw an exception - int x = 5; - int y = 10; - int z = x + y; - assertEquals(15, z); - })); - } - - @Test - void testExecuteWithTryCatch_WithException() throws Throwable { - ThrowingRunnable action = Mockito.mock(ThrowingRunnable.class); - Mockito.doThrow(new RuntimeException("Test Exception")).when(action).run(); - assertDoesNotThrow(() -> LoggingUtils.executeWithTryCatch(action)); - // Verify that the action was executed (despite the exception) - Mockito.verify(action).run(); - } - - @Test - void testExecuteWithTryCatch_WithNoSuchMethodError() throws Throwable { - ThrowingRunnable action = Mockito.mock(ThrowingRunnable.class); - Mockito.doThrow(new NoSuchMethodError("Test Error")).when(action).run(); - assertDoesNotThrow(() -> LoggingUtils.executeWithTryCatch(action)); - // Verify that the action was executed (despite the error) - Mockito.verify(action).run(); + assertEquals(Level.DEBUG, ((TestLogger) testLogger).level); } @Test diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestMDCAdapter.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestMDCAdapter.java index 03319a8df7..5423050b3f 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestMDCAdapter.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestMDCAdapter.java @@ -35,6 +35,10 @@ import java.util.Map; import org.slf4j.spi.MDCAdapter; +/** + * this adapter is for unit test only. It is setup via TestServiceProvider + * to test behavior when LogWithMDC + */ public class TestMDCAdapter implements MDCAdapter { Map mdcValues = new HashMap<>(); From b5b6e9427fd29e64d2e1837f67b4f8a4ba8e5ee1 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Mon, 10 Feb 2025 16:49:15 -0500 Subject: [PATCH 63/92] rename classes. --- .../api/gax/grpc/GrpcLoggingInterceptor.java | 12 +- .../httpjson/HttpJsonLoggingInterceptor.java | 10 +- .../api/gax/logging/LoggerProvider.java | 4 +- .../api/gax/logging/LoggingHelpers.java | 98 ----- .../google/api/gax/logging/LoggingUtils.java | 244 ++----------- .../google/api/gax/logging/Slf4jUtils.java | 268 ++++++++++++++ .../api/gax/logging/LoggingEnabledTest.java | 8 +- .../api/gax/logging/LoggingHelpersTest.java | 93 ----- .../api/gax/logging/LoggingUtilsTest.java | 307 ++-------------- .../api/gax/logging/Slf4jUtilsTest.java | 334 ++++++++++++++++++ 10 files changed, 691 insertions(+), 687 deletions(-) delete mode 100644 gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingHelpers.java create mode 100644 gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java delete mode 100644 gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingHelpersTest.java create mode 100644 gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index cc7c5aca83..a18c518e7c 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -30,12 +30,12 @@ package com.google.api.gax.grpc; -import static com.google.api.gax.logging.LoggingHelpers.executeWithTryCatch; -import static com.google.api.gax.logging.LoggingHelpers.logRequest; -import static com.google.api.gax.logging.LoggingHelpers.logResponse; -import static com.google.api.gax.logging.LoggingHelpers.recordResponseHeaders; -import static com.google.api.gax.logging.LoggingHelpers.recordResponsePayload; -import static com.google.api.gax.logging.LoggingHelpers.recordServiceRpcAndRequestHeaders; +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; diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index b880b4020d..04b5cc68b0 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -30,11 +30,11 @@ package com.google.api.gax.httpjson; -import static com.google.api.gax.logging.LoggingHelpers.logRequest; -import static com.google.api.gax.logging.LoggingHelpers.logResponse; -import static com.google.api.gax.logging.LoggingHelpers.recordResponseHeaders; -import static com.google.api.gax.logging.LoggingHelpers.recordResponsePayload; -import static com.google.api.gax.logging.LoggingHelpers.recordServiceRpcAndRequestHeaders; +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.httpjson.ForwardingHttpJsonClientCall.SimpleForwardingHttpJsonClientCall; diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggerProvider.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggerProvider.java index ac265f7105..27bf47c45f 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggerProvider.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggerProvider.java @@ -30,8 +30,10 @@ package com.google.api.gax.logging; +import com.google.api.core.InternalApi; import org.slf4j.Logger; +@InternalApi public class LoggerProvider { private Logger logger; @@ -47,7 +49,7 @@ public static LoggerProvider setLogger(Class clazz) { public Logger getLogger() { if (logger == null) { - this.logger = LoggingUtils.getLogger(clazz); + this.logger = Slf4jUtils.getLogger(clazz); } return logger; } diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingHelpers.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingHelpers.java deleted file mode 100644 index 67e0b9876a..0000000000 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingHelpers.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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.logging; - -import java.util.Map; - -public class LoggingHelpers { - - private static boolean loggingEnabled = isLoggingEnabled(); - static final String GOOGLE_SDK_JAVA_LOGGING = "GOOGLE_SDK_JAVA_LOGGING"; - - static boolean isLoggingEnabled() { - String enableLogging = System.getenv(GOOGLE_SDK_JAVA_LOGGING); - return "true".equalsIgnoreCase(enableLogging); - } - - public static void recordServiceRpcAndRequestHeaders( - String serviceName, - String rpcName, - String endpoint, - Map requestHeaders, - LogData.Builder logDataBuilder, - LoggerProvider loggerProvider) { - if (loggingEnabled) { - LoggingUtils.recordServiceRpcAndRequestHeaders( - serviceName, rpcName, endpoint, requestHeaders, logDataBuilder, loggerProvider); - } - } - - public static void recordResponseHeaders( - Map headers, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { - if (loggingEnabled) { - LoggingUtils.recordResponseHeaders(headers, logDataBuilder, loggerProvider); - } - } - - public static void recordResponsePayload( - RespT message, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { - if (loggingEnabled) { - LoggingUtils.recordResponsePayload(message, logDataBuilder, loggerProvider); - } - } - - public static void logResponse( - String status, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { - if (loggingEnabled) { - LoggingUtils.logResponse(status, logDataBuilder, loggerProvider); - } - } - - public static void logRequest( - RespT message, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { - if (loggingEnabled) { - LoggingUtils.logRequest(message, logDataBuilder, loggerProvider); - } - } - - public static void executeWithTryCatch(ThrowingRunnable action) { - try { - action.run(); - } catch (Throwable t) { - // let logging exceptions fail silently - } - } - - @FunctionalInterface - public interface ThrowingRunnable { - void run() throws Throwable; - } -} diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java index e798a0c59c..3c5d645de9 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java @@ -31,238 +31,70 @@ package com.google.api.gax.logging; import com.google.api.core.InternalApi; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; -import com.google.protobuf.InvalidProtocolBufferException; -import com.google.protobuf.Message; -import com.google.protobuf.util.JsonFormat; import java.util.Map; -import java.util.Map.Entry; -import java.util.function.Consumer; -import org.slf4j.ILoggerFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.MDC; -import org.slf4j.event.Level; -import org.slf4j.spi.LoggingEventBuilder; @InternalApi -class LoggingUtils { +public class LoggingUtils { - private static final Logger NO_OP_LOGGER = org.slf4j.helpers.NOPLogger.NOP_LOGGER; - private static boolean loggingEnabled = LoggingHelpers.isLoggingEnabled(); - private static final Gson gson = new Gson(); + private static boolean loggingEnabled = isLoggingEnabled(); + static final String GOOGLE_SDK_JAVA_LOGGING = "GOOGLE_SDK_JAVA_LOGGING"; - private static boolean hasAddKeyValue; - - static { - hasAddKeyValue = checkIfClazzAvailable("org.slf4j.event.KeyValuePair"); - } - - static boolean checkIfClazzAvailable(String clazzName) { - try { - Class.forName(clazzName); - return true; // SLF4j 2.x or later - } catch (ClassNotFoundException e) { - return false; // SLF4j 1.x or earlier - } - } - - private LoggingUtils() {} - - static Logger getLogger(Class clazz) { - return getLogger(clazz, new DefaultLoggerFactoryProvider()); - } - - // constructor with LoggerFactoryProvider to make testing easier - static Logger getLogger(Class clazz, LoggerFactoryProvider factoryProvider) { - if (loggingEnabled) { - ILoggerFactory loggerFactory = factoryProvider.getLoggerFactory(); - return loggerFactory.getLogger(clazz.getName()); - } else { - // use SLF4j's NOP logger regardless of bindings - return NO_OP_LOGGER; - } + static boolean isLoggingEnabled() { + String enableLogging = System.getenv(GOOGLE_SDK_JAVA_LOGGING); + return "true".equalsIgnoreCase(enableLogging); } - static void log( - Logger logger, org.slf4j.event.Level level, Map contextMap, String message) { - if (hasAddKeyValue) { - logWithKeyValuePair(logger, level, contextMap, message); - } else { - logWithMDC(logger, level, contextMap, message); - } - } - - // exposed for testing - static void logWithMDC( - Logger logger, org.slf4j.event.Level level, Map contextMap, String message) { - if (!contextMap.isEmpty()) { - for (Entry entry : contextMap.entrySet()) { - String key = entry.getKey(); - Object value = entry.getValue(); - // MDC.put(key, value.toString()); - MDC.put(key, value instanceof String ? (String) value : gson.toJson(value)); - } - } - switch (level) { - case TRACE: - logger.trace(message); - break; - case DEBUG: - logger.debug(message); - break; - case INFO: - logger.info(message); - break; - case WARN: - logger.warn(message); - break; - case ERROR: - logger.error(message); - break; - default: - logger.debug(message); - // Default to DEBUG level - } - if (!contextMap.isEmpty()) { - MDC.clear(); - } - } - - private static void logWithKeyValuePair( - Logger logger, org.slf4j.event.Level level, Map contextMap, String message) { - LoggingEventBuilder loggingEventBuilder; - switch (level) { - case TRACE: - loggingEventBuilder = logger.atTrace(); - break; - case DEBUG: - loggingEventBuilder = logger.atDebug(); - break; - case INFO: - loggingEventBuilder = logger.atInfo(); - break; - case WARN: - loggingEventBuilder = logger.atWarn(); - break; - case ERROR: - loggingEventBuilder = logger.atError(); - break; - default: - loggingEventBuilder = logger.atDebug(); - // Default to DEBUG level - } - contextMap.forEach(loggingEventBuilder::addKeyValue); - loggingEventBuilder.log(message); - } - - interface LoggerFactoryProvider { - ILoggerFactory getLoggerFactory(); - } - - static class DefaultLoggerFactoryProvider implements LoggerFactoryProvider { - @Override - public ILoggerFactory getLoggerFactory() { - return LoggerFactory.getILoggerFactory(); - } - } - - // logging helper methods - static Map messageToMapWithGson(Message message) - throws InvalidProtocolBufferException { - String json = JsonFormat.printer().print(message); - return gson.fromJson(json, new TypeToken>() {}.getType()); - } - - static void recordServiceRpcAndRequestHeaders( + public static void recordServiceRpcAndRequestHeaders( String serviceName, String rpcName, String endpoint, Map requestHeaders, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { - LoggingHelpers.executeWithTryCatch( - () -> { - Logger logger = loggerProvider.getLogger(); - if (logger.isInfoEnabled()) { - addIfNotEmpty(logDataBuilder::serviceName, serviceName); - addIfNotEmpty(logDataBuilder::rpcName, rpcName); - addIfNotEmpty(logDataBuilder::httpUrl, endpoint); - } - if (logger.isDebugEnabled()) { - logDataBuilder.requestHeaders(requestHeaders); - } - }); - } - - private static void addIfNotEmpty(Consumer setter, String value) { - if (value != null && !value.isEmpty()) { - setter.accept(value); + if (loggingEnabled) { + Slf4jUtils.recordServiceRpcAndRequestHeaders( + serviceName, rpcName, endpoint, requestHeaders, logDataBuilder, loggerProvider); } } - static void recordResponseHeaders( + public static void recordResponseHeaders( Map headers, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { - LoggingHelpers.executeWithTryCatch( - () -> { - Logger logger = loggerProvider.getLogger(); - if (logger.isDebugEnabled()) { - logDataBuilder.responseHeaders(headers); - } - }); + if (loggingEnabled) { + Slf4jUtils.recordResponseHeaders(headers, logDataBuilder, loggerProvider); + } } - static void recordResponsePayload( + public static void recordResponsePayload( RespT message, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { - LoggingHelpers.executeWithTryCatch( - () -> { - Logger logger = loggerProvider.getLogger(); - if (logger.isDebugEnabled()) { - Map messageToMapWithGson = - LoggingUtils.messageToMapWithGson((Message) message); - - logDataBuilder.responsePayload(messageToMapWithGson); - } - }); + if (loggingEnabled) { + Slf4jUtils.recordResponsePayload(message, logDataBuilder, loggerProvider); + } } - static void logResponse( + public static void logResponse( String status, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { - LoggingHelpers.executeWithTryCatch( - () -> { - Logger logger = loggerProvider.getLogger(); - if (logger.isInfoEnabled()) { - logDataBuilder.responseStatus(status); - } - if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { - Map responseData = logDataBuilder.build().toMapResponse(); - LoggingUtils.log(logger, Level.INFO, responseData, "Received Grpc response"); - } - if (logger.isDebugEnabled()) { - Map responsedDetailsMap = logDataBuilder.build().toMapResponse(); - LoggingUtils.log(logger, Level.DEBUG, responsedDetailsMap, "Received Grpc response"); - } - }); + if (loggingEnabled) { + Slf4jUtils.logResponse(status, logDataBuilder, loggerProvider); + } } - static void logRequest( + public static void logRequest( RespT message, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { - LoggingHelpers.executeWithTryCatch( - () -> { - Logger logger = loggerProvider.getLogger(); - if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { - LoggingUtils.log( - logger, Level.INFO, logDataBuilder.build().toMapRequest(), "Sending gRPC request"); - } - if (logger.isDebugEnabled()) { - Map messageToMapWithGson = - LoggingUtils.messageToMapWithGson((Message) message); + if (loggingEnabled) { + Slf4jUtils.logRequest(message, logDataBuilder, loggerProvider); + } + } + + public static void executeWithTryCatch(ThrowingRunnable action) { + try { + action.run(); + } catch (Throwable t) { + // let logging exceptions fail silently + } + } - logDataBuilder.requestPayload(messageToMapWithGson); - Map requestDetailsMap = logDataBuilder.build().toMapRequest(); - LoggingUtils.log(logger, Level.DEBUG, requestDetailsMap, "Sending gRPC request"); - } - }); + @FunctionalInterface + public interface ThrowingRunnable { + void run() throws Throwable; } } diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java new file mode 100644 index 0000000000..a7c81d5521 --- /dev/null +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java @@ -0,0 +1,268 @@ +/* + * 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.logging; + +import com.google.api.core.InternalApi; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.Message; +import com.google.protobuf.util.JsonFormat; +import java.util.Map; +import java.util.Map.Entry; +import java.util.function.Consumer; +import org.slf4j.ILoggerFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.slf4j.event.Level; +import org.slf4j.spi.LoggingEventBuilder; + +@InternalApi +class Slf4jUtils { + + private static final Logger NO_OP_LOGGER = org.slf4j.helpers.NOPLogger.NOP_LOGGER; + private static boolean loggingEnabled = LoggingUtils.isLoggingEnabled(); + private static final Gson gson = new Gson(); + + private static boolean hasAddKeyValue; + + static { + hasAddKeyValue = checkIfClazzAvailable("org.slf4j.event.KeyValuePair"); + } + + static boolean checkIfClazzAvailable(String clazzName) { + try { + Class.forName(clazzName); + return true; // SLF4j 2.x or later + } catch (ClassNotFoundException e) { + return false; // SLF4j 1.x or earlier + } + } + + private Slf4jUtils() {} + + static Logger getLogger(Class clazz) { + return getLogger(clazz, new DefaultLoggerFactoryProvider()); + } + + // constructor with LoggerFactoryProvider to make testing easier + static Logger getLogger(Class clazz, LoggerFactoryProvider factoryProvider) { + if (loggingEnabled) { + ILoggerFactory loggerFactory = factoryProvider.getLoggerFactory(); + return loggerFactory.getLogger(clazz.getName()); + } else { + // use SLF4j's NOP logger regardless of bindings + return NO_OP_LOGGER; + } + } + + static void log( + Logger logger, org.slf4j.event.Level level, Map contextMap, String message) { + if (hasAddKeyValue) { + logWithKeyValuePair(logger, level, contextMap, message); + } else { + logWithMDC(logger, level, contextMap, message); + } + } + + // exposed for testing + static void logWithMDC( + Logger logger, org.slf4j.event.Level level, Map contextMap, String message) { + if (!contextMap.isEmpty()) { + for (Entry entry : contextMap.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + // MDC.put(key, value.toString()); + MDC.put(key, value instanceof String ? (String) value : gson.toJson(value)); + } + } + switch (level) { + case TRACE: + logger.trace(message); + break; + case DEBUG: + logger.debug(message); + break; + case INFO: + logger.info(message); + break; + case WARN: + logger.warn(message); + break; + case ERROR: + logger.error(message); + break; + default: + logger.debug(message); + // Default to DEBUG level + } + if (!contextMap.isEmpty()) { + MDC.clear(); + } + } + + private static void logWithKeyValuePair( + Logger logger, org.slf4j.event.Level level, Map contextMap, String message) { + LoggingEventBuilder loggingEventBuilder; + switch (level) { + case TRACE: + loggingEventBuilder = logger.atTrace(); + break; + case DEBUG: + loggingEventBuilder = logger.atDebug(); + break; + case INFO: + loggingEventBuilder = logger.atInfo(); + break; + case WARN: + loggingEventBuilder = logger.atWarn(); + break; + case ERROR: + loggingEventBuilder = logger.atError(); + break; + default: + loggingEventBuilder = logger.atDebug(); + // Default to DEBUG level + } + contextMap.forEach(loggingEventBuilder::addKeyValue); + loggingEventBuilder.log(message); + } + + interface LoggerFactoryProvider { + ILoggerFactory getLoggerFactory(); + } + + static class DefaultLoggerFactoryProvider implements LoggerFactoryProvider { + @Override + public ILoggerFactory getLoggerFactory() { + return LoggerFactory.getILoggerFactory(); + } + } + + // logging helper methods + static Map messageToMapWithGson(Message message) + throws InvalidProtocolBufferException { + String json = JsonFormat.printer().print(message); + return gson.fromJson(json, new TypeToken>() {}.getType()); + } + + static void recordServiceRpcAndRequestHeaders( + String serviceName, + String rpcName, + String endpoint, + Map requestHeaders, + LogData.Builder logDataBuilder, + LoggerProvider loggerProvider) { + LoggingUtils.executeWithTryCatch( + () -> { + Logger logger = loggerProvider.getLogger(); + if (logger.isInfoEnabled()) { + addIfNotEmpty(logDataBuilder::serviceName, serviceName); + addIfNotEmpty(logDataBuilder::rpcName, rpcName); + addIfNotEmpty(logDataBuilder::httpUrl, endpoint); + } + if (logger.isDebugEnabled()) { + logDataBuilder.requestHeaders(requestHeaders); + } + }); + } + + private static void addIfNotEmpty(Consumer setter, String value) { + if (value != null && !value.isEmpty()) { + setter.accept(value); + } + } + + static void recordResponseHeaders( + Map headers, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { + LoggingUtils.executeWithTryCatch( + () -> { + Logger logger = loggerProvider.getLogger(); + if (logger.isDebugEnabled()) { + logDataBuilder.responseHeaders(headers); + } + }); + } + + static void recordResponsePayload( + RespT message, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { + LoggingUtils.executeWithTryCatch( + () -> { + Logger logger = loggerProvider.getLogger(); + if (logger.isDebugEnabled()) { + Map messageToMapWithGson = + Slf4jUtils.messageToMapWithGson((Message) message); + + logDataBuilder.responsePayload(messageToMapWithGson); + } + }); + } + + static void logResponse( + String status, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { + LoggingUtils.executeWithTryCatch( + () -> { + Logger logger = loggerProvider.getLogger(); + if (logger.isInfoEnabled()) { + logDataBuilder.responseStatus(status); + } + if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { + Map responseData = logDataBuilder.build().toMapResponse(); + Slf4jUtils.log(logger, Level.INFO, responseData, "Received Grpc response"); + } + if (logger.isDebugEnabled()) { + Map responsedDetailsMap = logDataBuilder.build().toMapResponse(); + Slf4jUtils.log(logger, Level.DEBUG, responsedDetailsMap, "Received Grpc response"); + } + }); + } + + static void logRequest( + RespT message, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { + LoggingUtils.executeWithTryCatch( + () -> { + Logger logger = loggerProvider.getLogger(); + if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { + Slf4jUtils.log( + logger, Level.INFO, logDataBuilder.build().toMapRequest(), "Sending gRPC request"); + } + if (logger.isDebugEnabled()) { + Map messageToMapWithGson = + Slf4jUtils.messageToMapWithGson((Message) message); + + logDataBuilder.requestPayload(messageToMapWithGson); + Map requestDetailsMap = logDataBuilder.build().toMapRequest(); + Slf4jUtils.log(logger, Level.DEBUG, requestDetailsMap, "Sending gRPC request"); + } + }); + } +} diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingEnabledTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingEnabledTest.java index 4352d13eb4..b786ba1dff 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingEnabledTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingEnabledTest.java @@ -33,7 +33,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.google.api.gax.logging.LoggingUtils.LoggerFactoryProvider; +import com.google.api.gax.logging.Slf4jUtils.LoggerFactoryProvider; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.slf4j.ILoggerFactory; @@ -47,14 +47,14 @@ class LoggingEnabledTest { @Test void testIsLoggingEnabled_true() { - Assertions.assertTrue(LoggingHelpers.isLoggingEnabled()); + Assertions.assertTrue(LoggingUtils.isLoggingEnabled()); } // GOOGLE_SDK_JAVA_LOGGING = true @Test void testGetLogger_loggingEnabled_slf4jBindingPresent() { // should get ILoggerFactory from TestServiceProvider - Logger logger = LoggingUtils.getLogger(LoggingUtilsTest.class); + Logger logger = Slf4jUtils.getLogger(Slf4jUtilsTest.class); Assertions.assertInstanceOf(Logger.class, logger); Assertions.assertNotEquals(NOPLogger.class, logger.getClass()); } @@ -69,7 +69,7 @@ void testGetLogger_loggingEnabled_noBinding_shouldGetNOPLogger() { when(mockLoggerFactoryProvider.getLoggerFactory()).thenReturn(nopLoggerFactory); // Use the mock LoggerFactoryProvider in getLogger() - Logger logger = LoggingUtils.getLogger(LoggingUtilsTest.class, mockLoggerFactoryProvider); + Logger logger = Slf4jUtils.getLogger(Slf4jUtilsTest.class, mockLoggerFactoryProvider); // Assert that the returned logger is a NOPLogger Assertions.assertInstanceOf(NOPLogger.class, logger); diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingHelpersTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingHelpersTest.java deleted file mode 100644 index e96063b269..0000000000 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingHelpersTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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.logging; - -import static org.junit.jupiter.api.Assertions.*; - -import com.google.api.gax.logging.LoggingHelpers.ThrowingRunnable; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -class LoggingHelpersTest { - - @Test - void testIsLoggingEnabled_defaultToFalse() { - // LoggingHelpers.setEnvironmentProvider(envProvider); - assertFalse(LoggingHelpers.isLoggingEnabled()); - } - - @Test - void testExecuteWithTryCatch_noException() { - assertDoesNotThrow( - () -> - LoggingHelpers.executeWithTryCatch( - () -> { - // Some code that should not throw an exception - int x = 5; - int y = 10; - int z = x + y; - assertEquals(15, z); - })); - } - - @Test - void testExecuteWithTryCatch_WithException() throws Throwable { - ThrowingRunnable action = Mockito.mock(ThrowingRunnable.class); - Mockito.doThrow(new RuntimeException("Test Exception")).when(action).run(); - assertDoesNotThrow(() -> LoggingHelpers.executeWithTryCatch(action)); - // Verify that the action was executed (despite the exception) - Mockito.verify(action).run(); - } - - @Test - void testExecuteWithTryCatch_WithNoSuchMethodError() throws Throwable { - ThrowingRunnable action = Mockito.mock(ThrowingRunnable.class); - Mockito.doThrow(new NoSuchMethodError("Test Error")).when(action).run(); - assertDoesNotThrow(() -> LoggingHelpers.executeWithTryCatch(action)); - // Verify that the action was executed (despite the error) - Mockito.verify(action).run(); - } - - @Test - void recordServiceRpcAndRequestHeaders() {} - - @Test - void recordResponseHeaders() {} - - @Test - void recordResponsePayload() {} - - @Test - void logResponse() {} - - @Test - void logRequest() {} -} diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java index 88ba11a160..19e8a96d1d 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java @@ -30,305 +30,64 @@ package com.google.api.gax.logging; -import static com.google.api.gax.logging.LoggingUtils.messageToMapWithGson; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.anyMap; -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 static org.junit.jupiter.api.Assertions.*; -import com.google.protobuf.Field; -import com.google.protobuf.Field.Cardinality; -import com.google.protobuf.InvalidProtocolBufferException; -import com.google.protobuf.Option; -import java.util.HashMap; -import java.util.Map; +import com.google.api.gax.logging.LoggingUtils.ThrowingRunnable; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import org.slf4j.Logger; -import org.slf4j.event.Level; -import org.slf4j.helpers.NOPLogger; class LoggingUtilsTest { - // GOOGLE_SDK_JAVA_LOGGING = false @Test - void testGetLogger_loggingDisabled_shouldGetNOPLogger() { - Logger logger = LoggingUtils.getLogger(LoggingUtilsTest.class); - assertEquals(NOPLogger.class, logger.getClass()); - assertFalse(logger.isInfoEnabled()); - assertFalse(logger.isDebugEnabled()); + void testIsLoggingEnabled_defaultToFalse() { + // LoggingUtils.setEnvironmentProvider(envProvider); + assertFalse(LoggingUtils.isLoggingEnabled()); } @Test - void testLog_slf4J2xLogger() { - Map contextMap = new HashMap<>(); - contextMap.put("key1", "value"); - contextMap.put("key2", 123); - String message = "Test message"; - TestLogger testLogger = new TestLogger("test-logger"); - LoggingUtils.log(testLogger, org.slf4j.event.Level.DEBUG, contextMap, message); - - assertEquals(message, testLogger.messageList.get(0)); - - assertEquals("value", testLogger.keyValuePairsMap.get("key1")); - assertEquals(123, testLogger.keyValuePairsMap.get("key2")); + void testExecuteWithTryCatch_noException() { + assertDoesNotThrow( + () -> + LoggingUtils.executeWithTryCatch( + () -> { + // Some code that should not throw an exception + int x = 5; + int y = 10; + int z = x + y; + assertEquals(15, z); + })); } @Test - void testLogWithMDC_InfoLevel_VerifyMDC() { - // this test replies on TestMDCApapter and TestServiceProvider - TestLogger testLogger = new TestLogger("test-logger"); - Map contextMap = new HashMap<>(); - contextMap.put("key1", "value1"); - contextMap.put("key2", 123); - String message = "Test message"; - - // need a service provider - LoggingUtils.logWithMDC(testLogger, Level.INFO, contextMap, message); - - Map mdcMap = testLogger.MDCMap; - - assertEquals(2, mdcMap.size()); - assertEquals("value1", mdcMap.get("key1")); - assertEquals("123", mdcMap.get("key2")); - - assertEquals(message, testLogger.messageList.get(0)); + void testExecuteWithTryCatch_WithException() throws Throwable { + ThrowingRunnable action = Mockito.mock(ThrowingRunnable.class); + Mockito.doThrow(new RuntimeException("Test Exception")).when(action).run(); + assertDoesNotThrow(() -> LoggingUtils.executeWithTryCatch(action)); + // Verify that the action was executed (despite the exception) + Mockito.verify(action).run(); } @Test - void testMessageToMap_ValidMessage() throws InvalidProtocolBufferException { - Field field = - Field.newBuilder() - .setNumber(2) - .setName("field_name1") - .addOptions(Option.newBuilder().setName("opt_name1").build()) - .addOptions(Option.newBuilder().setName("opt_name2").build()) - .setCardinality(Cardinality.CARDINALITY_OPTIONAL) - .build(); - - Map map = messageToMapWithGson(field); - - assertEquals("field_name1", map.get("name")); - assertEquals(2.0, map.get("number")); // Gson converts ints to doubles by default + void testExecuteWithTryCatch_WithNoSuchMethodError() throws Throwable { + ThrowingRunnable action = Mockito.mock(ThrowingRunnable.class); + Mockito.doThrow(new NoSuchMethodError("Test Error")).when(action).run(); + assertDoesNotThrow(() -> LoggingUtils.executeWithTryCatch(action)); + // Verify that the action was executed (despite the error) + Mockito.verify(action).run(); } @Test - void testRecordServiceRpcAndRequestHeaders_infoEnabled() { - String serviceName = "testService"; - String rpcName = "testRpc"; - String endpoint = "http://test.com/endpoint"; - Map requestHeaders = new HashMap<>(); - requestHeaders.put("header1", "value1"); - requestHeaders.put("header2", "value2"); - - LogData.Builder logDataBuilder = LogData.builder(); - - TestLogger testLogger = new TestLogger("test-logger", true, true); - - LoggingUtils.recordServiceRpcAndRequestHeaders( - serviceName, - rpcName, - endpoint, - requestHeaders, - logDataBuilder, - setUpLoggerProviderMock(testLogger)); - - LogData logData = logDataBuilder.build(); - assertEquals(serviceName, logData.serviceName()); - assertEquals(rpcName, logData.rpcName()); - assertEquals(endpoint, logData.httpUrl()); - assertEquals(requestHeaders, logData.requestHeaders()); - } - - LoggerProvider setUpLoggerProviderMock(TestLogger testLogger) { - LoggerProvider loggerProvider = mock(LoggerProvider.class); - when(loggerProvider.getLogger()).thenReturn(testLogger); - return loggerProvider; - } + void recordServiceRpcAndRequestHeaders() {} @Test - void testRecordServiceRpcAndRequestHeaders_infoDisabled() { - String serviceName = "testService"; - String rpcName = "testRpc"; - String endpoint = "http://test.com/endpoint"; - Map requestHeaders = new HashMap<>(); - requestHeaders.put("header1", "value1"); - requestHeaders.put("header2", "value2"); - - LogData.Builder logDataBuilder = LogData.builder(); - - TestLogger testLogger = new TestLogger("test-logger", false, false); - - LoggerProvider loggerProvider = setUpLoggerProviderMock(testLogger); - LoggingUtils.recordServiceRpcAndRequestHeaders( - serviceName, rpcName, endpoint, requestHeaders, logDataBuilder, loggerProvider); - - LogData logData = logDataBuilder.build(); - assertEquals(null, logData.serviceName()); - assertEquals(null, logData.rpcName()); - assertEquals(null, logData.httpUrl()); - assertEquals(null, logData.requestHeaders()); - } + void recordResponseHeaders() {} @Test - void testRecordResponseHeaders_debugEnabled() { - Map responseHeaders = new HashMap<>(); - responseHeaders.put("header1", "value1"); - responseHeaders.put("header2", "value2"); - - LogData.Builder logDataBuilder = LogData.builder(); - TestLogger testLogger = new TestLogger("test-logger", true, true); - - LoggingUtils.recordResponseHeaders( - responseHeaders, logDataBuilder, setUpLoggerProviderMock(testLogger)); - - LogData logData = logDataBuilder.build(); - assertEquals(responseHeaders, logData.responseHeaders()); - } + void recordResponsePayload() {} @Test - void testRecordResponseHeaders_debugDisabled() { - Map responseHeaders = new HashMap<>(); - responseHeaders.put("header1", "value1"); - responseHeaders.put("header2", "value2"); - - LogData.Builder logDataBuilder = LogData.builder(); - TestLogger testLogger = new TestLogger("test-logger", true, false); - - LoggingUtils.recordResponseHeaders( - responseHeaders, logDataBuilder, setUpLoggerProviderMock(testLogger)); - - LogData logData = logDataBuilder.build(); - assertEquals(null, logData.responseHeaders()); - } + void logResponse() {} @Test - void testRecordResponsePayload_debugEnabled() { - - Field field = - Field.newBuilder() - .setName("field_name1") - .addOptions(Option.newBuilder().setName("opt_name1").build()) - .addOptions(Option.newBuilder().setName("opt_name2").build()) - .build(); - - LogData.Builder logDataBuilder = LogData.builder(); - TestLogger testLogger = new TestLogger("test-logger", true, true); - - LoggingUtils.recordResponsePayload(field, logDataBuilder, setUpLoggerProviderMock(testLogger)); - - LogData logData = logDataBuilder.build(); - assertEquals(2, logData.responsePayload().size()); - assertEquals("field_name1", logData.responsePayload().get("name")); - assertEquals( - "[{name=opt_name1}, {name=opt_name2}]", - logData.responsePayload().get("options").toString()); - } - - @Test - void testLogRequest_infoEnabled_debugDisabled() { - Object message = new Object(); // not used in info path - LogData.Builder logDataBuilder = Mockito.mock(LogData.Builder.class); - - LogData.Builder testLogDataBuilder = - LogData.builder().serviceName("service-name").rpcName("rpc-name"); - when(logDataBuilder.build()).thenReturn(testLogDataBuilder.build()); - - TestLogger testLogger = new TestLogger("test", true, false); - LoggingUtils.logRequest(message, logDataBuilder, setUpLoggerProviderMock(testLogger)); - - assertEquals(2, testLogger.keyValuePairsMap.size()); - assertEquals("Sending gRPC request", testLogger.messageList.get(0)); - verify(logDataBuilder, never()).requestPayload(anyMap()); // Ensure debug path is not taken - - assertEquals(Level.INFO, testLogger.level); - } - - @Test - void testLogRequest_debugEnabled() throws InvalidProtocolBufferException { - Field field = - Field.newBuilder() - .setName("field_name1") - .addOptions(Option.newBuilder().setName("opt_name1").build()) - .addOptions(Option.newBuilder().setName("opt_name2").build()) - .build(); - - LogData.Builder logDataBuilder = Mockito.mock(LogData.Builder.class); - LogData.Builder testLogDataBuilder = - LogData.builder() - .serviceName("service-name") - .rpcName("rpc-name") - .requestPayload(LoggingUtils.messageToMapWithGson(field)); - when(logDataBuilder.build()).thenReturn(testLogDataBuilder.build()); - - TestLogger testLogger = new TestLogger("test-logger", true, true); - - LoggingUtils.logRequest(field, logDataBuilder, setUpLoggerProviderMock(testLogger)); - - verify(logDataBuilder).requestPayload(LoggingUtils.messageToMapWithGson(field)); - - assertEquals(3, testLogger.keyValuePairsMap.size()); - assertEquals(2, ((Map) testLogger.keyValuePairsMap.get("request.payload")).size()); - assertEquals("Sending gRPC request", testLogger.messageList.get(0)); - - assertEquals(Level.DEBUG, testLogger.level); - } - - @Test - void testLogResponse_infoEnabled_debugDisabled() { - String status = "OK"; - Map responseData = new HashMap<>(); - - LogData.Builder logDataBuilder = Mockito.mock(LogData.Builder.class); - LogData.Builder testLogDataBuilder = - LogData.builder() - .serviceName("service-name") - .rpcName("rpc-name") - .responsePayload(responseData); - when(logDataBuilder.build()).thenReturn(testLogDataBuilder.build()); - TestLogger testLogger = new TestLogger("test-logger", true, false); - - LoggingUtils.logResponse(status, logDataBuilder, setUpLoggerProviderMock(testLogger)); - - verify(logDataBuilder).responseStatus(status); - assertEquals("Received Grpc response", ((TestLogger) testLogger).messageList.get(0)); - assertEquals(3, ((TestLogger) testLogger).keyValuePairsMap.size()); - assertTrue(((TestLogger) testLogger).keyValuePairsMap.containsKey("response.payload")); - assertEquals(Level.INFO, ((TestLogger) testLogger).level); - Map keyValuePairsMap = ((TestLogger) testLogger).keyValuePairsMap; - } - - @Test - void testLogResponse_infoEnabled_debugEnabled() { - String status = "OK"; - Map responseData = new HashMap<>(); - - LogData.Builder logDataBuilder = Mockito.mock(LogData.Builder.class); - LogData.Builder testLogDataBuilder = - LogData.builder() - .serviceName("service-name") - .rpcName("rpc-name") - .responsePayload(responseData); - when(logDataBuilder.build()).thenReturn(testLogDataBuilder.build()); - TestLogger testLogger = new TestLogger("test-logger", true, true); - - LoggingUtils.logResponse(status, logDataBuilder, setUpLoggerProviderMock(testLogger)); - - verify(logDataBuilder).responseStatus(status); - assertEquals("Received Grpc response", ((TestLogger) testLogger).messageList.get(0)); - assertEquals(3, ((TestLogger) testLogger).keyValuePairsMap.size()); - assertTrue(((TestLogger) testLogger).keyValuePairsMap.containsKey("response.payload")); - - assertEquals(Level.DEBUG, ((TestLogger) testLogger).level); - } - - @Test - void testCheckIfClazzAvailable() { - assertFalse(LoggingUtils.checkIfClazzAvailable("fake.class.should.not.be.in.classpath")); - assertTrue(LoggingUtils.checkIfClazzAvailable("org.slf4j.event.KeyValuePair")); - } + void logRequest() {} } diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java new file mode 100644 index 0000000000..f9e334fc0d --- /dev/null +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java @@ -0,0 +1,334 @@ +/* + * 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.logging; + +import static com.google.api.gax.logging.Slf4jUtils.messageToMapWithGson; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyMap; +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 com.google.protobuf.Field; +import com.google.protobuf.Field.Cardinality; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.Option; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.slf4j.Logger; +import org.slf4j.event.Level; +import org.slf4j.helpers.NOPLogger; + +class Slf4jUtilsTest { + + // GOOGLE_SDK_JAVA_LOGGING = false + @Test + void testGetLogger_loggingDisabled_shouldGetNOPLogger() { + Logger logger = Slf4jUtils.getLogger(Slf4jUtilsTest.class); + assertEquals(NOPLogger.class, logger.getClass()); + assertFalse(logger.isInfoEnabled()); + assertFalse(logger.isDebugEnabled()); + } + + @Test + void testLog_slf4J2xLogger() { + Map contextMap = new HashMap<>(); + contextMap.put("key1", "value"); + contextMap.put("key2", 123); + String message = "Test message"; + TestLogger testLogger = new TestLogger("test-logger"); + Slf4jUtils.log(testLogger, org.slf4j.event.Level.DEBUG, contextMap, message); + + assertEquals(message, testLogger.messageList.get(0)); + + assertEquals("value", testLogger.keyValuePairsMap.get("key1")); + assertEquals(123, testLogger.keyValuePairsMap.get("key2")); + } + + @Test + void testLogWithMDC_InfoLevel_VerifyMDC() { + // this test replies on TestMDCApapter and TestServiceProvider + TestLogger testLogger = new TestLogger("test-logger"); + Map contextMap = new HashMap<>(); + contextMap.put("key1", "value1"); + contextMap.put("key2", 123); + String message = "Test message"; + + // need a service provider + Slf4jUtils.logWithMDC(testLogger, Level.INFO, contextMap, message); + + Map mdcMap = testLogger.MDCMap; + + assertEquals(2, mdcMap.size()); + assertEquals("value1", mdcMap.get("key1")); + assertEquals("123", mdcMap.get("key2")); + + assertEquals(message, testLogger.messageList.get(0)); + } + + @Test + void testMessageToMap_ValidMessage() throws InvalidProtocolBufferException { + Field field = + Field.newBuilder() + .setNumber(2) + .setName("field_name1") + .addOptions(Option.newBuilder().setName("opt_name1").build()) + .addOptions(Option.newBuilder().setName("opt_name2").build()) + .setCardinality(Cardinality.CARDINALITY_OPTIONAL) + .build(); + + Map map = messageToMapWithGson(field); + + assertEquals("field_name1", map.get("name")); + assertEquals(2.0, map.get("number")); // Gson converts ints to doubles by default + } + + @Test + void testRecordServiceRpcAndRequestHeaders_infoEnabled() { + String serviceName = "testService"; + String rpcName = "testRpc"; + String endpoint = "http://test.com/endpoint"; + Map requestHeaders = new HashMap<>(); + requestHeaders.put("header1", "value1"); + requestHeaders.put("header2", "value2"); + + LogData.Builder logDataBuilder = LogData.builder(); + + TestLogger testLogger = new TestLogger("test-logger", true, true); + + Slf4jUtils.recordServiceRpcAndRequestHeaders( + serviceName, + rpcName, + endpoint, + requestHeaders, + logDataBuilder, + setUpLoggerProviderMock(testLogger)); + + LogData logData = logDataBuilder.build(); + assertEquals(serviceName, logData.serviceName()); + assertEquals(rpcName, logData.rpcName()); + assertEquals(endpoint, logData.httpUrl()); + assertEquals(requestHeaders, logData.requestHeaders()); + } + + LoggerProvider setUpLoggerProviderMock(TestLogger testLogger) { + LoggerProvider loggerProvider = mock(LoggerProvider.class); + when(loggerProvider.getLogger()).thenReturn(testLogger); + return loggerProvider; + } + + @Test + void testRecordServiceRpcAndRequestHeaders_infoDisabled() { + String serviceName = "testService"; + String rpcName = "testRpc"; + String endpoint = "http://test.com/endpoint"; + Map requestHeaders = new HashMap<>(); + requestHeaders.put("header1", "value1"); + requestHeaders.put("header2", "value2"); + + LogData.Builder logDataBuilder = LogData.builder(); + + TestLogger testLogger = new TestLogger("test-logger", false, false); + + LoggerProvider loggerProvider = setUpLoggerProviderMock(testLogger); + Slf4jUtils.recordServiceRpcAndRequestHeaders( + serviceName, rpcName, endpoint, requestHeaders, logDataBuilder, loggerProvider); + + LogData logData = logDataBuilder.build(); + assertEquals(null, logData.serviceName()); + assertEquals(null, logData.rpcName()); + assertEquals(null, logData.httpUrl()); + assertEquals(null, logData.requestHeaders()); + } + + @Test + void testRecordResponseHeaders_debugEnabled() { + Map responseHeaders = new HashMap<>(); + responseHeaders.put("header1", "value1"); + responseHeaders.put("header2", "value2"); + + LogData.Builder logDataBuilder = LogData.builder(); + TestLogger testLogger = new TestLogger("test-logger", true, true); + + Slf4jUtils.recordResponseHeaders( + responseHeaders, logDataBuilder, setUpLoggerProviderMock(testLogger)); + + LogData logData = logDataBuilder.build(); + assertEquals(responseHeaders, logData.responseHeaders()); + } + + @Test + void testRecordResponseHeaders_debugDisabled() { + Map responseHeaders = new HashMap<>(); + responseHeaders.put("header1", "value1"); + responseHeaders.put("header2", "value2"); + + LogData.Builder logDataBuilder = LogData.builder(); + TestLogger testLogger = new TestLogger("test-logger", true, false); + + Slf4jUtils.recordResponseHeaders( + responseHeaders, logDataBuilder, setUpLoggerProviderMock(testLogger)); + + LogData logData = logDataBuilder.build(); + assertEquals(null, logData.responseHeaders()); + } + + @Test + void testRecordResponsePayload_debugEnabled() { + + Field field = + Field.newBuilder() + .setName("field_name1") + .addOptions(Option.newBuilder().setName("opt_name1").build()) + .addOptions(Option.newBuilder().setName("opt_name2").build()) + .build(); + + LogData.Builder logDataBuilder = LogData.builder(); + TestLogger testLogger = new TestLogger("test-logger", true, true); + + Slf4jUtils.recordResponsePayload(field, logDataBuilder, setUpLoggerProviderMock(testLogger)); + + LogData logData = logDataBuilder.build(); + assertEquals(2, logData.responsePayload().size()); + assertEquals("field_name1", logData.responsePayload().get("name")); + assertEquals( + "[{name=opt_name1}, {name=opt_name2}]", + logData.responsePayload().get("options").toString()); + } + + @Test + void testLogRequest_infoEnabled_debugDisabled() { + Object message = new Object(); // not used in info path + LogData.Builder logDataBuilder = Mockito.mock(LogData.Builder.class); + + LogData.Builder testLogDataBuilder = + LogData.builder().serviceName("service-name").rpcName("rpc-name"); + when(logDataBuilder.build()).thenReturn(testLogDataBuilder.build()); + + TestLogger testLogger = new TestLogger("test", true, false); + Slf4jUtils.logRequest(message, logDataBuilder, setUpLoggerProviderMock(testLogger)); + + assertEquals(2, testLogger.keyValuePairsMap.size()); + assertEquals("Sending gRPC request", testLogger.messageList.get(0)); + verify(logDataBuilder, never()).requestPayload(anyMap()); // Ensure debug path is not taken + + assertEquals(Level.INFO, testLogger.level); + } + + @Test + void testLogRequest_debugEnabled() throws InvalidProtocolBufferException { + Field field = + Field.newBuilder() + .setName("field_name1") + .addOptions(Option.newBuilder().setName("opt_name1").build()) + .addOptions(Option.newBuilder().setName("opt_name2").build()) + .build(); + + LogData.Builder logDataBuilder = Mockito.mock(LogData.Builder.class); + LogData.Builder testLogDataBuilder = + LogData.builder() + .serviceName("service-name") + .rpcName("rpc-name") + .requestPayload(Slf4jUtils.messageToMapWithGson(field)); + when(logDataBuilder.build()).thenReturn(testLogDataBuilder.build()); + + TestLogger testLogger = new TestLogger("test-logger", true, true); + + Slf4jUtils.logRequest(field, logDataBuilder, setUpLoggerProviderMock(testLogger)); + + verify(logDataBuilder).requestPayload(Slf4jUtils.messageToMapWithGson(field)); + + assertEquals(3, testLogger.keyValuePairsMap.size()); + assertEquals(2, ((Map) testLogger.keyValuePairsMap.get("request.payload")).size()); + assertEquals("Sending gRPC request", testLogger.messageList.get(0)); + + assertEquals(Level.DEBUG, testLogger.level); + } + + @Test + void testLogResponse_infoEnabled_debugDisabled() { + String status = "OK"; + Map responseData = new HashMap<>(); + + LogData.Builder logDataBuilder = Mockito.mock(LogData.Builder.class); + LogData.Builder testLogDataBuilder = + LogData.builder() + .serviceName("service-name") + .rpcName("rpc-name") + .responsePayload(responseData); + when(logDataBuilder.build()).thenReturn(testLogDataBuilder.build()); + TestLogger testLogger = new TestLogger("test-logger", true, false); + + Slf4jUtils.logResponse(status, logDataBuilder, setUpLoggerProviderMock(testLogger)); + + verify(logDataBuilder).responseStatus(status); + assertEquals("Received Grpc response", ((TestLogger) testLogger).messageList.get(0)); + assertEquals(3, ((TestLogger) testLogger).keyValuePairsMap.size()); + assertTrue(((TestLogger) testLogger).keyValuePairsMap.containsKey("response.payload")); + assertEquals(Level.INFO, ((TestLogger) testLogger).level); + Map keyValuePairsMap = ((TestLogger) testLogger).keyValuePairsMap; + } + + @Test + void testLogResponse_infoEnabled_debugEnabled() { + String status = "OK"; + Map responseData = new HashMap<>(); + + LogData.Builder logDataBuilder = Mockito.mock(LogData.Builder.class); + LogData.Builder testLogDataBuilder = + LogData.builder() + .serviceName("service-name") + .rpcName("rpc-name") + .responsePayload(responseData); + when(logDataBuilder.build()).thenReturn(testLogDataBuilder.build()); + TestLogger testLogger = new TestLogger("test-logger", true, true); + + Slf4jUtils.logResponse(status, logDataBuilder, setUpLoggerProviderMock(testLogger)); + + verify(logDataBuilder).responseStatus(status); + assertEquals("Received Grpc response", ((TestLogger) testLogger).messageList.get(0)); + assertEquals(3, ((TestLogger) testLogger).keyValuePairsMap.size()); + assertTrue(((TestLogger) testLogger).keyValuePairsMap.containsKey("response.payload")); + + assertEquals(Level.DEBUG, ((TestLogger) testLogger).level); + } + + @Test + void testCheckIfClazzAvailable() { + assertFalse(Slf4jUtils.checkIfClazzAvailable("fake.class.should.not.be.in.classpath")); + assertTrue(Slf4jUtils.checkIfClazzAvailable("org.slf4j.event.KeyValuePair")); + } +} From 5db888411ba6d1004887f9b0d68fc9cb94cb618a Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Mon, 10 Feb 2025 17:09:45 -0500 Subject: [PATCH 64/92] fix surefire config to exclude/include logging tests. add to PenvVarTest ci. --- .github/workflows/ci.yaml | 1 + gax-java/gax/pom.xml | 6 ++---- .../main/java/com/google/api/gax/logging/Slf4jUtils.java | 1 + .../java/com/google/api/gax/logging/TestMDCAdapter.java | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7a7040c634..28adb76ba9 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -133,6 +133,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" diff --git a/gax-java/gax/pom.xml b/gax-java/gax/pom.xml index 4c199371cc..46eff29cb6 100644 --- a/gax-java/gax/pom.xml +++ b/gax-java/gax/pom.xml @@ -116,8 +116,7 @@ @{argLine} -Djava.util.logging.SimpleFormatter.format="%1$tY %1$tl:%1$tM:%1$tS.%1$tL %2$s %4$s: %5$s%6$s%n" - !EndpointContextTest#endpointContextBuild_universeDomainEnvVarSet+endpointContextBuild_multipleUniverseDomainConfigurations_clientSettingsHasPriority - !LoggingEnabledTest + !EndpointContextTest#endpointContextBuild_universeDomainEnvVarSet+endpointContextBuild_multipleUniverseDomainConfigurations_clientSettingsHasPriority,!LoggingEnabledTest @@ -132,8 +131,7 @@ org.apache.maven.plugins maven-surefire-plugin - EndpointContextTest#endpointContextBuild_universeDomainEnvVarSet+endpointContextBuild_multipleUniverseDomainConfigurations_clientSettingsHasPriority - !LoggingEnabledTest + EndpointContextTest#endpointContextBuild_universeDomainEnvVarSet+endpointContextBuild_multipleUniverseDomainConfigurations_clientSettingsHasPriority,LoggingEnabledTest diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java index a7c81d5521..c5f994eb15 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java @@ -46,6 +46,7 @@ import org.slf4j.event.Level; import org.slf4j.spi.LoggingEventBuilder; +// Actual interaction with SLF4J happens only in this class. @InternalApi class Slf4jUtils { diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestMDCAdapter.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestMDCAdapter.java index 5423050b3f..8d6f2d0c1a 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestMDCAdapter.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestMDCAdapter.java @@ -36,8 +36,8 @@ import org.slf4j.spi.MDCAdapter; /** - * this adapter is for unit test only. It is setup via TestServiceProvider - * to test behavior when LogWithMDC + * this adapter is for unit test only. It is setup via TestServiceProvider to test behavior when + * LogWithMDC */ public class TestMDCAdapter implements MDCAdapter { Map mdcValues = new HashMap<>(); From 56316d3fcf3d4c0f004eaab0115336d919de518d Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 11 Feb 2025 10:30:47 -0500 Subject: [PATCH 65/92] showcase test setups. --- showcase/gapic-showcase/pom.xml | 86 ++++++++++++++----- .../v1beta1/it/logging/ITLogging.java | 35 +++----- .../v1beta1/it/logging/ITLogging1x.java | 4 +- .../v1beta1/it/logging/ITLoggingDisabled.java | 84 ++++++++++++++++++ showcase/pom.xml | 36 +++++--- 5 files changed, 186 insertions(+), 59 deletions(-) create mode 100644 showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLoggingDisabled.java diff --git a/showcase/gapic-showcase/pom.xml b/showcase/gapic-showcase/pom.xml index 3d0e892268..032dfaf77d 100644 --- a/showcase/gapic-showcase/pom.xml +++ b/showcase/gapic-showcase/pom.xml @@ -73,23 +73,14 @@ - - - - - - - - - - + @@ -252,9 +243,7 @@ slf4j2_logback - - - + slf4j2_logback @@ -296,9 +285,11 @@ org.apache.maven.plugins maven-compiler-plugin - - **/ITLogging.java - + + **/com/google/showcase/v1beta1/it/*.java + **/com/google/showcase/v1beta1/it/logging/ITLoggingDisabled.java + **/com/google/showcase/v1beta1/it/logging/ITLogging1x.java + @@ -306,10 +297,7 @@ slf4j1_logback - - - - + slf4j1_logback @@ -344,7 +332,6 @@ 2.14.2 - @@ -352,7 +339,62 @@ maven-compiler-plugin - **/ITLogging.java + **/com/google/showcase/v1beta1/it/*.java + **/com/google/showcase/v1beta1/it/logging/ITLoggingDisabled.java + **/com/google/showcase/v1beta1/it/logging/ITLogging.java + + + + + + + + disabledLogging + + + + disable_logging + + + + + org.slf4j + slf4j-api + 1.7.36 + + + ch.qos.logback + logback-classic + 1.2.13 + test + + + ch.qos.logback + logback-core + 1.2.13 + test + + + net.logstash.logback + logstash-logback-encoder + 7.3 + + + com.fasterxml.jackson.core + jackson-databind + 2.14.2 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + **/com/google/showcase/v1beta1/it/*.java + **/com/google/showcase/v1beta1/it/logging/ITLogging1x.java + **/com/google/showcase/v1beta1/it/logging/ITLogging.java diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java index 21d3a8bc6b..ad39e175a4 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java @@ -39,7 +39,7 @@ import org.slf4j.event.KeyValuePair; // This test needs to run with GOOGLE_SDK_JAVA_LOGGING=true -// mvn clean test -Dslf4j2_logback -Dtest=com.google.showcase.v1beta1.it.logging.ITLogging +// mvn clean verify -P '!showcase,enable-integration-tests,loggingTestBase,slf4j2_logback' public class ITLogging { private static EchoClient grpcClient; @@ -60,6 +60,7 @@ public class ITLogging { private static final String ECHO_STRING = "echo?"; private static Logger logger = LoggerFactory.getLogger(ITLogging.class); + private TestAppender setupTestLogger(Class clazz) { TestAppender testAppender = new TestAppender(); testAppender.start(); @@ -84,21 +85,11 @@ static void destroyClients() throws InterruptedException { grpcClient.close(); httpjsonClient.close(); - grpcClient.awaitTermination(TestClientInitializer.AWAIT_TERMINATION_SECONDS, -TimeUnit.SECONDS); + grpcClient.awaitTermination(TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS); httpjsonClient.awaitTermination( TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS); } - // only run when GOOGLE_SDK_JAVA_LOGGING!=true - @Test - void testloggingDisabled() { - TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptor.class); - assertThat(echoGrpc(ECHO_STRING)).isEqualTo(ECHO_STRING); - - assertThat(testAppender.events.size()).isEqualTo(0); - } - @Test void testGrpc_receiveContent_logDebug() { TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptor.class); @@ -113,9 +104,7 @@ void testGrpc_receiveContent_logDebug() { assertThat(loggingEvent1.getLevel()).isEqualTo(Level.DEBUG); List keyValuePairs = loggingEvent1.getKeyValuePairs(); assertThat(keyValuePairs.size()).isEqualTo(4); - assertThat(keyValuePairs) - .containsAtLeast( - SERVICE_NAME_KEY_VALUE_PAIR, RPC_NAME_KEY_VALUE_PAIR); + assertThat(keyValuePairs).containsAtLeast(SERVICE_NAME_KEY_VALUE_PAIR, RPC_NAME_KEY_VALUE_PAIR); for (KeyValuePair kvp : keyValuePairs) { if (kvp.key.equals("request.payload")) { @@ -166,9 +155,7 @@ void testHttpJson_receiveContent_logDebug() { assertThat(loggingEvent1.getLevel()).isEqualTo(Level.DEBUG); List keyValuePairs = loggingEvent1.getKeyValuePairs(); assertThat(keyValuePairs.size()).isEqualTo(4); - assertThat(keyValuePairs) - .contains( - RPC_NAME_KEY_VALUE_PAIR); + assertThat(keyValuePairs).contains(RPC_NAME_KEY_VALUE_PAIR); for (KeyValuePair kvp : keyValuePairs) { if (kvp.key.equals("request.payload")) { @@ -192,9 +179,7 @@ void testHttpJson_receiveContent_logDebug() { List keyValuePairs2 = loggingEvent2.getKeyValuePairs(); assertThat(keyValuePairs2.size()).isEqualTo(4); assertThat(keyValuePairs2) - .containsAtLeast( - RESPONSE_STATUS_KEY_VALUE_PAIR_HTTP, - RPC_NAME_KEY_VALUE_PAIR); + .containsAtLeast(RESPONSE_STATUS_KEY_VALUE_PAIR_HTTP, RPC_NAME_KEY_VALUE_PAIR); for (KeyValuePair kvp : keyValuePairs2) { if (kvp.key.equals("response.payload")) { Map payload = (Map) kvp.value; @@ -204,9 +189,10 @@ void testHttpJson_receiveContent_logDebug() { } if (kvp.key.equals("response.headers")) { Map headers = (Map) kvp.value; - assertThat(headers.size()).isEqualTo(3); + assertThat(headers.size()).isEqualTo(11); } - } testAppender.stop(); + } + testAppender.stop(); } private String echoGrpc(String value) { @@ -215,8 +201,7 @@ private String echoGrpc(String value) { } private String echoHttpJson(String value) { - EchoResponse response = -httpjsonClient.echo(EchoRequest.newBuilder().setContent(value).build()); + EchoResponse response = httpjsonClient.echo(EchoRequest.newBuilder().setContent(value).build()); return response.getContent(); } } diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java index 310c5969ea..29ecd79f10 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java @@ -39,6 +39,8 @@ // import org.slf4j.event.KeyValuePair; // This test needs to run with GOOGLE_SDK_JAVA_LOGGING=true +// mvn verify -P +// '!showcase,enable-integration-tests,loggingTestBase,envVarLoggingTest1x,slf4j1_logback ' public class ITLogging1x { private static EchoClient grpcClient; @@ -54,7 +56,7 @@ public class ITLogging1x { // new KeyValuePair("response.status", "200"); // // private static final KeyValuePair RESPONSE_HEADERS_KEY_VALUE_PAIR = - // new KeyValuePair("response.headers", ImmutableMap.of("content-type", "application/grpc")); + // new KeyValuePair("response.headers", ImmutableMap.of("content-type","application/grpc")); private static final String ECHO_STRING = "echo?"; diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLoggingDisabled.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLoggingDisabled.java new file mode 100644 index 0000000000..93fd59f79e --- /dev/null +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLoggingDisabled.java @@ -0,0 +1,84 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.showcase.v1beta1.it.logging; + +import static com.google.common.truth.Truth.assertThat; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; +import com.google.api.gax.grpc.GrpcLoggingInterceptor; +import com.google.showcase.v1beta1.EchoClient; +import com.google.showcase.v1beta1.EchoRequest; +import com.google.showcase.v1beta1.EchoResponse; +import com.google.showcase.v1beta1.it.util.TestClientInitializer; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.slf4j.LoggerFactory; + +// mvn verify -P '!showcase,enable-integration-tests,loggingTestBase,disableLogging,slf4j2_logback ' +public class ITLoggingDisabled { + + private static EchoClient grpcClient; + private static EchoClient httpjsonClient; + private static final String ECHO_STRING = "echo?"; + + private TestAppender setupTestLogger(Class clazz) { + TestAppender testAppender = new TestAppender(); + testAppender.start(); + ListAppender listAppender = new ListAppender<>(); + listAppender.start(); + org.slf4j.Logger logger = LoggerFactory.getLogger(clazz); + ((ch.qos.logback.classic.Logger) logger).setLevel(Level.DEBUG); + ((ch.qos.logback.classic.Logger) logger).addAppender(testAppender); + return testAppender; + } + + @BeforeAll + static void createClients() throws Exception { + // Create gRPC Echo Client + grpcClient = TestClientInitializer.createGrpcEchoClient(); + // Create Http JSON Echo Client + httpjsonClient = TestClientInitializer.createHttpJsonEchoClient(); + } + + @AfterAll + static void destroyClients() throws InterruptedException { + grpcClient.close(); + httpjsonClient.close(); + + grpcClient.awaitTermination(TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS); + httpjsonClient.awaitTermination( + TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS); + } + + // only run when GOOGLE_SDK_JAVA_LOGGING!=true + @Test + void testloggingDisabled() { + TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptor.class); + assertThat(echoGrpc(ECHO_STRING)).isEqualTo(ECHO_STRING); + + assertThat(testAppender.events.size()).isEqualTo(0); + } + + private String echoGrpc(String value) { + EchoResponse response = grpcClient.echo(EchoRequest.newBuilder().setContent(value).build()); + return response.getContent(); + } +} diff --git a/showcase/pom.xml b/showcase/pom.xml index 502238ad93..f009b74d05 100644 --- a/showcase/pom.xml +++ b/showcase/pom.xml @@ -114,6 +114,16 @@ + + org.apache.maven.plugins + maven-compiler-plugin + + + + **/com/google/showcase/v1beta1/it/logging/*.java + + + org.apache.maven.plugins maven-surefire-plugin @@ -126,11 +136,6 @@ org.apache.maven.plugins maven-failsafe-plugin - - - **/ITLogging.java - - @@ -150,7 +155,8 @@ - envVarLoggingTest + loggingTestBase + @@ -165,11 +171,6 @@ org.apache.maven.plugins maven-failsafe-plugin - - - **/ITLogging.java - - @@ -184,6 +185,19 @@ org.codehaus.mojo flatten-maven-plugin + + org.apache.maven.plugins + maven-failsafe-plugin + + + + + org.apache.maven.surefire + surefire-junit-platform + ${surefire.version} + + + From b7234e3b1b94c0972962fc1faacbaa8397a22702 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 11 Feb 2025 11:14:47 -0500 Subject: [PATCH 66/92] add showcase logging test steps to ci. cleanups. --- .github/workflows/ci.yaml | 22 +++++++++++++++--- showcase/gapic-showcase/pom.xml | 21 ----------------- .../v1beta1/it/logging/ITLogging1x.java | 23 +------------------ .../v1beta1/it/logging/ITLoggingDisabled.java | 8 +------ showcase/pom.xml | 19 +++++++-------- 5 files changed, 31 insertions(+), 62 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 28adb76ba9..344578d977 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -256,15 +256,31 @@ jobs: -P enable-integration-tests \ --batch-mode \ --no-transfer-progress - # The `envVarLoggingTest` profile runs tests that require an environment variable - - name: Showcase integration tests - Logging + # The `slf4j2_logback` profile brings logging dependency and compiles logging tests, require env var to be set + - name: Showcase integration tests - Logging 2x run: | - mvn verify -P '!showcase,enable-integration-tests,envVarLoggingTest' \ + 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 1x + 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 + 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 diff --git a/showcase/gapic-showcase/pom.xml b/showcase/gapic-showcase/pom.xml index 032dfaf77d..7d4dd6d7a2 100644 --- a/showcase/gapic-showcase/pom.xml +++ b/showcase/gapic-showcase/pom.xml @@ -73,17 +73,6 @@ - - - - - - - - - - - @@ -231,16 +220,6 @@ - - jul - - - org.slf4j - slf4j-jdk14 - 2.0.7 - - - slf4j2_logback diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java index 29ecd79f10..f6761845b1 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java @@ -40,24 +40,12 @@ // This test needs to run with GOOGLE_SDK_JAVA_LOGGING=true // mvn verify -P -// '!showcase,enable-integration-tests,loggingTestBase,envVarLoggingTest1x,slf4j1_logback ' +// '!showcase,enable-integration-tests,loggingTestBase,slf4j1_logback ' public class ITLogging1x { private static EchoClient grpcClient; private static EchoClient httpjsonClient; - // private static final KeyValuePair SERVICE_NAME_KEY_VALUE_PAIR = - // new KeyValuePair("serviceName", "google.showcase.v1beta1.Echo"); - // private static final KeyValuePair RPC_NAME_KEY_VALUE_PAIR = - // new KeyValuePair("rpcName", "google.showcase.v1beta1.Echo/Echo"); - // private static final KeyValuePair RESPONSE_STATUS_KEY_VALUE_PAIR = - // new KeyValuePair("response.status", "OK"); - // private static final KeyValuePair RESPONSE_STATUS_KEY_VALUE_PAIR_HTTP = - // new KeyValuePair("response.status", "200"); - // - // private static final KeyValuePair RESPONSE_HEADERS_KEY_VALUE_PAIR = - // new KeyValuePair("response.headers", ImmutableMap.of("content-type","application/grpc")); - private static final String ECHO_STRING = "echo?"; private static Logger logger = LoggerFactory.getLogger(ITLogging1x.class); @@ -97,15 +85,6 @@ void test() { assertThat(logger.isDebugEnabled()).isTrue(); } - // // only run when GOOGLE_SDK_JAVA_LOGGING!=true - // @Test - // void testloggingDisabled() { - // TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptor.class); - // assertThat(echoGrpc(ECHO_STRING)).isEqualTo(ECHO_STRING); - // - // assertThat(testAppender.events.size()).isEqualTo(0); - // } - @Test void testGrpc_receiveContent_logDebug() { TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptor.class); diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLoggingDisabled.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLoggingDisabled.java index 93fd59f79e..16c6acb0a6 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLoggingDisabled.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLoggingDisabled.java @@ -32,11 +32,10 @@ import org.junit.jupiter.api.Test; import org.slf4j.LoggerFactory; -// mvn verify -P '!showcase,enable-integration-tests,loggingTestBase,disableLogging,slf4j2_logback ' +// mvn verify -P '!showcase,enable-integration-tests,loggingTestBase,disabledLogging' public class ITLoggingDisabled { private static EchoClient grpcClient; - private static EchoClient httpjsonClient; private static final String ECHO_STRING = "echo?"; private TestAppender setupTestLogger(Class clazz) { @@ -54,18 +53,13 @@ private TestAppender setupTestLogger(Class clazz) { static void createClients() throws Exception { // Create gRPC Echo Client grpcClient = TestClientInitializer.createGrpcEchoClient(); - // Create Http JSON Echo Client - httpjsonClient = TestClientInitializer.createHttpJsonEchoClient(); } @AfterAll static void destroyClients() throws InterruptedException { grpcClient.close(); - httpjsonClient.close(); grpcClient.awaitTermination(TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS); - httpjsonClient.awaitTermination( - TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS); } // only run when GOOGLE_SDK_JAVA_LOGGING!=true diff --git a/showcase/pom.xml b/showcase/pom.xml index f009b74d05..c24a3fbe93 100644 --- a/showcase/pom.xml +++ b/showcase/pom.xml @@ -74,6 +74,16 @@ native + + org.apache.maven.plugins + maven-compiler-plugin + + + + **/com/google/showcase/v1beta1/it/logging/*.java + + + org.graalvm.buildtools native-maven-plugin @@ -95,15 +105,6 @@ - - org.apache.maven.plugins - maven-surefire-plugin - - - **/ITLogging.java - - - From e7acafcf842fb74e957d3e92f8dfd80c296bda06 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 11 Feb 2025 12:01:14 -0500 Subject: [PATCH 67/92] rm unused logging configs. --- .../src/test/resources/logback-test.xml | 17 ----------------- .../src/test/resources/logging.properties | 5 ----- 2 files changed, 22 deletions(-) delete mode 100644 showcase/gapic-showcase/src/test/resources/logback-test.xml delete mode 100644 showcase/gapic-showcase/src/test/resources/logging.properties diff --git a/showcase/gapic-showcase/src/test/resources/logback-test.xml b/showcase/gapic-showcase/src/test/resources/logback-test.xml deleted file mode 100644 index 25f8d63aca..0000000000 --- a/showcase/gapic-showcase/src/test/resources/logback-test.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/showcase/gapic-showcase/src/test/resources/logging.properties b/showcase/gapic-showcase/src/test/resources/logging.properties deleted file mode 100644 index ce394f934c..0000000000 --- a/showcase/gapic-showcase/src/test/resources/logging.properties +++ /dev/null @@ -1,5 +0,0 @@ -handlers=java.util.logging.ConsoleHandler, com.google.api.gax.logging.JsonContextMapHandler - -java.util.logging.ConsoleHandler.level=ALL -java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter -com.google.api.gax.logging.JsonContextMapHandler.level=ALL From 801cef6b8a13f3bb740c4c0327493174860ff5cf Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 11 Feb 2025 12:03:48 -0500 Subject: [PATCH 68/92] fix merging miss. --- gax-java/dependencies.properties | 2 -- 1 file changed, 2 deletions(-) diff --git a/gax-java/dependencies.properties b/gax-java/dependencies.properties index 64a4a062c5..025c28eb42 100644 --- a/gax-java/dependencies.properties +++ b/gax-java/dependencies.properties @@ -75,8 +75,6 @@ maven.com_google_http_client_google_http_client=com.google.http-client:google-ht maven.com_google_http_client_google_http_client_gson=com.google.http-client:google-http-client-gson:1.45.3 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:graal-sdk:22.3.5 -maven.org_slf4j_slf4j_api=org.slf4j:slf4j-api:2.0.16 maven.org_graalvm_sdk=org.graalvm.sdk:nativeimage:24.1.2 maven.org_slf4j_slf4j_api=org.slf4j:slf4j-api:2.0.16 From cf8ecbc607333f51ce03f708e8d83b9ede49b1af Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 11 Feb 2025 12:07:55 -0500 Subject: [PATCH 69/92] minor cleanups. --- .../com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java | 2 +- .../api/gax/httpjson/HttpJsonLoggingInterceptorTest.java | 2 +- .../com/google/showcase/v1beta1/it/logging/ITLoggingExtra.java | 3 --- 3 files changed, 2 insertions(+), 5 deletions(-) delete mode 100644 showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLoggingExtra.java diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java index 00dca46390..3e15cc3bfd 100644 --- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java +++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java index 8c83a0d550..ae6ea51490 100644 --- a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java +++ b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLoggingExtra.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLoggingExtra.java deleted file mode 100644 index 1234b2a2c7..0000000000 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLoggingExtra.java +++ /dev/null @@ -1,3 +0,0 @@ -package com.google.showcase.v1beta1.it.logging; - -public class ITLoggingExtra {} From 19faf95ab02cd43efb85023ea4e7c4c4c4c27cd6 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 11 Feb 2025 13:29:08 -0500 Subject: [PATCH 70/92] add compiler exclude config to enable-golden-tests profile. --- showcase/pom.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/showcase/pom.xml b/showcase/pom.xml index c24a3fbe93..73e6634240 100644 --- a/showcase/pom.xml +++ b/showcase/pom.xml @@ -206,6 +206,16 @@ enable-golden-tests + + org.apache.maven.plugins + maven-compiler-plugin + + + + **/com/google/showcase/v1beta1/it/logging/*.java + + + org.codehaus.mojo exec-maven-plugin From 47f1212a837f37fae86e9d736f4b25a3ce5fdb2d Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 11 Feb 2025 13:31:38 -0500 Subject: [PATCH 71/92] add missing env var for ci test. --- .github/workflows/ci.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 344578d977..5ecf1470b2 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -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: | From 420de1e340773e0f7e1a16df4fc23d5f723b70e8 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 11 Feb 2025 14:01:04 -0500 Subject: [PATCH 72/92] add test coverage, lint. --- .../api/gax/logging/LoggerProviderTest.java | 62 +++++++++++++++++++ .../api/gax/logging/LoggingUtilsTest.java | 19 +----- 2 files changed, 65 insertions(+), 16 deletions(-) create mode 100644 gax-java/gax/src/test/java/com/google/api/gax/logging/LoggerProviderTest.java diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggerProviderTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggerProviderTest.java new file mode 100644 index 0000000000..41b6e2204d --- /dev/null +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggerProviderTest.java @@ -0,0 +1,62 @@ +/* + * 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.logging; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; + +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; + +class LoggerProviderTest { + + @Test + void testGetLogger_CreatesLoggerOnce() { + Class testClass = this.getClass(); + LoggerProvider provider = LoggerProvider.setLogger(testClass); + Logger logger1 = provider.getLogger(); + Logger logger2 = provider.getLogger(); + + assertNotNull(logger1); + assertSame(logger1, logger2); + } + + @Test + void testSetLogger_ReturnsNewInstance() { + Class testClass1 = this.getClass(); + Class testClass2 = String.class; + LoggerProvider provider1 = LoggerProvider.setLogger(testClass1); + LoggerProvider provider2 = LoggerProvider.setLogger(testClass2); + + assertNotSame(provider1, provider2); + } +} diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java index 19e8a96d1d..ba54aa433e 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java @@ -30,7 +30,9 @@ package com.google.api.gax.logging; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import com.google.api.gax.logging.LoggingUtils.ThrowingRunnable; import org.junit.jupiter.api.Test; @@ -75,19 +77,4 @@ void testExecuteWithTryCatch_WithNoSuchMethodError() throws Throwable { // Verify that the action was executed (despite the error) Mockito.verify(action).run(); } - - @Test - void recordServiceRpcAndRequestHeaders() {} - - @Test - void recordResponseHeaders() {} - - @Test - void recordResponsePayload() {} - - @Test - void logResponse() {} - - @Test - void logRequest() {} } From c222198f9fd87f6dcf15cc78633e38f03ce3ec99 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 11 Feb 2025 14:13:42 -0500 Subject: [PATCH 73/92] add working dir for showcase logging tests in ci. --- .github/workflows/ci.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5ecf1470b2..66000627cd 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -260,6 +260,7 @@ jobs: --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 2x + working-directory: showcase run: | mvn clean verify -P '!showcase,enable-integration-tests,loggingTestBase,slf4j2_logback' \ --batch-mode \ @@ -269,6 +270,7 @@ jobs: 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 1x + working-directory: showcase run: | mvn clean verify -P '!showcase,enable-integration-tests,loggingTestBase,slf4j1_logback' \ --batch-mode \ @@ -279,6 +281,7 @@ jobs: # 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 \ From 227857a4d46e8784f39e2839ad006af2ef97eafa Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 11 Feb 2025 14:44:56 -0500 Subject: [PATCH 74/92] add dep for gax-java bazel builds, for integration tests. --- gax-java/dependencies.properties | 1 + gax-java/gax-grpc/BUILD.bazel | 1 + gax-java/gax-httpjson/BUILD.bazel | 1 + gax-java/gax/BUILD.bazel | 3 ++- 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/gax-java/dependencies.properties b/gax-java/dependencies.properties index 025c28eb42..563187f2af 100644 --- a/gax-java/dependencies.properties +++ b/gax-java/dependencies.properties @@ -77,6 +77,7 @@ maven.org_codehaus_mojo_animal_sniffer_annotations=org.codehaus.mojo:animal-snif 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 +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 diff --git a/gax-java/gax-grpc/BUILD.bazel b/gax-java/gax-grpc/BUILD.bazel index be224ff3f8..500a6056f7 100644 --- a/gax-java/gax-grpc/BUILD.bazel +++ b/gax-java/gax-grpc/BUILD.bazel @@ -43,6 +43,7 @@ _TEST_COMPILE_DEPS = [ "@com_google_api_grpc_grpc_google_common_protos//jar", "//gax:gax_testlib", "@com_googlecode_java_diff_utils_diffutils//jar", + "@org_slf4j_slf4j_api//jar", ] java_library( diff --git a/gax-java/gax-httpjson/BUILD.bazel b/gax-java/gax-httpjson/BUILD.bazel index 1e134e6770..a825589405 100644 --- a/gax-java/gax-httpjson/BUILD.bazel +++ b/gax-java/gax-httpjson/BUILD.bazel @@ -34,6 +34,7 @@ _TEST_COMPILE_DEPS = [ "@com_google_truth_truth//jar", "//gax:gax_testlib", "@com_googlecode_java_diff_utils_diffutils//jar", + "@org_slf4j_slf4j_api//jar", ] java_library( diff --git a/gax-java/gax/BUILD.bazel b/gax-java/gax/BUILD.bazel index 6a7a5a4cd5..80b26ad785 100644 --- a/gax-java/gax/BUILD.bazel +++ b/gax-java/gax/BUILD.bazel @@ -29,7 +29,8 @@ _COMPILE_DEPS = [ "@com_google_guava_failureaccess//jar", "@javax_annotation_javax_annotation_api//jar", "@org_graalvm_sdk//jar", - "@maven//:org_slf4j_slf4j_api", + "@org_slf4j_slf4j_api//jar", + "@com_google_protobuf_protobuf_java_util//jar" ] _TEST_COMPILE_DEPS = [ From 07888cb259177430b74ed6363ac7149c1114d092 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 11 Feb 2025 16:04:52 -0500 Subject: [PATCH 75/92] some code cleanups. --- gax-java/gax-grpc/BUILD.bazel | 1 - .../gax/grpc/GrpcLoggingInterceptorTest.java | 12 ------- gax-java/gax-httpjson/BUILD.bazel | 1 - .../HttpJsonLoggingInterceptorTest.java | 5 --- .../com/google/api/gax/logging/LogData.java | 1 - .../api/gax/logging/LoggingEnabledTest.java | 4 +-- .../api/gax/logging/LoggingUtilsTest.java | 1 - .../api/gax/logging/Slf4jUtilsTest.java | 2 +- .../api/gax/logging/TestMDCAdapter.java | 2 +- showcase/gapic-showcase/pom.xml | 33 ++----------------- showcase/pom.xml | 15 +-------- 11 files changed, 7 insertions(+), 70 deletions(-) diff --git a/gax-java/gax-grpc/BUILD.bazel b/gax-java/gax-grpc/BUILD.bazel index 500a6056f7..be224ff3f8 100644 --- a/gax-java/gax-grpc/BUILD.bazel +++ b/gax-java/gax-grpc/BUILD.bazel @@ -43,7 +43,6 @@ _TEST_COMPILE_DEPS = [ "@com_google_api_grpc_grpc_google_common_protos//jar", "//gax:gax_testlib", "@com_googlecode_java_diff_utils_diffutils//jar", - "@org_slf4j_slf4j_api//jar", ] java_library( diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java index 3e15cc3bfd..fad4cd468b 100644 --- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java +++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java @@ -49,8 +49,6 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; @ExtendWith(MockitoExtension.class) class GrpcLoggingInterceptorTest { @@ -60,8 +58,6 @@ class GrpcLoggingInterceptorTest { private static final MethodDescriptor method = FakeMethodDescriptor.create(); - private static final Logger LOGGER = LoggerFactory.getLogger(GrpcLoggingInterceptorTest.class); - @Test void testInterceptor_basic() { when(channel.newCall(Mockito.>any(), any(CallOptions.class))) @@ -104,13 +100,5 @@ void testInterceptor_responseListener() { Status status = Status.OK; interceptor.currentListener.onClose(status, new Metadata()); - - // --- Verify that the response listener's methods were called --- - // verify(interceptor) - // .recordResponseHeaders(eq(responseHeaders), any(LogData.Builder.class), - // any(Logger.class)); - // // verify(interceptor).recordResponsePayload(any(), any(LogData.Builder.class), - // // any(Logger.class)); - // verify(interceptor).logResponse(eq(status), any(LogData.Builder.class), any(Logger.class)); } } diff --git a/gax-java/gax-httpjson/BUILD.bazel b/gax-java/gax-httpjson/BUILD.bazel index a825589405..1e134e6770 100644 --- a/gax-java/gax-httpjson/BUILD.bazel +++ b/gax-java/gax-httpjson/BUILD.bazel @@ -34,7 +34,6 @@ _TEST_COMPILE_DEPS = [ "@com_google_truth_truth//jar", "//gax:gax_testlib", "@com_googlecode_java_diff_utils_diffutils//jar", - "@org_slf4j_slf4j_api//jar", ] java_library( diff --git a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java index ae6ea51490..c7e134b5ce 100644 --- a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java +++ b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptorTest.java @@ -33,14 +33,9 @@ import static org.mockito.Mockito.mock; import com.google.api.gax.httpjson.ApiMethodDescriptor.MethodType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; class HttpJsonLoggingInterceptorTest { - private static final Logger LOGGER = - LoggerFactory.getLogger(HttpJsonLoggingInterceptorTest.class); - @SuppressWarnings("unchecked") private static final ApiMethodDescriptor method = ApiMethodDescriptor.newBuilder() diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java index d7b5b9d18c..368800dba0 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LogData.java @@ -102,7 +102,6 @@ public abstract static class Builder { } // helper functions to convert to map for logging - // todo: error handling? public Map toMapRequest() { Map map = new HashMap<>(); if (serviceName() != null) { diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingEnabledTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingEnabledTest.java index b786ba1dff..b14f5c1c46 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingEnabledTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingEnabledTest.java @@ -42,7 +42,7 @@ import org.slf4j.helpers.NOPLoggerFactory; // This test class should only run when env GOOGLE_SDK_JAVA_LOGGING = true -// it is excluded by default +// it is excluded by default and only included for `envVarTest` profile class LoggingEnabledTest { @Test @@ -68,10 +68,8 @@ void testGetLogger_loggingEnabled_noBinding_shouldGetNOPLogger() { ILoggerFactory nopLoggerFactory = new NOPLoggerFactory(); when(mockLoggerFactoryProvider.getLoggerFactory()).thenReturn(nopLoggerFactory); - // Use the mock LoggerFactoryProvider in getLogger() Logger logger = Slf4jUtils.getLogger(Slf4jUtilsTest.class, mockLoggerFactoryProvider); - // Assert that the returned logger is a NOPLogger Assertions.assertInstanceOf(NOPLogger.class, logger); } } diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java index ba54aa433e..728113bb2e 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java @@ -42,7 +42,6 @@ class LoggingUtilsTest { @Test void testIsLoggingEnabled_defaultToFalse() { - // LoggingUtils.setEnvironmentProvider(envProvider); assertFalse(LoggingUtils.isLoggingEnabled()); } diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java index f9e334fc0d..0e79bef3e1 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java @@ -80,7 +80,7 @@ void testLog_slf4J2xLogger() { @Test void testLogWithMDC_InfoLevel_VerifyMDC() { - // this test replies on TestMDCApapter and TestServiceProvider + // this test relies on TestMDCApapter and TestServiceProvider TestLogger testLogger = new TestLogger("test-logger"); Map contextMap = new HashMap<>(); contextMap.put("key1", "value1"); diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestMDCAdapter.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestMDCAdapter.java index 8d6f2d0c1a..b1b5d379cc 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestMDCAdapter.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestMDCAdapter.java @@ -36,7 +36,7 @@ import org.slf4j.spi.MDCAdapter; /** - * this adapter is for unit test only. It is setup via TestServiceProvider to test behavior when + * this adapter is for unit test only. It is set up via TestServiceProvider to test behavior when * LogWithMDC */ public class TestMDCAdapter implements MDCAdapter { diff --git a/showcase/gapic-showcase/pom.xml b/showcase/gapic-showcase/pom.xml index 7d4dd6d7a2..4f8adb0ad6 100644 --- a/showcase/gapic-showcase/pom.xml +++ b/showcase/gapic-showcase/pom.xml @@ -233,6 +233,7 @@ org.slf4j slf4j-api 2.0.16 + test ch.qos.logback @@ -247,16 +248,6 @@ 1.3.14 test - - net.logstash.logback - logstash-logback-encoder - 8.0 - - - com.fasterxml.jackson.core - jackson-databind - 2.14.2 - @@ -287,6 +278,7 @@ org.slf4j slf4j-api 1.7.36 + test ch.qos.logback @@ -300,16 +292,6 @@ 1.2.13 test - - net.logstash.logback - logstash-logback-encoder - 7.3 - - - com.fasterxml.jackson.core - jackson-databind - 2.14.2 - @@ -340,6 +322,7 @@ org.slf4j slf4j-api 1.7.36 + test ch.qos.logback @@ -353,16 +336,6 @@ 1.2.13 test - - net.logstash.logback - logstash-logback-encoder - 7.3 - - - com.fasterxml.jackson.core - jackson-databind - 2.14.2 - diff --git a/showcase/pom.xml b/showcase/pom.xml index 0f07e6056c..256034da48 100644 --- a/showcase/pom.xml +++ b/showcase/pom.xml @@ -163,7 +163,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.0 + 3.5.2 sponge_log ${skipUnitTests} @@ -186,19 +186,6 @@ org.codehaus.mojo flatten-maven-plugin - - org.apache.maven.plugins - maven-failsafe-plugin - - - - - org.apache.maven.surefire - surefire-junit-platform - ${surefire.version} - - - From 20017cc941f9d4fc92212f38c054a9a748e14787 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 12 Feb 2025 12:58:59 -0500 Subject: [PATCH 76/92] add comments, lint, minor fixes. updated logback version for test. --- .../google/api/gax/logging/LoggingUtils.java | 41 +++++++++++++++++++ .../api/gax/logging/LoggingUtilsTest.java | 5 ++- gax-java/pom.xml | 23 ----------- showcase/gapic-showcase/pom.xml | 5 +-- .../v1beta1/it/logging/ITLogging.java | 3 -- 5 files changed, 46 insertions(+), 31 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java index 3c5d645de9..c1e5e99c5c 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java @@ -44,6 +44,17 @@ static boolean isLoggingEnabled() { return "true".equalsIgnoreCase(enableLogging); } + /** + * Sets logDataBuilder with service name, rpc name, endpoint and request headers based on logging + * level + * + * @param serviceName + * @param rpcName + * @param endpoint + * @param requestHeaders + * @param logDataBuilder + * @param loggerProvider + */ public static void recordServiceRpcAndRequestHeaders( String serviceName, String rpcName, @@ -57,6 +68,13 @@ public static void recordServiceRpcAndRequestHeaders( } } + /** + * Sets logDataBuilder with response headers based on logging level + * + * @param headers + * @param logDataBuilder + * @param loggerProvider + */ public static void recordResponseHeaders( Map headers, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { if (loggingEnabled) { @@ -64,6 +82,14 @@ public static void recordResponseHeaders( } } + /** + * Sets logDataBuilder with respond payload based on logging level + * + * @param message + * @param logDataBuilder + * @param loggerProvider + * @param + */ public static void recordResponsePayload( RespT message, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { if (loggingEnabled) { @@ -71,6 +97,13 @@ public static void recordResponsePayload( } } + /** + * Log response based on logging level configured + * + * @param status + * @param logDataBuilder + * @param loggerProvider + */ public static void logResponse( String status, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { if (loggingEnabled) { @@ -78,6 +111,14 @@ public static void logResponse( } } + /** + * Log request based on logging level configured + * + * @param message + * @param logDataBuilder + * @param loggerProvider + * @param + */ public static void logRequest( RespT message, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { if (loggingEnabled) { diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java index 728113bb2e..9e3099e929 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingUtilsTest.java @@ -33,6 +33,7 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.Mockito.verify; import com.google.api.gax.logging.LoggingUtils.ThrowingRunnable; import org.junit.jupiter.api.Test; @@ -65,7 +66,7 @@ void testExecuteWithTryCatch_WithException() throws Throwable { Mockito.doThrow(new RuntimeException("Test Exception")).when(action).run(); assertDoesNotThrow(() -> LoggingUtils.executeWithTryCatch(action)); // Verify that the action was executed (despite the exception) - Mockito.verify(action).run(); + verify(action).run(); } @Test @@ -74,6 +75,6 @@ void testExecuteWithTryCatch_WithNoSuchMethodError() throws Throwable { Mockito.doThrow(new NoSuchMethodError("Test Error")).when(action).run(); assertDoesNotThrow(() -> LoggingUtils.executeWithTryCatch(action)); // Verify that the action was executed (despite the error) - Mockito.verify(action).run(); + verify(action).run(); } } diff --git a/gax-java/pom.xml b/gax-java/pom.xml index 05960c3098..e005d44ffb 100644 --- a/gax-java/pom.xml +++ b/gax-java/pom.xml @@ -227,29 +227,6 @@ - - org.codehaus.mojo - animal-sniffer-maven-plugin - 1.24 - - - java8 - - check - - - - org.codehaus.mojo.signature - java18 - 1.0 - - - org.slf4j.* - - - - - maven-compiler-plugin diff --git a/showcase/gapic-showcase/pom.xml b/showcase/gapic-showcase/pom.xml index 4f8adb0ad6..e54fa12f26 100644 --- a/showcase/gapic-showcase/pom.xml +++ b/showcase/gapic-showcase/pom.xml @@ -238,14 +238,13 @@ ch.qos.logback logback-classic - - 1.3.14 + 1.5.16 test ch.qos.logback logback-core - 1.3.14 + 1.5.16 test diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java index ad39e175a4..fe06ac80a4 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java @@ -59,8 +59,6 @@ public class ITLogging { private static final String ECHO_STRING = "echo?"; - private static Logger logger = LoggerFactory.getLogger(ITLogging.class); - private TestAppender setupTestLogger(Class clazz) { TestAppender testAppender = new TestAppender(); testAppender.start(); @@ -95,7 +93,6 @@ void testGrpc_receiveContent_logDebug() { TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptor.class); assertThat(echoGrpc(ECHO_STRING)).isEqualTo(ECHO_STRING); - assertThat(true).isTrue(); assertThat(testAppender.events.size()).isEqualTo(2); // logging event for request From 2d3ba3e4b2380c5685d67daa218c0a0dbb431827 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 12 Feb 2025 13:05:52 -0500 Subject: [PATCH 77/92] minor cleanup in test. --- .../com/google/showcase/v1beta1/it/logging/ITLogging.java | 5 ----- .../com/google/showcase/v1beta1/it/logging/ITLogging1x.java | 3 --- 2 files changed, 8 deletions(-) diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java index fe06ac80a4..2c12fc0e98 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java @@ -20,7 +20,6 @@ import ch.qos.logback.classic.Level; import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.read.ListAppender; import com.google.api.gax.grpc.GrpcLoggingInterceptor; import com.google.api.gax.httpjson.HttpJsonLoggingInterceptor; import com.google.common.collect.ImmutableMap; @@ -34,7 +33,6 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.event.KeyValuePair; @@ -62,8 +60,6 @@ public class ITLogging { private TestAppender setupTestLogger(Class clazz) { TestAppender testAppender = new TestAppender(); testAppender.start(); - ListAppender listAppender = new ListAppender<>(); - listAppender.start(); org.slf4j.Logger logger = LoggerFactory.getLogger(clazz); ((ch.qos.logback.classic.Logger) logger).setLevel(Level.DEBUG); ((ch.qos.logback.classic.Logger) logger).addAppender(testAppender); @@ -93,7 +89,6 @@ void testGrpc_receiveContent_logDebug() { TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptor.class); assertThat(echoGrpc(ECHO_STRING)).isEqualTo(ECHO_STRING); - assertThat(testAppender.events.size()).isEqualTo(2); // logging event for request ILoggingEvent loggingEvent1 = testAppender.events.get(0); diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java index f6761845b1..b16a08b4a2 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java @@ -20,7 +20,6 @@ import ch.qos.logback.classic.Level; import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.read.ListAppender; import com.google.api.gax.grpc.GrpcLoggingInterceptor; import com.google.api.gax.httpjson.HttpJsonLoggingInterceptor; import com.google.common.collect.ImmutableMap; @@ -53,8 +52,6 @@ public class ITLogging1x { private TestAppender setupTestLogger(Class clazz) { TestAppender testAppender = new TestAppender(); testAppender.start(); - ListAppender listAppender = new ListAppender<>(); - listAppender.start(); Logger logger = LoggerFactory.getLogger(clazz); ((ch.qos.logback.classic.Logger) logger).setLevel(Level.DEBUG); ((ch.qos.logback.classic.Logger) logger).addAppender(testAppender); From 7c1ea762649703ceff780bc2b72b564d0788169c Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 12 Feb 2025 13:31:57 -0500 Subject: [PATCH 78/92] add test cases for info level. --- .../v1beta1/it/logging/ITLogging.java | 64 +++++++++++++++- .../v1beta1/it/logging/ITLogging1x.java | 74 ++++++++++++++++++- 2 files changed, 130 insertions(+), 8 deletions(-) diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java index 2c12fc0e98..cc467c5b9a 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java @@ -51,17 +51,19 @@ public class ITLogging { new KeyValuePair("response.status", "OK"); private static final KeyValuePair RESPONSE_STATUS_KEY_VALUE_PAIR_HTTP = new KeyValuePair("response.status", "200"); + private static final KeyValuePair REQUEST_URL_KEY_VALUE_PAIR = + new KeyValuePair("request.url", "http://localhost:7469"); private static final KeyValuePair RESPONSE_HEADERS_KEY_VALUE_PAIR = new KeyValuePair("response.headers", ImmutableMap.of("content-type", "application/grpc")); private static final String ECHO_STRING = "echo?"; - private TestAppender setupTestLogger(Class clazz) { + private TestAppender setupTestLogger(Class clazz, Level level) { TestAppender testAppender = new TestAppender(); testAppender.start(); org.slf4j.Logger logger = LoggerFactory.getLogger(clazz); - ((ch.qos.logback.classic.Logger) logger).setLevel(Level.DEBUG); + ((ch.qos.logback.classic.Logger) logger).setLevel(level); ((ch.qos.logback.classic.Logger) logger).addAppender(testAppender); return testAppender; } @@ -86,7 +88,7 @@ static void destroyClients() throws InterruptedException { @Test void testGrpc_receiveContent_logDebug() { - TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptor.class); + TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptor.class, Level.DEBUG); assertThat(echoGrpc(ECHO_STRING)).isEqualTo(ECHO_STRING); assertThat(testAppender.events.size()).isEqualTo(2); @@ -136,9 +138,36 @@ void testGrpc_receiveContent_logDebug() { testAppender.stop(); } + @Test + void testGrpc_receiveContent_logInfo() { + TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptor.class, Level.INFO); + assertThat(echoGrpc(ECHO_STRING)).isEqualTo(ECHO_STRING); + + assertThat(testAppender.events.size()).isEqualTo(2); + // logging event for request + ILoggingEvent loggingEvent1 = testAppender.events.get(0); + assertThat(loggingEvent1.getMessage()).isEqualTo("Sending gRPC request"); + assertThat(loggingEvent1.getLevel()).isEqualTo(Level.INFO); + List keyValuePairs = loggingEvent1.getKeyValuePairs(); + assertThat(keyValuePairs.size()).isEqualTo(2); + assertThat(keyValuePairs).containsAtLeast(SERVICE_NAME_KEY_VALUE_PAIR, RPC_NAME_KEY_VALUE_PAIR); + + // logging event for response + ILoggingEvent loggingEvent2 = testAppender.events.get(1); + assertThat(loggingEvent2.getMessage()).isEqualTo("Received Grpc response"); + + assertThat(loggingEvent2.getLevel()).isEqualTo(Level.INFO); + List keyValuePairs2 = loggingEvent2.getKeyValuePairs(); + assertThat(keyValuePairs2.size()).isEqualTo(3); + assertThat(keyValuePairs2) + .containsAtLeast( + RESPONSE_STATUS_KEY_VALUE_PAIR, SERVICE_NAME_KEY_VALUE_PAIR, RPC_NAME_KEY_VALUE_PAIR); + testAppender.stop(); + } + @Test void testHttpJson_receiveContent_logDebug() { - TestAppender testAppender = setupTestLogger(HttpJsonLoggingInterceptor.class); + TestAppender testAppender = setupTestLogger(HttpJsonLoggingInterceptor.class, Level.DEBUG); assertThat(echoHttpJson(ECHO_STRING)).isEqualTo(ECHO_STRING); assertThat(testAppender.events.size()).isEqualTo(2); // logging event for request @@ -148,6 +177,7 @@ void testHttpJson_receiveContent_logDebug() { List keyValuePairs = loggingEvent1.getKeyValuePairs(); assertThat(keyValuePairs.size()).isEqualTo(4); assertThat(keyValuePairs).contains(RPC_NAME_KEY_VALUE_PAIR); + assertThat(keyValuePairs).contains(REQUEST_URL_KEY_VALUE_PAIR); for (KeyValuePair kvp : keyValuePairs) { if (kvp.key.equals("request.payload")) { @@ -187,6 +217,32 @@ void testHttpJson_receiveContent_logDebug() { testAppender.stop(); } + @Test + void testHttpJson_receiveContent_logInfo() { + TestAppender testAppender = setupTestLogger(HttpJsonLoggingInterceptor.class, Level.INFO); + assertThat(echoHttpJson(ECHO_STRING)).isEqualTo(ECHO_STRING); + assertThat(testAppender.events.size()).isEqualTo(2); + // logging event for request + ILoggingEvent loggingEvent1 = testAppender.events.get(0); + assertThat(loggingEvent1.getMessage()).isEqualTo("Sending gRPC request"); + assertThat(loggingEvent1.getLevel()).isEqualTo(Level.INFO); + List keyValuePairs = loggingEvent1.getKeyValuePairs(); + assertThat(keyValuePairs.size()).isEqualTo(2); + assertThat(keyValuePairs).contains(RPC_NAME_KEY_VALUE_PAIR); + assertThat(keyValuePairs).contains(REQUEST_URL_KEY_VALUE_PAIR); + + // logging event for response + ILoggingEvent loggingEvent2 = testAppender.events.get(1); + assertThat(loggingEvent2.getMessage()).isEqualTo("Received Grpc response"); + + assertThat(loggingEvent2.getLevel()).isEqualTo(Level.INFO); + List keyValuePairs2 = loggingEvent2.getKeyValuePairs(); + assertThat(keyValuePairs2.size()).isEqualTo(2); + assertThat(keyValuePairs2) + .containsAtLeast(RESPONSE_STATUS_KEY_VALUE_PAIR_HTTP, RPC_NAME_KEY_VALUE_PAIR); + testAppender.stop(); + } + private String echoGrpc(String value) { EchoResponse response = grpcClient.echo(EchoRequest.newBuilder().setContent(value).build()); return response.getContent(); diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java index b16a08b4a2..112616948a 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java @@ -49,11 +49,11 @@ public class ITLogging1x { private static Logger logger = LoggerFactory.getLogger(ITLogging1x.class); - private TestAppender setupTestLogger(Class clazz) { + private TestAppender setupTestLogger(Class clazz, Level level) { TestAppender testAppender = new TestAppender(); testAppender.start(); Logger logger = LoggerFactory.getLogger(clazz); - ((ch.qos.logback.classic.Logger) logger).setLevel(Level.DEBUG); + ((ch.qos.logback.classic.Logger) logger).setLevel(level); ((ch.qos.logback.classic.Logger) logger).addAppender(testAppender); return testAppender; } @@ -84,7 +84,7 @@ void test() { @Test void testGrpc_receiveContent_logDebug() { - TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptor.class); + TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptor.class, Level.DEBUG); assertThat(echoGrpc(ECHO_STRING)).isEqualTo(ECHO_STRING); assertThat(testAppender.events.size()).isEqualTo(2); @@ -127,9 +127,46 @@ void testGrpc_receiveContent_logDebug() { testAppender.stop(); } + @Test + void testGrpc_receiveContent_logInfo() { + TestAppender testAppender = setupTestLogger(GrpcLoggingInterceptor.class, Level.INFO); + assertThat(echoGrpc(ECHO_STRING)).isEqualTo(ECHO_STRING); + + assertThat(testAppender.events.size()).isEqualTo(2); + // logging event for request + ILoggingEvent loggingEvent1 = testAppender.events.get(0); + assertThat(loggingEvent1.getMessage()).isEqualTo("Sending gRPC request"); + assertThat(loggingEvent1.getLevel()).isEqualTo(Level.INFO); + Map mdcPropertyMap = loggingEvent1.getMDCPropertyMap(); + assertThat(mdcPropertyMap) + .containsExactlyEntriesIn( + ImmutableMap.of( + "serviceName", + "google.showcase.v1beta1.Echo", + "rpcName", + "google.showcase.v1beta1.Echo/Echo")); + + // logging event for response + ILoggingEvent loggingEvent2 = testAppender.events.get(1); + assertThat(loggingEvent2.getMessage()).isEqualTo("Received Grpc response"); + assertThat(loggingEvent2.getLevel()).isEqualTo(Level.INFO); + Map responseMdcPropertyMap = loggingEvent2.getMDCPropertyMap(); + assertThat(responseMdcPropertyMap) + .containsExactlyEntriesIn( + ImmutableMap.of( + "serviceName", + "google.showcase.v1beta1.Echo", + "rpcName", + "google.showcase.v1beta1.Echo/Echo", + "response.status", + "OK")); + + testAppender.stop(); + } + @Test void testHttpJson_receiveContent_logDebug() { - TestAppender testAppender = setupTestLogger(HttpJsonLoggingInterceptor.class); + TestAppender testAppender = setupTestLogger(HttpJsonLoggingInterceptor.class, Level.DEBUG); assertThat(echoHttpJson(ECHO_STRING)).isEqualTo(ECHO_STRING); assertThat(testAppender.events.size()).isEqualTo(2); // logging event for request @@ -160,6 +197,35 @@ void testHttpJson_receiveContent_logDebug() { testAppender.stop(); } + @Test + void testHttpJson_receiveContent_logInfo() { + TestAppender testAppender = setupTestLogger(HttpJsonLoggingInterceptor.class, Level.INFO); + assertThat(echoHttpJson(ECHO_STRING)).isEqualTo(ECHO_STRING); + assertThat(testAppender.events.size()).isEqualTo(2); + // logging event for request + ILoggingEvent loggingEvent1 = testAppender.events.get(0); + assertThat(loggingEvent1.getMessage()).isEqualTo("Sending gRPC request"); + assertThat(loggingEvent1.getLevel()).isEqualTo(Level.INFO); + Map mdcPropertyMap = loggingEvent1.getMDCPropertyMap(); + assertThat(mdcPropertyMap) + .containsExactlyEntriesIn( + ImmutableMap.of( + "rpcName", "google.showcase.v1beta1.Echo/Echo", + "request.url", "http://localhost:7469")); + + // logging event for response + ILoggingEvent loggingEvent2 = testAppender.events.get(1); + assertThat(loggingEvent2.getMessage()).isEqualTo("Received Grpc response"); + assertThat(loggingEvent2.getLevel()).isEqualTo(Level.INFO); + Map responseMdcPropertyMap = loggingEvent2.getMDCPropertyMap(); + assertThat(responseMdcPropertyMap) + .containsExactlyEntriesIn( + ImmutableMap.of( + "rpcName", "google.showcase.v1beta1.Echo/Echo", + "response.status", "200")); + testAppender.stop(); + } + private String echoGrpc(String value) { EchoResponse response = grpcClient.echo(EchoRequest.newBuilder().setContent(value).build()); return response.getContent(); From 7aa8333e80788a552912a610d9607f76d3bd8bb7 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 12 Feb 2025 13:35:26 -0500 Subject: [PATCH 79/92] minor cleanups. --- .../v1beta1/it/logging/ITLogging.java | 18 ++--- .../v1beta1/it/logging/ITLogging1x.java | 67 +++++++------------ 2 files changed, 33 insertions(+), 52 deletions(-) diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java index cc467c5b9a..29d4f0226b 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java @@ -57,6 +57,8 @@ public class ITLogging { private static final KeyValuePair RESPONSE_HEADERS_KEY_VALUE_PAIR = new KeyValuePair("response.headers", ImmutableMap.of("content-type", "application/grpc")); + private static final String SENDING_REQUEST_MESSAGE = "Sending gRPC request"; + private static final String RECEIVING_RESPONSE_MESSAGE = "Received Grpc response"; private static final String ECHO_STRING = "echo?"; private TestAppender setupTestLogger(Class clazz, Level level) { @@ -94,7 +96,7 @@ void testGrpc_receiveContent_logDebug() { assertThat(testAppender.events.size()).isEqualTo(2); // logging event for request ILoggingEvent loggingEvent1 = testAppender.events.get(0); - assertThat(loggingEvent1.getMessage()).isEqualTo("Sending gRPC request"); + assertThat(loggingEvent1.getMessage()).isEqualTo(SENDING_REQUEST_MESSAGE); assertThat(loggingEvent1.getLevel()).isEqualTo(Level.DEBUG); List keyValuePairs = loggingEvent1.getKeyValuePairs(); assertThat(keyValuePairs.size()).isEqualTo(4); @@ -116,7 +118,7 @@ void testGrpc_receiveContent_logDebug() { // logging event for response ILoggingEvent loggingEvent2 = testAppender.events.get(1); - assertThat(loggingEvent2.getMessage()).isEqualTo("Received Grpc response"); + assertThat(loggingEvent2.getMessage()).isEqualTo(RECEIVING_RESPONSE_MESSAGE); assertThat(loggingEvent2.getLevel()).isEqualTo(Level.DEBUG); List keyValuePairs2 = loggingEvent2.getKeyValuePairs(); @@ -146,7 +148,7 @@ void testGrpc_receiveContent_logInfo() { assertThat(testAppender.events.size()).isEqualTo(2); // logging event for request ILoggingEvent loggingEvent1 = testAppender.events.get(0); - assertThat(loggingEvent1.getMessage()).isEqualTo("Sending gRPC request"); + assertThat(loggingEvent1.getMessage()).isEqualTo(SENDING_REQUEST_MESSAGE); assertThat(loggingEvent1.getLevel()).isEqualTo(Level.INFO); List keyValuePairs = loggingEvent1.getKeyValuePairs(); assertThat(keyValuePairs.size()).isEqualTo(2); @@ -154,7 +156,7 @@ void testGrpc_receiveContent_logInfo() { // logging event for response ILoggingEvent loggingEvent2 = testAppender.events.get(1); - assertThat(loggingEvent2.getMessage()).isEqualTo("Received Grpc response"); + assertThat(loggingEvent2.getMessage()).isEqualTo(RECEIVING_RESPONSE_MESSAGE); assertThat(loggingEvent2.getLevel()).isEqualTo(Level.INFO); List keyValuePairs2 = loggingEvent2.getKeyValuePairs(); @@ -172,7 +174,7 @@ void testHttpJson_receiveContent_logDebug() { assertThat(testAppender.events.size()).isEqualTo(2); // logging event for request ILoggingEvent loggingEvent1 = testAppender.events.get(0); - assertThat(loggingEvent1.getMessage()).isEqualTo("Sending gRPC request"); + assertThat(loggingEvent1.getMessage()).isEqualTo(SENDING_REQUEST_MESSAGE); assertThat(loggingEvent1.getLevel()).isEqualTo(Level.DEBUG); List keyValuePairs = loggingEvent1.getKeyValuePairs(); assertThat(keyValuePairs.size()).isEqualTo(4); @@ -195,7 +197,7 @@ void testHttpJson_receiveContent_logDebug() { // logging event for response ILoggingEvent loggingEvent2 = testAppender.events.get(1); - assertThat(loggingEvent2.getMessage()).isEqualTo("Received Grpc response"); + assertThat(loggingEvent2.getMessage()).isEqualTo(RECEIVING_RESPONSE_MESSAGE); assertThat(loggingEvent2.getLevel()).isEqualTo(Level.DEBUG); List keyValuePairs2 = loggingEvent2.getKeyValuePairs(); @@ -224,7 +226,7 @@ void testHttpJson_receiveContent_logInfo() { assertThat(testAppender.events.size()).isEqualTo(2); // logging event for request ILoggingEvent loggingEvent1 = testAppender.events.get(0); - assertThat(loggingEvent1.getMessage()).isEqualTo("Sending gRPC request"); + assertThat(loggingEvent1.getMessage()).isEqualTo(SENDING_REQUEST_MESSAGE); assertThat(loggingEvent1.getLevel()).isEqualTo(Level.INFO); List keyValuePairs = loggingEvent1.getKeyValuePairs(); assertThat(keyValuePairs.size()).isEqualTo(2); @@ -233,7 +235,7 @@ void testHttpJson_receiveContent_logInfo() { // logging event for response ILoggingEvent loggingEvent2 = testAppender.events.get(1); - assertThat(loggingEvent2.getMessage()).isEqualTo("Received Grpc response"); + assertThat(loggingEvent2.getMessage()).isEqualTo(RECEIVING_RESPONSE_MESSAGE); assertThat(loggingEvent2.getLevel()).isEqualTo(Level.INFO); List keyValuePairs2 = loggingEvent2.getKeyValuePairs(); diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java index 112616948a..bdc2d8f1df 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java @@ -35,8 +35,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -// import org.slf4j.event.KeyValuePair; - // This test needs to run with GOOGLE_SDK_JAVA_LOGGING=true // mvn verify -P // '!showcase,enable-integration-tests,loggingTestBase,slf4j1_logback ' @@ -46,6 +44,11 @@ public class ITLogging1x { private static EchoClient httpjsonClient; private static final String ECHO_STRING = "echo?"; + private static final String SERVICE_NAME = "google.showcase.v1beta1.Echo"; + private static final String RPC_NAME = "google.showcase.v1beta1.Echo/Echo"; + private static final String ENDPOINT = "http://localhost:7469"; + private static final String SENDING_REQUEST_MESSAGE = "Sending gRPC request"; + private static final String RECEIVING_RESPONSE_MESSAGE = "Received Grpc response"; private static Logger logger = LoggerFactory.getLogger(ITLogging1x.class); @@ -90,16 +93,12 @@ void testGrpc_receiveContent_logDebug() { assertThat(testAppender.events.size()).isEqualTo(2); // logging event for request ILoggingEvent loggingEvent1 = testAppender.events.get(0); - assertThat(loggingEvent1.getMessage()).isEqualTo("Sending gRPC request"); + assertThat(loggingEvent1.getMessage()).isEqualTo(SENDING_REQUEST_MESSAGE); assertThat(loggingEvent1.getLevel()).isEqualTo(Level.DEBUG); Map mdcPropertyMap = loggingEvent1.getMDCPropertyMap(); assertThat(mdcPropertyMap) .containsAtLeastEntriesIn( - ImmutableMap.of( - "serviceName", - "google.showcase.v1beta1.Echo", - "rpcName", - "google.showcase.v1beta1.Echo/Echo")); + ImmutableMap.of("serviceName", SERVICE_NAME, "rpcName", RPC_NAME)); assertThat(mdcPropertyMap).containsKey("request.headers"); assertThat(mdcPropertyMap.get("request.headers")).startsWith("{\"x-goog-api-"); @@ -109,18 +108,13 @@ void testGrpc_receiveContent_logDebug() { // logging event for response ILoggingEvent loggingEvent2 = testAppender.events.get(1); - assertThat(loggingEvent2.getMessage()).isEqualTo("Received Grpc response"); + assertThat(loggingEvent2.getMessage()).isEqualTo(RECEIVING_RESPONSE_MESSAGE); assertThat(loggingEvent2.getLevel()).isEqualTo(Level.DEBUG); Map responseMdcPropertyMap = loggingEvent2.getMDCPropertyMap(); assertThat(responseMdcPropertyMap) .containsAtLeastEntriesIn( ImmutableMap.of( - "serviceName", - "google.showcase.v1beta1.Echo", - "rpcName", - "google.showcase.v1beta1.Echo/Echo", - "response.status", - "OK")); + "serviceName", SERVICE_NAME, "rpcName", RPC_NAME, "response.status", "OK")); assertThat(responseMdcPropertyMap).containsKey("response.payload"); assertThat(responseMdcPropertyMap).containsKey("response.headers"); @@ -135,31 +129,22 @@ void testGrpc_receiveContent_logInfo() { assertThat(testAppender.events.size()).isEqualTo(2); // logging event for request ILoggingEvent loggingEvent1 = testAppender.events.get(0); - assertThat(loggingEvent1.getMessage()).isEqualTo("Sending gRPC request"); + assertThat(loggingEvent1.getMessage()).isEqualTo(SENDING_REQUEST_MESSAGE); assertThat(loggingEvent1.getLevel()).isEqualTo(Level.INFO); Map mdcPropertyMap = loggingEvent1.getMDCPropertyMap(); assertThat(mdcPropertyMap) .containsExactlyEntriesIn( - ImmutableMap.of( - "serviceName", - "google.showcase.v1beta1.Echo", - "rpcName", - "google.showcase.v1beta1.Echo/Echo")); + ImmutableMap.of("serviceName", SERVICE_NAME, "rpcName", RPC_NAME)); // logging event for response ILoggingEvent loggingEvent2 = testAppender.events.get(1); - assertThat(loggingEvent2.getMessage()).isEqualTo("Received Grpc response"); + assertThat(loggingEvent2.getMessage()).isEqualTo(RECEIVING_RESPONSE_MESSAGE); assertThat(loggingEvent2.getLevel()).isEqualTo(Level.INFO); Map responseMdcPropertyMap = loggingEvent2.getMDCPropertyMap(); assertThat(responseMdcPropertyMap) .containsExactlyEntriesIn( ImmutableMap.of( - "serviceName", - "google.showcase.v1beta1.Echo", - "rpcName", - "google.showcase.v1beta1.Echo/Echo", - "response.status", - "OK")); + "serviceName", SERVICE_NAME, "rpcName", RPC_NAME, "response.status", "OK")); testAppender.stop(); } @@ -171,11 +156,11 @@ void testHttpJson_receiveContent_logDebug() { assertThat(testAppender.events.size()).isEqualTo(2); // logging event for request ILoggingEvent loggingEvent1 = testAppender.events.get(0); - assertThat(loggingEvent1.getMessage()).isEqualTo("Sending gRPC request"); + assertThat(loggingEvent1.getMessage()).isEqualTo(SENDING_REQUEST_MESSAGE); assertThat(loggingEvent1.getLevel()).isEqualTo(Level.DEBUG); Map mdcPropertyMap = loggingEvent1.getMDCPropertyMap(); - assertThat(mdcPropertyMap).containsEntry("rpcName", "google.showcase.v1beta1.Echo/Echo"); - assertThat(mdcPropertyMap).containsKey("request.url"); + assertThat(mdcPropertyMap).containsEntry("rpcName", RPC_NAME); + assertThat(mdcPropertyMap).containsEntry("request.url", ENDPOINT); assertThat(mdcPropertyMap).containsKey("request.headers"); assertThat(mdcPropertyMap.get("request.headers")).startsWith("{\"x-goog-api-"); assertThat(mdcPropertyMap).containsKey("request.payload"); @@ -184,14 +169,11 @@ void testHttpJson_receiveContent_logDebug() { // logging event for response ILoggingEvent loggingEvent2 = testAppender.events.get(1); - assertThat(loggingEvent2.getMessage()).isEqualTo("Received Grpc response"); + assertThat(loggingEvent2.getMessage()).isEqualTo(RECEIVING_RESPONSE_MESSAGE); assertThat(loggingEvent2.getLevel()).isEqualTo(Level.DEBUG); Map responseMdcPropertyMap = loggingEvent2.getMDCPropertyMap(); assertThat(responseMdcPropertyMap) - .containsAtLeastEntriesIn( - ImmutableMap.of( - "rpcName", "google.showcase.v1beta1.Echo/Echo", - "response.status", "200")); + .containsAtLeastEntriesIn(ImmutableMap.of("rpcName", RPC_NAME, "response.status", "200")); assertThat(responseMdcPropertyMap).containsKey("response.payload"); assertThat(responseMdcPropertyMap).containsKey("response.headers"); testAppender.stop(); @@ -204,25 +186,22 @@ void testHttpJson_receiveContent_logInfo() { assertThat(testAppender.events.size()).isEqualTo(2); // logging event for request ILoggingEvent loggingEvent1 = testAppender.events.get(0); - assertThat(loggingEvent1.getMessage()).isEqualTo("Sending gRPC request"); + assertThat(loggingEvent1.getMessage()).isEqualTo(SENDING_REQUEST_MESSAGE); assertThat(loggingEvent1.getLevel()).isEqualTo(Level.INFO); Map mdcPropertyMap = loggingEvent1.getMDCPropertyMap(); assertThat(mdcPropertyMap) .containsExactlyEntriesIn( ImmutableMap.of( - "rpcName", "google.showcase.v1beta1.Echo/Echo", - "request.url", "http://localhost:7469")); + "rpcName", RPC_NAME, + "request.url", ENDPOINT)); // logging event for response ILoggingEvent loggingEvent2 = testAppender.events.get(1); - assertThat(loggingEvent2.getMessage()).isEqualTo("Received Grpc response"); + assertThat(loggingEvent2.getMessage()).isEqualTo(RECEIVING_RESPONSE_MESSAGE); assertThat(loggingEvent2.getLevel()).isEqualTo(Level.INFO); Map responseMdcPropertyMap = loggingEvent2.getMDCPropertyMap(); assertThat(responseMdcPropertyMap) - .containsExactlyEntriesIn( - ImmutableMap.of( - "rpcName", "google.showcase.v1beta1.Echo/Echo", - "response.status", "200")); + .containsExactlyEntriesIn(ImmutableMap.of("rpcName", RPC_NAME, "response.status", "200")); testAppender.stop(); } From d5c759deb49e7aa6f8fddb1845e52591aff4a064 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 12 Feb 2025 13:42:45 -0500 Subject: [PATCH 80/92] comments and cleanups. --- .../google/api/gax/httpjson/HttpJsonLoggingInterceptor.java | 2 -- .../java/com/google/api/gax/logging/LoggingEnabledTest.java | 4 +--- .../test/java/com/google/api/gax/logging/Slf4jUtilsTest.java | 3 ++- .../com/google/showcase/v1beta1/it/logging/TestAppender.java | 1 + 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index 04b5cc68b0..cc228f1917 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -47,8 +47,6 @@ @InternalApi public class HttpJsonLoggingInterceptor implements HttpJsonClientInterceptor { - // private static final Logger LOGGER = LoggingUtils.getLogger(HttpJsonLoggingInterceptor.class); - private static final LoggerProvider LOGGER_PROVIDER = LoggerProvider.setLogger(HttpJsonLoggingInterceptor.class); diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingEnabledTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingEnabledTest.java index b14f5c1c46..2d755a6ecc 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingEnabledTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggingEnabledTest.java @@ -41,7 +41,7 @@ import org.slf4j.helpers.NOPLogger; import org.slf4j.helpers.NOPLoggerFactory; -// This test class should only run when env GOOGLE_SDK_JAVA_LOGGING = true +// These tests should only run when env GOOGLE_SDK_JAVA_LOGGING = true // it is excluded by default and only included for `envVarTest` profile class LoggingEnabledTest { @@ -50,7 +50,6 @@ void testIsLoggingEnabled_true() { Assertions.assertTrue(LoggingUtils.isLoggingEnabled()); } - // GOOGLE_SDK_JAVA_LOGGING = true @Test void testGetLogger_loggingEnabled_slf4jBindingPresent() { // should get ILoggerFactory from TestServiceProvider @@ -59,7 +58,6 @@ void testGetLogger_loggingEnabled_slf4jBindingPresent() { Assertions.assertNotEquals(NOPLogger.class, logger.getClass()); } - // GOOGLE_SDK_JAVA_LOGGING = true @Test void testGetLogger_loggingEnabled_noBinding_shouldGetNOPLogger() { // Create a mock LoggerFactoryProvider, mimic SLF4J's behavior to return NOPLoggerFactory when diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java index 0e79bef3e1..a40b694e2d 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java @@ -54,7 +54,7 @@ class Slf4jUtilsTest { - // GOOGLE_SDK_JAVA_LOGGING = false + // This test should only run GOOGLE_SDK_JAVA_LOGGING != true @Test void testGetLogger_loggingDisabled_shouldGetNOPLogger() { Logger logger = Slf4jUtils.getLogger(Slf4jUtilsTest.class); @@ -63,6 +63,7 @@ void testGetLogger_loggingDisabled_shouldGetNOPLogger() { assertFalse(logger.isDebugEnabled()); } + // These tests does not require GOOGLE_SDK_JAVA_LOGGING @Test void testLog_slf4J2xLogger() { Map contextMap = new HashMap<>(); diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/TestAppender.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/TestAppender.java index cad4d9427a..769ad9a740 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/TestAppender.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/TestAppender.java @@ -29,6 +29,7 @@ public class TestAppender extends AppenderBase { @Override protected void append(ILoggingEvent eventObject) { // triggering Logback to capture the current MDC context and store it with the log event + // the default ListAppender does not capture MDC contents eventObject.getMDCPropertyMap(); events.add(eventObject); From 1641f5563e762fa1de2cb0189e0ade41474a9d76 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 12 Feb 2025 14:24:09 -0500 Subject: [PATCH 81/92] rename method. --- .../com/google/api/gax/grpc/GrpcLoggingInterceptor.java | 2 +- .../api/gax/httpjson/HttpJsonLoggingInterceptor.java | 2 +- .../java/com/google/api/gax/logging/LoggerProvider.java | 2 +- .../com/google/api/gax/logging/LoggerProviderTest.java | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index a18c518e7c..f7a2cf602d 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -56,7 +56,7 @@ public class GrpcLoggingInterceptor implements ClientInterceptor { private static final LoggerProvider LOGGER_PROVIDER = - LoggerProvider.setLogger(GrpcLoggingInterceptor.class); + LoggerProvider.forClazz(GrpcLoggingInterceptor.class); ClientCall.Listener currentListener; // expose for test setup diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index cc228f1917..b2f3d8f5b4 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -48,7 +48,7 @@ public class HttpJsonLoggingInterceptor implements HttpJsonClientInterceptor { private static final LoggerProvider LOGGER_PROVIDER = - LoggerProvider.setLogger(HttpJsonLoggingInterceptor.class); + LoggerProvider.forClazz(HttpJsonLoggingInterceptor.class); @Override public HttpJsonClientCall interceptCall( diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggerProvider.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggerProvider.java index 27bf47c45f..24745ee01e 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggerProvider.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggerProvider.java @@ -43,7 +43,7 @@ private LoggerProvider(Class clazz) { this.clazz = clazz; } - public static LoggerProvider setLogger(Class clazz) { + public static LoggerProvider forClazz(Class clazz) { return new LoggerProvider(clazz); } diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggerProviderTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggerProviderTest.java index 41b6e2204d..b16e94e538 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggerProviderTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/LoggerProviderTest.java @@ -42,7 +42,7 @@ class LoggerProviderTest { @Test void testGetLogger_CreatesLoggerOnce() { Class testClass = this.getClass(); - LoggerProvider provider = LoggerProvider.setLogger(testClass); + LoggerProvider provider = LoggerProvider.forClazz(testClass); Logger logger1 = provider.getLogger(); Logger logger2 = provider.getLogger(); @@ -51,11 +51,11 @@ void testGetLogger_CreatesLoggerOnce() { } @Test - void testSetLogger_ReturnsNewInstance() { + void testForClazz_ReturnsNewInstance() { Class testClass1 = this.getClass(); Class testClass2 = String.class; - LoggerProvider provider1 = LoggerProvider.setLogger(testClass1); - LoggerProvider provider2 = LoggerProvider.setLogger(testClass2); + LoggerProvider provider1 = LoggerProvider.forClazz(testClass1); + LoggerProvider provider2 = LoggerProvider.forClazz(testClass2); assertNotSame(provider1, provider2); } From d86b7a0e9e38abee6a2b3ceabea421e3eab4bf8c Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 12 Feb 2025 16:58:18 -0500 Subject: [PATCH 82/92] added tests. --- .../google/api/gax/logging/Slf4jUtils.java | 1 - .../api/gax/logging/Slf4jUtilsTest.java | 32 +++++++++++++++++++ .../google/api/gax/logging/TestLogger.java | 13 ++++++-- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java index c5f994eb15..db3fd67c98 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java @@ -102,7 +102,6 @@ static void logWithMDC( for (Entry entry : contextMap.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); - // MDC.put(key, value.toString()); MDC.put(key, value instanceof String ? (String) value : gson.toJson(value)); } } diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java index a40b694e2d..be0a9ab1fe 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java @@ -100,6 +100,38 @@ void testLogWithMDC_InfoLevel_VerifyMDC() { assertEquals(message, testLogger.messageList.get(0)); } + @Test + void testLogWithMDC_DEBUG() { + TestLogger testLogger = new TestLogger("test-logger"); + Slf4jUtils.logWithMDC(testLogger, Level.DEBUG, new HashMap<>(), "test message"); + + assertEquals(Level.DEBUG, testLogger.level); + } + + @Test + void testLogWithMDC_TRACE() { + TestLogger testLogger = new TestLogger("test-logger"); + Slf4jUtils.logWithMDC(testLogger, Level.TRACE, new HashMap<>(), "test message"); + + assertEquals(Level.TRACE, testLogger.level); + } + + @Test + void testLogWithMDC_WARN() { + TestLogger testLogger = new TestLogger("test-logger"); + Slf4jUtils.logWithMDC(testLogger, Level.WARN, new HashMap<>(), "test message"); + + assertEquals(Level.WARN, testLogger.level); + } + + @Test + void testLogWithMDC_ERROR() { + TestLogger testLogger = new TestLogger("test-logger"); + Slf4jUtils.logWithMDC(testLogger, Level.ERROR, new HashMap<>(), "test message"); + + assertEquals(Level.ERROR, testLogger.level); + } + @Test void testMessageToMap_ValidMessage() throws InvalidProtocolBufferException { Field field = diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestLogger.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestLogger.java index 7c496383f4..3ee7e513cd 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/TestLogger.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/TestLogger.java @@ -77,7 +77,9 @@ public boolean isTraceEnabled() { } @Override - public void trace(String msg) {} + public void trace(String msg) { + level = Level.TRACE; + } @Override public void trace(String format, Object arg) {} @@ -121,6 +123,7 @@ public void debug(String msg) { Map currentMDC = MDC.getCopyOfContextMap(); MDCMap.putAll(currentMDC); messageList.add(msg); + level = Level.DEBUG; } @Override @@ -208,7 +211,9 @@ public boolean isWarnEnabled() { } @Override - public void warn(String msg) {} + public void warn(String msg) { + level = Level.WARN; + } @Override public void warn(String format, Object arg) {} @@ -248,7 +253,9 @@ public boolean isErrorEnabled() { } @Override - public void error(String msg) {} + public void error(String msg) { + level = Level.ERROR; + } @Override public void error(String format, Object arg) {} From f611c74d1cedadbc20b19658724ca84c48feb1a3 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Fri, 14 Feb 2025 16:11:57 -0500 Subject: [PATCH 83/92] feedback: added comments, move slf4j dep to each module inside gax. --- .github/workflows/ci.yaml | 4 ++-- gax-java/gax-grpc/pom.xml | 6 ++++++ gax-java/gax-httpjson/pom.xml | 6 ++++++ gax-java/gax/pom.xml | 6 ++++++ gax-java/pom.xml | 6 ------ showcase/gapic-showcase/pom.xml | 3 ++- 6 files changed, 22 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 66000627cd..801ed4c977 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -259,7 +259,7 @@ jobs: --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 2x + - name: Showcase integration tests - Logging SLF4J 2.x working-directory: showcase run: | mvn clean verify -P '!showcase,enable-integration-tests,loggingTestBase,slf4j2_logback' \ @@ -269,7 +269,7 @@ jobs: 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 1x + - name: Showcase integration tests - Logging SLF4J 1.x working-directory: showcase run: | mvn clean verify -P '!showcase,enable-integration-tests,loggingTestBase,slf4j1_logback' \ diff --git a/gax-java/gax-grpc/pom.xml b/gax-java/gax-grpc/pom.xml index 01a62345ef..cb33a3dae3 100644 --- a/gax-java/gax-grpc/pom.xml +++ b/gax-java/gax-grpc/pom.xml @@ -92,6 +92,12 @@ auto-value-annotations provided + + + org.slf4j + slf4j-api + true + diff --git a/gax-java/gax-httpjson/pom.xml b/gax-java/gax-httpjson/pom.xml index 268f5e1bdc..30e1389b80 100644 --- a/gax-java/gax-httpjson/pom.xml +++ b/gax-java/gax-httpjson/pom.xml @@ -78,6 +78,12 @@ auto-value-annotations provided + + + org.slf4j + slf4j-api + true + diff --git a/gax-java/gax/pom.xml b/gax-java/gax/pom.xml index da4ea6ffce..7323e6315e 100644 --- a/gax-java/gax/pom.xml +++ b/gax-java/gax/pom.xml @@ -73,6 +73,12 @@ opentelemetry-api true + + + org.slf4j + slf4j-api + true + diff --git a/gax-java/pom.xml b/gax-java/pom.xml index e005d44ffb..193276a45d 100644 --- a/gax-java/pom.xml +++ b/gax-java/pom.xml @@ -172,12 +172,6 @@ - - - org.slf4j - slf4j-api - true - org.junit.jupiter diff --git a/showcase/gapic-showcase/pom.xml b/showcase/gapic-showcase/pom.xml index e54fa12f26..331146b5d8 100644 --- a/showcase/gapic-showcase/pom.xml +++ b/showcase/gapic-showcase/pom.xml @@ -310,7 +310,8 @@ disabledLogging - + + disable_logging From 539031b0442d408ca407b740d0d5ae68a2765f24 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Fri, 14 Feb 2025 16:17:26 -0500 Subject: [PATCH 84/92] rename hasAddKeyValue to isSLF4J2x. --- .../java/com/google/api/gax/logging/Slf4jUtils.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java index db3fd67c98..c4e115d7ac 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java @@ -54,18 +54,18 @@ class Slf4jUtils { private static boolean loggingEnabled = LoggingUtils.isLoggingEnabled(); private static final Gson gson = new Gson(); - private static boolean hasAddKeyValue; + private static boolean isSLF4J2x; static { - hasAddKeyValue = checkIfClazzAvailable("org.slf4j.event.KeyValuePair"); + isSLF4J2x = checkIfClazzAvailable("org.slf4j.event.KeyValuePair"); } static boolean checkIfClazzAvailable(String clazzName) { try { Class.forName(clazzName); - return true; // SLF4j 2.x or later + return true; } catch (ClassNotFoundException e) { - return false; // SLF4j 1.x or earlier + return false; } } @@ -88,7 +88,7 @@ static Logger getLogger(Class clazz, LoggerFactoryProvider factoryProvider) { static void log( Logger logger, org.slf4j.event.Level level, Map contextMap, String message) { - if (hasAddKeyValue) { + if (isSLF4J2x) { logWithKeyValuePair(logger, level, contextMap, message); } else { logWithMDC(logger, level, contextMap, message); From 091ab3890033231e2808c7cb5acf21a36b7ef66d Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 18 Feb 2025 14:21:32 -0500 Subject: [PATCH 85/92] add final to class, add comments. --- .../com/google/api/gax/grpc/GrpcLoggingInterceptor.java | 8 +++++--- .../api/gax/httpjson/HttpJsonLoggingInterceptor.java | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index f7a2cf602d..51b019d545 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -53,7 +53,7 @@ import java.util.Map; @InternalApi -public class GrpcLoggingInterceptor implements ClientInterceptor { +public final class GrpcLoggingInterceptor implements ClientInterceptor { private static final LoggerProvider LOGGER_PROVIDER = LoggerProvider.forClazz(GrpcLoggingInterceptor.class); @@ -73,7 +73,7 @@ public void start(Listener responseListener, Metadata headers) { recordServiceRpcAndRequestHeaders( method.getServiceName(), method.getFullMethodName(), - null, + null, // endpoint is for http request only metadataHeadersToMap(headers), logDataBuilder, LOGGER_PROVIDER); @@ -117,7 +117,9 @@ private static Map metadataHeadersToMap(Metadata headers) { 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)) { continue; } diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index b2f3d8f5b4..98924651a5 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -45,7 +45,7 @@ import java.util.Map; @InternalApi -public class HttpJsonLoggingInterceptor implements HttpJsonClientInterceptor { +public final class HttpJsonLoggingInterceptor implements HttpJsonClientInterceptor { private static final LoggerProvider LOGGER_PROVIDER = LoggerProvider.forClazz(HttpJsonLoggingInterceptor.class); @@ -66,7 +66,7 @@ public HttpJsonClientCall interceptCall( public void start( HttpJsonClientCall.Listener responseListener, HttpJsonMetadata headers) { recordServiceRpcAndRequestHeaders( - null, + null, // service name is not available for http requests method.getFullMethodName(), endpoint, httpJsonMetadataToMap(headers), From b3b328e770e48c0517c30aa95913a892670548fb Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 18 Feb 2025 14:28:41 -0500 Subject: [PATCH 86/92] revert final change. --- .../java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java | 2 +- .../com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java index 51b019d545..9c305261cb 100644 --- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java +++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcLoggingInterceptor.java @@ -53,7 +53,7 @@ import java.util.Map; @InternalApi -public final class GrpcLoggingInterceptor implements ClientInterceptor { +public class GrpcLoggingInterceptor implements ClientInterceptor { private static final LoggerProvider LOGGER_PROVIDER = LoggerProvider.forClazz(GrpcLoggingInterceptor.class); diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java index 98924651a5..5a1b1b8c2b 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLoggingInterceptor.java @@ -45,7 +45,7 @@ import java.util.Map; @InternalApi -public final class HttpJsonLoggingInterceptor implements HttpJsonClientInterceptor { +public class HttpJsonLoggingInterceptor implements HttpJsonClientInterceptor { private static final LoggerProvider LOGGER_PROVIDER = LoggerProvider.forClazz(HttpJsonLoggingInterceptor.class); From 6aece18e57d921f832c94ced9f8943b171bc253b Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 18 Feb 2025 15:59:41 -0500 Subject: [PATCH 87/92] minor refactor: split internal Slf4jUtils and Slf4jLoggingHelpers. --- .../google/api/gax/logging/LoggingUtils.java | 10 +- .../api/gax/logging/Slf4jLoggingHelpers.java | 146 ++++++++++++++++++ .../google/api/gax/logging/Slf4jUtils.java | 118 +------------- .../api/gax/logging/Slf4jUtilsTest.java | 25 +-- 4 files changed, 172 insertions(+), 127 deletions(-) create mode 100644 gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jLoggingHelpers.java diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java index c1e5e99c5c..43b9254041 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/LoggingUtils.java @@ -63,7 +63,7 @@ public static void recordServiceRpcAndRequestHeaders( LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { if (loggingEnabled) { - Slf4jUtils.recordServiceRpcAndRequestHeaders( + Slf4jLoggingHelpers.recordServiceRpcAndRequestHeaders( serviceName, rpcName, endpoint, requestHeaders, logDataBuilder, loggerProvider); } } @@ -78,7 +78,7 @@ public static void recordServiceRpcAndRequestHeaders( public static void recordResponseHeaders( Map headers, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { if (loggingEnabled) { - Slf4jUtils.recordResponseHeaders(headers, logDataBuilder, loggerProvider); + Slf4jLoggingHelpers.recordResponseHeaders(headers, logDataBuilder, loggerProvider); } } @@ -93,7 +93,7 @@ public static void recordResponseHeaders( public static void recordResponsePayload( RespT message, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { if (loggingEnabled) { - Slf4jUtils.recordResponsePayload(message, logDataBuilder, loggerProvider); + Slf4jLoggingHelpers.recordResponsePayload(message, logDataBuilder, loggerProvider); } } @@ -107,7 +107,7 @@ public static void recordResponsePayload( public static void logResponse( String status, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { if (loggingEnabled) { - Slf4jUtils.logResponse(status, logDataBuilder, loggerProvider); + Slf4jLoggingHelpers.logResponse(status, logDataBuilder, loggerProvider); } } @@ -122,7 +122,7 @@ public static void logResponse( public static void logRequest( RespT message, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { if (loggingEnabled) { - Slf4jUtils.logRequest(message, logDataBuilder, loggerProvider); + Slf4jLoggingHelpers.logRequest(message, logDataBuilder, loggerProvider); } } diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jLoggingHelpers.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jLoggingHelpers.java new file mode 100644 index 0000000000..28ff4441a4 --- /dev/null +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jLoggingHelpers.java @@ -0,0 +1,146 @@ +/* + * 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.logging; + +import com.google.api.core.InternalApi; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.Message; +import com.google.protobuf.util.JsonFormat; +import java.util.Map; +import java.util.function.Consumer; +import org.slf4j.Logger; +import org.slf4j.event.Level; + +/** Contains helper methods to log requests and responses */ +@InternalApi +class Slf4jLoggingHelpers { + + static final Gson gson = new Gson(); + + static Map messageToMapWithGson(Message message) + throws InvalidProtocolBufferException { + String json = JsonFormat.printer().print(message); + return gson.fromJson(json, new TypeToken>() {}.getType()); + } + + static void recordServiceRpcAndRequestHeaders( + String serviceName, + String rpcName, + String endpoint, + Map requestHeaders, + LogData.Builder logDataBuilder, + LoggerProvider loggerProvider) { + LoggingUtils.executeWithTryCatch( + () -> { + Logger logger = loggerProvider.getLogger(); + if (logger.isInfoEnabled()) { + addIfNotEmpty(logDataBuilder::serviceName, serviceName); + addIfNotEmpty(logDataBuilder::rpcName, rpcName); + addIfNotEmpty(logDataBuilder::httpUrl, endpoint); + } + if (logger.isDebugEnabled()) { + logDataBuilder.requestHeaders(requestHeaders); + } + }); + } + + private static void addIfNotEmpty(Consumer setter, String value) { + if (value != null && !value.isEmpty()) { + setter.accept(value); + } + } + + static void recordResponseHeaders( + Map headers, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { + LoggingUtils.executeWithTryCatch( + () -> { + Logger logger = loggerProvider.getLogger(); + if (logger.isDebugEnabled()) { + logDataBuilder.responseHeaders(headers); + } + }); + } + + static void recordResponsePayload( + RespT message, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { + LoggingUtils.executeWithTryCatch( + () -> { + Logger logger = loggerProvider.getLogger(); + if (logger.isDebugEnabled()) { + Map messageToMapWithGson = messageToMapWithGson((Message) message); + + logDataBuilder.responsePayload(messageToMapWithGson); + } + }); + } + + static void logResponse( + String status, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { + LoggingUtils.executeWithTryCatch( + () -> { + Logger logger = loggerProvider.getLogger(); + if (logger.isInfoEnabled()) { + logDataBuilder.responseStatus(status); + } + if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { + Map responseData = logDataBuilder.build().toMapResponse(); + Slf4jUtils.log(logger, Level.INFO, responseData, "Received Grpc response"); + } + if (logger.isDebugEnabled()) { + Map responsedDetailsMap = logDataBuilder.build().toMapResponse(); + Slf4jUtils.log(logger, Level.DEBUG, responsedDetailsMap, "Received Grpc response"); + } + }); + } + + static void logRequest( + RespT message, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { + LoggingUtils.executeWithTryCatch( + () -> { + Logger logger = loggerProvider.getLogger(); + if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { + Slf4jUtils.log( + logger, Level.INFO, logDataBuilder.build().toMapRequest(), "Sending gRPC request"); + } + if (logger.isDebugEnabled()) { + Map messageToMapWithGson = messageToMapWithGson((Message) message); + + logDataBuilder.requestPayload(messageToMapWithGson); + Map requestDetailsMap = logDataBuilder.build().toMapRequest(); + Slf4jUtils.log(logger, Level.DEBUG, requestDetailsMap, "Sending gRPC request"); + } + }); + } + + private Slf4jLoggingHelpers() {} +} diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java index c4e115d7ac..587d1af5b2 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java @@ -31,30 +31,25 @@ package com.google.api.gax.logging; import com.google.api.core.InternalApi; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; -import com.google.protobuf.InvalidProtocolBufferException; -import com.google.protobuf.Message; -import com.google.protobuf.util.JsonFormat; import java.util.Map; import java.util.Map.Entry; -import java.util.function.Consumer; import org.slf4j.ILoggerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; -import org.slf4j.event.Level; import org.slf4j.spi.LoggingEventBuilder; -// Actual interaction with SLF4J happens only in this class. +/** + * Contains util methods to get SLF4J logger and log conditionally based SLF4J major version Actual + * interaction with SLF4J happens only in this class. + */ @InternalApi class Slf4jUtils { private static final Logger NO_OP_LOGGER = org.slf4j.helpers.NOPLogger.NOP_LOGGER; - private static boolean loggingEnabled = LoggingUtils.isLoggingEnabled(); - private static final Gson gson = new Gson(); + private static final boolean loggingEnabled = LoggingUtils.isLoggingEnabled(); - private static boolean isSLF4J2x; + private static final boolean isSLF4J2x; static { isSLF4J2x = checkIfClazzAvailable("org.slf4j.event.KeyValuePair"); @@ -102,7 +97,8 @@ static void logWithMDC( for (Entry entry : contextMap.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); - MDC.put(key, value instanceof String ? (String) value : gson.toJson(value)); + MDC.put( + key, value instanceof String ? (String) value : Slf4jLoggingHelpers.gson.toJson(value)); } } switch (level) { @@ -167,102 +163,4 @@ public ILoggerFactory getLoggerFactory() { return LoggerFactory.getILoggerFactory(); } } - - // logging helper methods - static Map messageToMapWithGson(Message message) - throws InvalidProtocolBufferException { - String json = JsonFormat.printer().print(message); - return gson.fromJson(json, new TypeToken>() {}.getType()); - } - - static void recordServiceRpcAndRequestHeaders( - String serviceName, - String rpcName, - String endpoint, - Map requestHeaders, - LogData.Builder logDataBuilder, - LoggerProvider loggerProvider) { - LoggingUtils.executeWithTryCatch( - () -> { - Logger logger = loggerProvider.getLogger(); - if (logger.isInfoEnabled()) { - addIfNotEmpty(logDataBuilder::serviceName, serviceName); - addIfNotEmpty(logDataBuilder::rpcName, rpcName); - addIfNotEmpty(logDataBuilder::httpUrl, endpoint); - } - if (logger.isDebugEnabled()) { - logDataBuilder.requestHeaders(requestHeaders); - } - }); - } - - private static void addIfNotEmpty(Consumer setter, String value) { - if (value != null && !value.isEmpty()) { - setter.accept(value); - } - } - - static void recordResponseHeaders( - Map headers, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { - LoggingUtils.executeWithTryCatch( - () -> { - Logger logger = loggerProvider.getLogger(); - if (logger.isDebugEnabled()) { - logDataBuilder.responseHeaders(headers); - } - }); - } - - static void recordResponsePayload( - RespT message, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { - LoggingUtils.executeWithTryCatch( - () -> { - Logger logger = loggerProvider.getLogger(); - if (logger.isDebugEnabled()) { - Map messageToMapWithGson = - Slf4jUtils.messageToMapWithGson((Message) message); - - logDataBuilder.responsePayload(messageToMapWithGson); - } - }); - } - - static void logResponse( - String status, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { - LoggingUtils.executeWithTryCatch( - () -> { - Logger logger = loggerProvider.getLogger(); - if (logger.isInfoEnabled()) { - logDataBuilder.responseStatus(status); - } - if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { - Map responseData = logDataBuilder.build().toMapResponse(); - Slf4jUtils.log(logger, Level.INFO, responseData, "Received Grpc response"); - } - if (logger.isDebugEnabled()) { - Map responsedDetailsMap = logDataBuilder.build().toMapResponse(); - Slf4jUtils.log(logger, Level.DEBUG, responsedDetailsMap, "Received Grpc response"); - } - }); - } - - static void logRequest( - RespT message, LogData.Builder logDataBuilder, LoggerProvider loggerProvider) { - LoggingUtils.executeWithTryCatch( - () -> { - Logger logger = loggerProvider.getLogger(); - if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { - Slf4jUtils.log( - logger, Level.INFO, logDataBuilder.build().toMapRequest(), "Sending gRPC request"); - } - if (logger.isDebugEnabled()) { - Map messageToMapWithGson = - Slf4jUtils.messageToMapWithGson((Message) message); - - logDataBuilder.requestPayload(messageToMapWithGson); - Map requestDetailsMap = logDataBuilder.build().toMapRequest(); - Slf4jUtils.log(logger, Level.DEBUG, requestDetailsMap, "Sending gRPC request"); - } - }); - } } diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java index be0a9ab1fe..981b5ba693 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java @@ -30,7 +30,7 @@ package com.google.api.gax.logging; -import static com.google.api.gax.logging.Slf4jUtils.messageToMapWithGson; +import static com.google.api.gax.logging.Slf4jLoggingHelpers.messageToMapWithGson; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -162,7 +162,7 @@ void testRecordServiceRpcAndRequestHeaders_infoEnabled() { TestLogger testLogger = new TestLogger("test-logger", true, true); - Slf4jUtils.recordServiceRpcAndRequestHeaders( + Slf4jLoggingHelpers.recordServiceRpcAndRequestHeaders( serviceName, rpcName, endpoint, @@ -197,7 +197,7 @@ void testRecordServiceRpcAndRequestHeaders_infoDisabled() { TestLogger testLogger = new TestLogger("test-logger", false, false); LoggerProvider loggerProvider = setUpLoggerProviderMock(testLogger); - Slf4jUtils.recordServiceRpcAndRequestHeaders( + Slf4jLoggingHelpers.recordServiceRpcAndRequestHeaders( serviceName, rpcName, endpoint, requestHeaders, logDataBuilder, loggerProvider); LogData logData = logDataBuilder.build(); @@ -216,7 +216,7 @@ void testRecordResponseHeaders_debugEnabled() { LogData.Builder logDataBuilder = LogData.builder(); TestLogger testLogger = new TestLogger("test-logger", true, true); - Slf4jUtils.recordResponseHeaders( + Slf4jLoggingHelpers.recordResponseHeaders( responseHeaders, logDataBuilder, setUpLoggerProviderMock(testLogger)); LogData logData = logDataBuilder.build(); @@ -232,7 +232,7 @@ void testRecordResponseHeaders_debugDisabled() { LogData.Builder logDataBuilder = LogData.builder(); TestLogger testLogger = new TestLogger("test-logger", true, false); - Slf4jUtils.recordResponseHeaders( + Slf4jLoggingHelpers.recordResponseHeaders( responseHeaders, logDataBuilder, setUpLoggerProviderMock(testLogger)); LogData logData = logDataBuilder.build(); @@ -252,7 +252,8 @@ void testRecordResponsePayload_debugEnabled() { LogData.Builder logDataBuilder = LogData.builder(); TestLogger testLogger = new TestLogger("test-logger", true, true); - Slf4jUtils.recordResponsePayload(field, logDataBuilder, setUpLoggerProviderMock(testLogger)); + Slf4jLoggingHelpers.recordResponsePayload( + field, logDataBuilder, setUpLoggerProviderMock(testLogger)); LogData logData = logDataBuilder.build(); assertEquals(2, logData.responsePayload().size()); @@ -272,7 +273,7 @@ void testLogRequest_infoEnabled_debugDisabled() { when(logDataBuilder.build()).thenReturn(testLogDataBuilder.build()); TestLogger testLogger = new TestLogger("test", true, false); - Slf4jUtils.logRequest(message, logDataBuilder, setUpLoggerProviderMock(testLogger)); + Slf4jLoggingHelpers.logRequest(message, logDataBuilder, setUpLoggerProviderMock(testLogger)); assertEquals(2, testLogger.keyValuePairsMap.size()); assertEquals("Sending gRPC request", testLogger.messageList.get(0)); @@ -295,14 +296,14 @@ void testLogRequest_debugEnabled() throws InvalidProtocolBufferException { LogData.builder() .serviceName("service-name") .rpcName("rpc-name") - .requestPayload(Slf4jUtils.messageToMapWithGson(field)); + .requestPayload(Slf4jLoggingHelpers.messageToMapWithGson(field)); when(logDataBuilder.build()).thenReturn(testLogDataBuilder.build()); TestLogger testLogger = new TestLogger("test-logger", true, true); - Slf4jUtils.logRequest(field, logDataBuilder, setUpLoggerProviderMock(testLogger)); + Slf4jLoggingHelpers.logRequest(field, logDataBuilder, setUpLoggerProviderMock(testLogger)); - verify(logDataBuilder).requestPayload(Slf4jUtils.messageToMapWithGson(field)); + verify(logDataBuilder).requestPayload(Slf4jLoggingHelpers.messageToMapWithGson(field)); assertEquals(3, testLogger.keyValuePairsMap.size()); assertEquals(2, ((Map) testLogger.keyValuePairsMap.get("request.payload")).size()); @@ -325,7 +326,7 @@ void testLogResponse_infoEnabled_debugDisabled() { when(logDataBuilder.build()).thenReturn(testLogDataBuilder.build()); TestLogger testLogger = new TestLogger("test-logger", true, false); - Slf4jUtils.logResponse(status, logDataBuilder, setUpLoggerProviderMock(testLogger)); + Slf4jLoggingHelpers.logResponse(status, logDataBuilder, setUpLoggerProviderMock(testLogger)); verify(logDataBuilder).responseStatus(status); assertEquals("Received Grpc response", ((TestLogger) testLogger).messageList.get(0)); @@ -349,7 +350,7 @@ void testLogResponse_infoEnabled_debugEnabled() { when(logDataBuilder.build()).thenReturn(testLogDataBuilder.build()); TestLogger testLogger = new TestLogger("test-logger", true, true); - Slf4jUtils.logResponse(status, logDataBuilder, setUpLoggerProviderMock(testLogger)); + Slf4jLoggingHelpers.logResponse(status, logDataBuilder, setUpLoggerProviderMock(testLogger)); verify(logDataBuilder).responseStatus(status); assertEquals("Received Grpc response", ((TestLogger) testLogger).messageList.get(0)); From c3db3e7961c27060f2336f571b92ef097eddd652 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 18 Feb 2025 16:32:10 -0500 Subject: [PATCH 88/92] check type before casting to Message --- .../com/google/api/gax/logging/Slf4jLoggingHelpers.java | 8 ++++++++ .../main/java/com/google/api/gax/logging/Slf4jUtils.java | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jLoggingHelpers.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jLoggingHelpers.java index 28ff4441a4..8f2b1b5f0f 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jLoggingHelpers.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jLoggingHelpers.java @@ -97,6 +97,10 @@ static void recordResponsePayload( () -> { Logger logger = loggerProvider.getLogger(); if (logger.isDebugEnabled()) { + if (!(message instanceof Message)) { + // expect RespT to be Message type, otherwise do nothing and return + return; + } Map messageToMapWithGson = messageToMapWithGson((Message) message); logDataBuilder.responsePayload(messageToMapWithGson); @@ -133,6 +137,10 @@ static void logRequest( logger, Level.INFO, logDataBuilder.build().toMapRequest(), "Sending gRPC request"); } if (logger.isDebugEnabled()) { + if (!(message instanceof Message)) { + // expect RespT to be Message type, otherwise do nothing and return + return; + } Map messageToMapWithGson = messageToMapWithGson((Message) message); logDataBuilder.requestPayload(messageToMapWithGson); diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java index 587d1af5b2..e9bce1101d 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jUtils.java @@ -64,8 +64,6 @@ static boolean checkIfClazzAvailable(String clazzName) { } } - private Slf4jUtils() {} - static Logger getLogger(Class clazz) { return getLogger(clazz, new DefaultLoggerFactoryProvider()); } @@ -153,6 +151,8 @@ private static void logWithKeyValuePair( loggingEventBuilder.log(message); } + private Slf4jUtils() {} + interface LoggerFactoryProvider { ILoggerFactory getLoggerFactory(); } From a975fd5ce7141299805574a3de76946694a2d72b Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Tue, 18 Feb 2025 17:02:23 -0500 Subject: [PATCH 89/92] fix: rm grpc from log message. --- .../com/google/api/gax/logging/Slf4jLoggingHelpers.java | 8 ++++---- .../java/com/google/api/gax/logging/Slf4jUtilsTest.java | 4 ++-- .../com/google/showcase/v1beta1/it/logging/ITLogging.java | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jLoggingHelpers.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jLoggingHelpers.java index 8f2b1b5f0f..aa28b835d2 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jLoggingHelpers.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jLoggingHelpers.java @@ -118,11 +118,11 @@ static void logResponse( } if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { Map responseData = logDataBuilder.build().toMapResponse(); - Slf4jUtils.log(logger, Level.INFO, responseData, "Received Grpc response"); + Slf4jUtils.log(logger, Level.INFO, responseData, "Received response"); } if (logger.isDebugEnabled()) { Map responsedDetailsMap = logDataBuilder.build().toMapResponse(); - Slf4jUtils.log(logger, Level.DEBUG, responsedDetailsMap, "Received Grpc response"); + Slf4jUtils.log(logger, Level.DEBUG, responsedDetailsMap, "Received response"); } }); } @@ -134,7 +134,7 @@ static void logRequest( Logger logger = loggerProvider.getLogger(); if (logger.isInfoEnabled() && !logger.isDebugEnabled()) { Slf4jUtils.log( - logger, Level.INFO, logDataBuilder.build().toMapRequest(), "Sending gRPC request"); + logger, Level.INFO, logDataBuilder.build().toMapRequest(), "Sending request"); } if (logger.isDebugEnabled()) { if (!(message instanceof Message)) { @@ -145,7 +145,7 @@ static void logRequest( logDataBuilder.requestPayload(messageToMapWithGson); Map requestDetailsMap = logDataBuilder.build().toMapRequest(); - Slf4jUtils.log(logger, Level.DEBUG, requestDetailsMap, "Sending gRPC request"); + Slf4jUtils.log(logger, Level.DEBUG, requestDetailsMap, "Sending request"); } }); } diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java index 981b5ba693..8b12cc9b28 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java @@ -276,7 +276,7 @@ void testLogRequest_infoEnabled_debugDisabled() { Slf4jLoggingHelpers.logRequest(message, logDataBuilder, setUpLoggerProviderMock(testLogger)); assertEquals(2, testLogger.keyValuePairsMap.size()); - assertEquals("Sending gRPC request", testLogger.messageList.get(0)); + assertEquals("Sending request", testLogger.messageList.get(0)); verify(logDataBuilder, never()).requestPayload(anyMap()); // Ensure debug path is not taken assertEquals(Level.INFO, testLogger.level); @@ -307,7 +307,7 @@ void testLogRequest_debugEnabled() throws InvalidProtocolBufferException { assertEquals(3, testLogger.keyValuePairsMap.size()); assertEquals(2, ((Map) testLogger.keyValuePairsMap.get("request.payload")).size()); - assertEquals("Sending gRPC request", testLogger.messageList.get(0)); + assertEquals("Sending request", testLogger.messageList.get(0)); assertEquals(Level.DEBUG, testLogger.level); } diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java index 29d4f0226b..5979b88c41 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging.java @@ -57,8 +57,8 @@ public class ITLogging { private static final KeyValuePair RESPONSE_HEADERS_KEY_VALUE_PAIR = new KeyValuePair("response.headers", ImmutableMap.of("content-type", "application/grpc")); - private static final String SENDING_REQUEST_MESSAGE = "Sending gRPC request"; - private static final String RECEIVING_RESPONSE_MESSAGE = "Received Grpc response"; + private static final String SENDING_REQUEST_MESSAGE = "Sending request"; + private static final String RECEIVING_RESPONSE_MESSAGE = "Received response"; private static final String ECHO_STRING = "echo?"; private TestAppender setupTestLogger(Class clazz, Level level) { From b1af8794c03c29d07801c38ac287126368aa7a16 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Thu, 20 Feb 2025 15:08:06 -0500 Subject: [PATCH 90/92] continue: rm grpc from log message. --- .../google/api/gax/logging/Slf4jUtilsTest.java | 17 ++++++++--------- .../v1beta1/it/logging/ITLogging1x.java | 4 ++-- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java index 8b12cc9b28..b40ca1bc2c 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/logging/Slf4jUtilsTest.java @@ -329,11 +329,10 @@ void testLogResponse_infoEnabled_debugDisabled() { Slf4jLoggingHelpers.logResponse(status, logDataBuilder, setUpLoggerProviderMock(testLogger)); verify(logDataBuilder).responseStatus(status); - assertEquals("Received Grpc response", ((TestLogger) testLogger).messageList.get(0)); - assertEquals(3, ((TestLogger) testLogger).keyValuePairsMap.size()); - assertTrue(((TestLogger) testLogger).keyValuePairsMap.containsKey("response.payload")); - assertEquals(Level.INFO, ((TestLogger) testLogger).level); - Map keyValuePairsMap = ((TestLogger) testLogger).keyValuePairsMap; + assertEquals("Received response", (testLogger).messageList.get(0)); + assertEquals(3, (testLogger).keyValuePairsMap.size()); + assertTrue((testLogger).keyValuePairsMap.containsKey("response.payload")); + assertEquals(Level.INFO, (testLogger).level); } @Test @@ -353,11 +352,11 @@ void testLogResponse_infoEnabled_debugEnabled() { Slf4jLoggingHelpers.logResponse(status, logDataBuilder, setUpLoggerProviderMock(testLogger)); verify(logDataBuilder).responseStatus(status); - assertEquals("Received Grpc response", ((TestLogger) testLogger).messageList.get(0)); - assertEquals(3, ((TestLogger) testLogger).keyValuePairsMap.size()); - assertTrue(((TestLogger) testLogger).keyValuePairsMap.containsKey("response.payload")); + assertEquals("Received response", (testLogger).messageList.get(0)); + assertEquals(3, (testLogger).keyValuePairsMap.size()); + assertTrue((testLogger).keyValuePairsMap.containsKey("response.payload")); - assertEquals(Level.DEBUG, ((TestLogger) testLogger).level); + assertEquals(Level.DEBUG, (testLogger).level); } @Test diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java index bdc2d8f1df..d0dec4b80d 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/logging/ITLogging1x.java @@ -47,8 +47,8 @@ public class ITLogging1x { private static final String SERVICE_NAME = "google.showcase.v1beta1.Echo"; private static final String RPC_NAME = "google.showcase.v1beta1.Echo/Echo"; private static final String ENDPOINT = "http://localhost:7469"; - private static final String SENDING_REQUEST_MESSAGE = "Sending gRPC request"; - private static final String RECEIVING_RESPONSE_MESSAGE = "Received Grpc response"; + private static final String SENDING_REQUEST_MESSAGE = "Sending request"; + private static final String RECEIVING_RESPONSE_MESSAGE = "Received response"; private static Logger logger = LoggerFactory.getLogger(ITLogging1x.class); From 89da1b4a55d9c20d3a0d39aa4d924839dfa45c9d Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Fri, 21 Feb 2025 15:26:22 -0500 Subject: [PATCH 91/92] minor change: use guava isNullOrEmpty. --- .../java/com/google/api/gax/logging/Slf4jLoggingHelpers.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jLoggingHelpers.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jLoggingHelpers.java index aa28b835d2..d21f705b4c 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jLoggingHelpers.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jLoggingHelpers.java @@ -38,6 +38,7 @@ import com.google.protobuf.util.JsonFormat; import java.util.Map; import java.util.function.Consumer; +import jdk.internal.joptsimple.internal.Strings; import org.slf4j.Logger; import org.slf4j.event.Level; @@ -75,7 +76,7 @@ static void recordServiceRpcAndRequestHeaders( } private static void addIfNotEmpty(Consumer setter, String value) { - if (value != null && !value.isEmpty()) { + if (!Strings.isNullOrEmpty(value)) { setter.accept(value); } } From fad1865e70ce8367d6c971c4cb3ffaa879e1fb89 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Fri, 21 Feb 2025 15:59:24 -0500 Subject: [PATCH 92/92] fix typo. --- .../java/com/google/api/gax/logging/Slf4jLoggingHelpers.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jLoggingHelpers.java b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jLoggingHelpers.java index d21f705b4c..2a914f4bf6 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jLoggingHelpers.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/logging/Slf4jLoggingHelpers.java @@ -31,6 +31,7 @@ package com.google.api.gax.logging; import com.google.api.core.InternalApi; +import com.google.common.base.Strings; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import com.google.protobuf.InvalidProtocolBufferException; @@ -38,7 +39,6 @@ import com.google.protobuf.util.JsonFormat; import java.util.Map; import java.util.function.Consumer; -import jdk.internal.joptsimple.internal.Strings; import org.slf4j.Logger; import org.slf4j.event.Level;