Make channelz work with proto lite (#11685)

Allows android apps to expose internal grpc state for debugging.
This commit is contained in:
John Cormie 2024-11-13 16:50:14 -08:00 committed by GitHub
parent 921f88ae30
commit b1703345f7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 147 additions and 85 deletions

View File

@ -21,6 +21,7 @@ import com.google.common.util.concurrent.ListenableFuture;
import com.google.protobuf.Any; import com.google.protobuf.Any;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import com.google.protobuf.Int64Value; import com.google.protobuf.Int64Value;
import com.google.protobuf.MessageLite;
import com.google.protobuf.util.Durations; import com.google.protobuf.util.Durations;
import com.google.protobuf.util.Timestamps; import com.google.protobuf.util.Timestamps;
import io.grpc.ConnectivityState; import io.grpc.ConnectivityState;
@ -79,6 +80,8 @@ import java.util.logging.Logger;
/** /**
* A static utility class for turning internal data structures into protos. * A static utility class for turning internal data structures into protos.
*
* <p>Works with both regular and lite protos.
*/ */
final class ChannelzProtoUtil { final class ChannelzProtoUtil {
private static final Logger logger = Logger.getLogger(ChannelzProtoUtil.class.getName()); private static final Logger logger = Logger.getLogger(ChannelzProtoUtil.class.getName());
@ -254,22 +257,20 @@ final class ChannelzProtoUtil {
} else { } else {
lingerOpt = SocketOptionLinger.getDefaultInstance(); lingerOpt = SocketOptionLinger.getDefaultInstance();
} }
return SocketOption return SocketOption.newBuilder()
.newBuilder()
.setName(SO_LINGER) .setName(SO_LINGER)
.setAdditional(Any.pack(lingerOpt)) .setAdditional(packToAny("SocketOptionLinger", lingerOpt))
.build(); .build();
} }
static SocketOption toSocketOptionTimeout(String name, int timeoutMillis) { static SocketOption toSocketOptionTimeout(String name, int timeoutMillis) {
Preconditions.checkNotNull(name); Preconditions.checkNotNull(name);
return SocketOption return SocketOption.newBuilder()
.newBuilder()
.setName(name) .setName(name)
.setAdditional( .setAdditional(
Any.pack( packToAny(
SocketOptionTimeout "SocketOptionTimeout",
.newBuilder() SocketOptionTimeout.newBuilder()
.setDuration(Durations.fromMillis(timeoutMillis)) .setDuration(Durations.fromMillis(timeoutMillis))
.build())) .build()))
.build(); .build();
@ -307,10 +308,9 @@ final class ChannelzProtoUtil {
.setTcpiAdvmss(i.advmss) .setTcpiAdvmss(i.advmss)
.setTcpiReordering(i.reordering) .setTcpiReordering(i.reordering)
.build(); .build();
return SocketOption return SocketOption.newBuilder()
.newBuilder()
.setName(TCP_INFO) .setName(TCP_INFO)
.setAdditional(Any.pack(tcpInfo)) .setAdditional(packToAny("SocketOptionTcpInfo", tcpInfo))
.build(); .build();
} }
@ -380,9 +380,10 @@ final class ChannelzProtoUtil {
private static List<ChannelTraceEvent> toChannelTraceEvents(List<Event> events) { private static List<ChannelTraceEvent> toChannelTraceEvents(List<Event> events) {
List<ChannelTraceEvent> channelTraceEvents = new ArrayList<>(); List<ChannelTraceEvent> channelTraceEvents = new ArrayList<>();
for (Event event : events) { for (Event event : events) {
ChannelTraceEvent.Builder builder = ChannelTraceEvent.newBuilder() ChannelTraceEvent.Builder builder =
ChannelTraceEvent.newBuilder()
.setDescription(event.description) .setDescription(event.description)
.setSeverity(Severity.valueOf(event.severity.name())) .setSeverity(toSeverity(event.severity))
.setTimestamp(Timestamps.fromNanos(event.timestampNanos)); .setTimestamp(Timestamps.fromNanos(event.timestampNanos));
if (event.channelRef != null) { if (event.channelRef != null) {
builder.setChannelRef(toChannelRef(event.channelRef)); builder.setChannelRef(toChannelRef(event.channelRef));
@ -395,13 +396,38 @@ final class ChannelzProtoUtil {
return Collections.unmodifiableList(channelTraceEvents); return Collections.unmodifiableList(channelTraceEvents);
} }
static Severity toSeverity(Event.Severity severity) {
if (severity == null) {
return Severity.CT_UNKNOWN;
}
switch (severity) {
case CT_INFO:
return Severity.CT_INFO;
case CT_ERROR:
return Severity.CT_ERROR;
case CT_WARNING:
return Severity.CT_WARNING;
default:
return Severity.CT_UNKNOWN;
}
}
static State toState(ConnectivityState state) { static State toState(ConnectivityState state) {
if (state == null) { if (state == null) {
return State.UNKNOWN; return State.UNKNOWN;
} }
try { switch (state) {
return Enum.valueOf(State.class, state.name()); case IDLE:
} catch (IllegalArgumentException e) { return State.IDLE;
case READY:
return State.READY;
case CONNECTING:
return State.CONNECTING;
case SHUTDOWN:
return State.SHUTDOWN;
case TRANSIENT_FAILURE:
return State.TRANSIENT_FAILURE;
default:
return State.UNKNOWN; return State.UNKNOWN;
} }
} }
@ -468,4 +494,12 @@ final class ChannelzProtoUtil {
throw Status.INTERNAL.withCause(e).asRuntimeException(); throw Status.INTERNAL.withCause(e).asRuntimeException();
} }
} }
// A version of Any.pack() that works with protolite.
private static Any packToAny(String typeName, MessageLite value) {
return Any.newBuilder()
.setTypeUrl("type.googleapis.com/grpc.channelz.v1." + typeName)
.setValue(value.toByteString())
.build();
}
} }

View File

@ -27,7 +27,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.protobuf.Any; import com.google.protobuf.Any;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import com.google.protobuf.Int64Value; import com.google.protobuf.Int64Value;
import com.google.protobuf.Message; import com.google.protobuf.MessageLite;
import com.google.protobuf.util.Durations; import com.google.protobuf.util.Durations;
import com.google.protobuf.util.Timestamps; import com.google.protobuf.util.Timestamps;
import io.grpc.ConnectivityState; import io.grpc.ConnectivityState;
@ -154,33 +154,44 @@ public final class ChannelzProtoUtilTest {
.setData(serverData) .setData(serverData)
.build(); .build();
private final SocketOption sockOptLingerDisabled = SocketOption private final SocketOption sockOptLingerDisabled =
.newBuilder() SocketOption.newBuilder()
.setName("SO_LINGER") .setName("SO_LINGER")
.setAdditional( .setAdditional(
Any.pack(SocketOptionLinger.getDefaultInstance())) Any.newBuilder()
.setTypeUrl("type.googleapis.com/grpc.channelz.v1.SocketOptionLinger")
.setValue(SocketOptionLinger.getDefaultInstance().toByteString())
.build())
.build(); .build();
private final SocketOption sockOptlinger10s = SocketOption private final SocketOption sockOptlinger10s =
.newBuilder() SocketOption.newBuilder()
.setName("SO_LINGER") .setName("SO_LINGER")
.setAdditional( .setAdditional(
Any.pack(SocketOptionLinger Any.newBuilder()
.newBuilder() .setTypeUrl("type.googleapis.com/grpc.channelz.v1.SocketOptionLinger")
.setValue(
SocketOptionLinger.newBuilder()
.setActive(true) .setActive(true)
.setDuration(Durations.fromSeconds(10)) .setDuration(Durations.fromSeconds(10))
.build())) .build()
.toByteString())
.build())
.build(); .build();
private final SocketOption sockOptTimeout200ms = SocketOption private final SocketOption sockOptTimeout200ms =
.newBuilder() SocketOption.newBuilder()
.setName("SO_TIMEOUT") .setName("SO_TIMEOUT")
.setAdditional( .setAdditional(
Any.pack(SocketOptionTimeout Any.newBuilder()
.newBuilder() .setTypeUrl("type.googleapis.com/grpc.channelz.v1.SocketOptionTimeout")
.setValue(
SocketOptionTimeout.newBuilder()
.setDuration(Durations.fromMillis(200)) .setDuration(Durations.fromMillis(200))
.build()
.toByteString())
.build()) .build())
).build(); .build();
private final SocketOption sockOptAdditional = SocketOption private final SocketOption sockOptAdditional = SocketOption
.newBuilder() .newBuilder()
@ -221,11 +232,13 @@ public final class ChannelzProtoUtilTest {
.setReordering(728) .setReordering(728)
.build(); .build();
private final SocketOption socketOptionTcpInfo = SocketOption private final SocketOption socketOptionTcpInfo =
.newBuilder() SocketOption.newBuilder()
.setName("TCP_INFO") .setName("TCP_INFO")
.setAdditional( .setAdditional(
Any.pack( Any.newBuilder()
.setTypeUrl("type.googleapis.com/grpc.channelz.v1.SocketOptionTcpInfo")
.setValue(
SocketOptionTcpInfo.newBuilder() SocketOptionTcpInfo.newBuilder()
.setTcpiState(70) .setTcpiState(70)
.setTcpiCaState(71) .setTcpiCaState(71)
@ -256,7 +269,8 @@ public final class ChannelzProtoUtilTest {
.setTcpiSndCwnd(726) .setTcpiSndCwnd(726)
.setTcpiAdvmss(727) .setTcpiAdvmss(727)
.setTcpiReordering(728) .setTcpiReordering(728)
.build())) .build()
.toByteString()))
.build(); .build();
private final TestListenSocket listenSocket = new TestListenSocket(); private final TestListenSocket listenSocket = new TestListenSocket();
@ -336,6 +350,16 @@ public final class ChannelzProtoUtilTest {
assertEquals(serverRef, ChannelzProtoUtil.toServerRef(server)); assertEquals(serverRef, ChannelzProtoUtil.toServerRef(server));
} }
@Test
public void toSeverity() {
for (Severity severity : Severity.values()) {
assertEquals(
severity.name(),
ChannelzProtoUtil.toSeverity(severity).name()); // OK because test isn't proguarded.
}
assertEquals(ChannelTraceEvent.Severity.CT_UNKNOWN, ChannelzProtoUtil.toSeverity(null));
}
@Test @Test
public void toSocketRef() { public void toSocketRef() {
assertEquals(socketRef, ChannelzProtoUtil.toSocketRef(socket)); assertEquals(socketRef, ChannelzProtoUtil.toSocketRef(socket));
@ -346,7 +370,7 @@ public final class ChannelzProtoUtilTest {
for (ConnectivityState connectivityState : ConnectivityState.values()) { for (ConnectivityState connectivityState : ConnectivityState.values()) {
assertEquals( assertEquals(
connectivityState.name(), connectivityState.name(),
ChannelzProtoUtil.toState(connectivityState).getValueDescriptor().getName()); ChannelzProtoUtil.toState(connectivityState).name()); // OK because test isn't proguarded.
} }
assertEquals(State.UNKNOWN, ChannelzProtoUtil.toState(null)); assertEquals(State.UNKNOWN, ChannelzProtoUtil.toState(null));
} }
@ -475,8 +499,12 @@ public final class ChannelzProtoUtilTest {
@Test @Test
public void socketSecurityOther() throws Exception { public void socketSecurityOther() throws Exception {
// what is packed here is not important, just pick some proto message // what is packed here is not important, just pick some proto message
Message contents = GetChannelRequest.newBuilder().setChannelId(1).build(); MessageLite contents = GetChannelRequest.newBuilder().setChannelId(1).build();
Any packed = Any.pack(contents); Any packed =
Any.newBuilder()
.setTypeUrl("type.googleapis.com/grpc.channelz.v1.GetChannelRequest")
.setValue(contents.toByteString())
.build();
socket.security socket.security
= new InternalChannelz.Security( = new InternalChannelz.Security(
new InternalChannelz.OtherSecurity("other_security", packed)); new InternalChannelz.OtherSecurity("other_security", packed));