Skip to content

Commit

Permalink
feat: add option for logging gRPC messages (#2230)
Browse files Browse the repository at this point in the history
Adds an option for logging all gRPC messages that are sent by
PGAdapter to Spanner to make it easier to get insights into
what exactly PGAdapter and/or an application does. This option
should only be used for debugging, and is not recommended for
production use, as it will produce a large amount of log lines.
  • Loading branch information
olavloite authored Aug 27, 2024
1 parent f44b4fa commit 17c620d
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
import com.google.api.core.InternalApi;
import com.google.auth.Credentials;
import com.google.cloud.spanner.connection.ConnectionOptions.Builder;
import com.google.cloud.spanner.pgadapter.logging.GrpcLogInterceptor;
import com.google.common.collect.ImmutableList;
import java.util.logging.Level;

/** Simple helper class to get access to a package-private method in the ConnectionOptions. */
@InternalApi
Expand All @@ -28,4 +31,22 @@ private ConnectionOptionsHelper() {}
public static Builder setCredentials(Builder connectionOptionsBuilder, Credentials credentials) {
return connectionOptionsBuilder.setCredentials(credentials);
}

/** Adds a gRPC log interceptor if the log level has been set to FINEST. */
public static Builder maybeAddGrpcLogInterceptor(
Builder connectionOptionsBuilder, boolean logGrpcMessagesAsInfo) {
if (GrpcLogInterceptor.isGrpcFinestLogEnabled()) {
return connectionOptionsBuilder.setConfigurator(
options ->
options.setInterceptorProvider(
() -> ImmutableList.of(new GrpcLogInterceptor(Level.FINEST))));
}
if (logGrpcMessagesAsInfo) {
return connectionOptionsBuilder.setConfigurator(
options ->
options.setInterceptorProvider(
() -> ImmutableList.of(new GrpcLogInterceptor(Level.INFO))));
}
return connectionOptionsBuilder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ public void connectToSpanner(String database, @Nullable Credentials credentials)
OptionsMetadata options = getServer().getOptions();
String uri = buildConnectionURL(database, options, getServer().getProperties());
ConnectionOptions.Builder connectionOptionsBuilder = ConnectionOptions.newBuilder().setUri(uri);
connectionOptionsBuilder =
ConnectionOptionsHelper.maybeAddGrpcLogInterceptor(
connectionOptionsBuilder, options.isLogGrpcMessages());
if (credentials != null) {
connectionOptionsBuilder =
ConnectionOptionsHelper.setCredentials(connectionOptionsBuilder, credentials);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// 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
//
// http://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.cloud.spanner.pgadapter.logging;

import com.google.api.core.InternalApi;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.MessageOrBuilder;
import com.google.protobuf.util.JsonFormat;
import com.google.protobuf.util.JsonFormat.Printer;
import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ClientInterceptor;
import io.grpc.ForwardingClientCall.SimpleForwardingClientCall;
import io.grpc.MethodDescriptor;
import java.util.logging.Level;
import java.util.logging.Logger;

/** gRPC interceptor for logging all messages that are sent by PGAdapter. */
@InternalApi
public class GrpcLogInterceptor implements ClientInterceptor {
private static final Logger logger = Logger.getLogger(GrpcLogInterceptor.class.getName());

private static final Printer PRINTER = JsonFormat.printer();

private final Level level;

public GrpcLogInterceptor(Level level) {
this.level = level;
}

public static boolean isGrpcFinestLogEnabled() {
return logger.isLoggable(Level.FINEST);
}

@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
return new SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) {
@Override
public void sendMessage(ReqT message) {
logger.log(level, () -> GrpcLogInterceptor.printMessage((MessageOrBuilder) message));
super.sendMessage(message);
}
};
}

private static String printMessage(MessageOrBuilder message) {
try {
return message.getClass().getSimpleName() + " " + PRINTER.print(message);
} catch (InvalidProtocolBufferException ignore) {
return "(unknown message)";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ public static class Builder {
private int port;
private String unixDomainSocketDirectory;
private boolean autoConfigEmulator;
private boolean logGrpcMessages;
private boolean debugMode;
private String endpoint;
private boolean usePlainText;
Expand Down Expand Up @@ -359,6 +360,15 @@ public Builder autoConfigureEmulator() {
return this.setCredentials(NoCredentials.getInstance());
}

/**
* Enables logging of all gRPC messages that are sent by PGAdapter to Spanner. Only enable this
* for debugging purposes.
*/
public Builder setLogGrpcMessages(boolean logGrpcMessages) {
this.logGrpcMessages = logGrpcMessages;
return this;
}

Builder enableDebugMode() {
this.debugMode = true;
return this;
Expand Down Expand Up @@ -473,6 +483,9 @@ private String[] toCommandLineArguments() {
}
addOption(args, OPTION_JDBC_PROPERTIES, jdbcOptionBuilder.toString());
}
if (logGrpcMessages) {
addOption(args, OPTION_LOG_GRPC_MESSAGES);
}
if (debugMode) {
addOption(args, OPTION_INTERNAL_DEBUG_MODE);
addOption(args, OPTION_SKIP_INTERNAL_DEBUG_MODE_WARNING);
Expand Down Expand Up @@ -590,6 +603,7 @@ public enum DdlTransactionMode {
"skip_internal_debug_warning";
private static final String OPTION_DEBUG_MODE = "debug";
private static final String OPTION_LEGACY_LOGGING = "legacy_logging";
private static final String OPTION_LOG_GRPC_MESSAGES = "log_grpc_messages";

private final Map<String, String> environment;
private final String osName;
Expand Down Expand Up @@ -620,6 +634,7 @@ public enum DdlTransactionMode {
private final String serverVersion;
private final boolean debugMode;
private final Duration startupTimeout;
private final boolean logGrpcMessages;

/**
* Creates a new instance of {@link OptionsMetadata} from the given arguments.
Expand Down Expand Up @@ -712,6 +727,7 @@ private OptionsMetadata(Builder builder) {
this.disableLocalhostCheck = commandLine.hasOption(OPTION_DISABLE_LOCALHOST_CHECK);
this.serverVersion = commandLine.getOptionValue(OPTION_SERVER_VERSION, DEFAULT_SERVER_VERSION);
this.debugMode = commandLine.hasOption(OPTION_INTERNAL_DEBUG_MODE);
this.logGrpcMessages = commandLine.hasOption(OPTION_LOG_GRPC_MESSAGES);
this.startupTimeout = startupTimeout;
}

Expand Down Expand Up @@ -785,6 +801,7 @@ public OptionsMetadata(
this.disableLocalhostCheck = false;
this.serverVersion = DEFAULT_SERVER_VERSION;
this.debugMode = false;
this.logGrpcMessages = false;
this.startupTimeout = DEFAULT_STARTUP_TIMEOUT;
}

Expand Down Expand Up @@ -1268,6 +1285,13 @@ private CommandLine buildOptions(String[] args) {
+ "have been tested with PGAdapter have been tested using the default value for this option. Changing "
+ "the value of this option could cause a client or driver to alter its behavior and cause unexpected "
+ "errors when used with PGAdapter.");
options.addOption(
OPTION_LOG_GRPC_MESSAGES,
"log-grpc-messages",
false,
"Logs all gRPC messages that are sent by PGAdapter to Spanner to the default log handler (stdout).\n"
+ "This option should only be enabled to debug problems and/or to determine exactly which gRPC messages\n"
+ "are being sent to Spanner. Enabling this in production will cause a large number of messages to be logged.");
options.addOption(
OPTION_INTERNAL_DEBUG_MODE,
"internal-debug-mode",
Expand Down Expand Up @@ -1364,6 +1388,10 @@ public boolean isBinaryFormat() {
return this.binaryFormat;
}

public boolean isLogGrpcMessages() {
return this.logGrpcMessages;
}

public boolean isDebugMode() {
return this.debugMode;
}
Expand Down

0 comments on commit 17c620d

Please sign in to comment.