diff --git a/instrumentation/grpc-1.6/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/grpc/v1_6/ProtobufMessageConverter.java b/instrumentation/grpc-1.6/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/grpc/v1_6/ProtobufMessageConverter.java index ff9d7a33..bdd991e5 100644 --- a/instrumentation/grpc-1.6/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/grpc/v1_6/ProtobufMessageConverter.java +++ b/instrumentation/grpc-1.6/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/grpc/v1_6/ProtobufMessageConverter.java @@ -23,11 +23,17 @@ import io.opentelemetry.javaagent.instrumentation.hypertrace.com.google.protobuf.Descriptors.FileDescriptor; import io.opentelemetry.javaagent.instrumentation.hypertrace.com.google.protobuf.DynamicMessage; import io.opentelemetry.javaagent.instrumentation.hypertrace.com.google.protobuf.util.JsonFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ProtobufMessageConverter { private static final Logger log = LoggerFactory.getLogger(ProtobufMessageConverter.class); + private static final Map fileDescriptorCache = new HashMap<>(); + /** * Converts an unrelocated protobuf message into a relocated DynamicMessage via a byte-array * round-trip. @@ -43,31 +49,64 @@ public static DynamicMessage convertToRelocatedDynamicMessage(Message message) t // 2. Obtain the original (unrelocated) message descriptor. Descriptors.Descriptor originalDescriptor = message.getDescriptorForType(); - // 3. Get the unrelocated file descriptor and its proto representation. + // 3. Build the relocated descriptor with all dependencies + Descriptor relocatedDescriptor = getRelocatedDescriptor(originalDescriptor); + if (relocatedDescriptor == null) { + throw new IllegalStateException( + "Could not find relocated descriptor for message type: " + + originalDescriptor.getFullName()); + } + + // 4. Parse the original message bytes using the relocated descriptor. + try { + return DynamicMessage.parseFrom(relocatedDescriptor, messageBytes); + } catch (Exception e) { + log.debug("Failed to parse message bytes using relocated descriptor: {}", e.getMessage()); + throw e; + } + } + + /** Recursively builds relocated file descriptors with all dependencies. */ + private static Descriptor getRelocatedDescriptor(Descriptors.Descriptor originalDescriptor) + throws Exception { Descriptors.FileDescriptor unrelocatedFileDescriptor = originalDescriptor.getFile(); + + // Check if we've already processed this file descriptor + String fileKey = unrelocatedFileDescriptor.getName(); + if (fileDescriptorCache.containsKey(fileKey)) { + FileDescriptor relocatedFileDescriptor = fileDescriptorCache.get(fileKey); + return relocatedFileDescriptor.findMessageTypeByName(originalDescriptor.getName()); + } + + // Process all dependencies first + List dependencies = new ArrayList<>(); + for (Descriptors.FileDescriptor dependency : unrelocatedFileDescriptor.getDependencies()) { + String depKey = dependency.getName(); + if (!fileDescriptorCache.containsKey(depKey)) { + // Convert the dependency file descriptor + com.google.protobuf.DescriptorProtos.FileDescriptorProto depProto = dependency.toProto(); + byte[] depBytes = depProto.toByteArray(); + FileDescriptorProto relocatedDepProto = FileDescriptorProto.parseFrom(depBytes); + + // Build with empty dependencies first (we'll fill them in later) + FileDescriptor relocatedDep = + FileDescriptor.buildFrom(relocatedDepProto, new FileDescriptor[] {}); + fileDescriptorCache.put(depKey, relocatedDep); + } + dependencies.add(fileDescriptorCache.get(depKey)); + } + + // Now build the current file descriptor with its dependencies com.google.protobuf.DescriptorProtos.FileDescriptorProto unrelocatedFileProto = unrelocatedFileDescriptor.toProto(); byte[] fileProtoBytes = unrelocatedFileProto.toByteArray(); - - // 4. Parse the file descriptor proto using relocated classes. - // This converts the unrelocated FileDescriptorProto into your relocated FileDescriptorProto. FileDescriptorProto relocatedFileProto = FileDescriptorProto.parseFrom(fileProtoBytes); - // 5. Build the relocated FileDescriptor. FileDescriptor relocatedFileDescriptor = - FileDescriptor.buildFrom(relocatedFileProto, new FileDescriptor[] {}); - - // 6. Find the relocated message descriptor by name. - Descriptor relocatedDescriptor = - relocatedFileDescriptor.findMessageTypeByName(originalDescriptor.getName()); - if (relocatedDescriptor == null) { - throw new IllegalStateException( - "Could not find relocated descriptor for message type: " + originalDescriptor.getName()); - } + FileDescriptor.buildFrom(relocatedFileProto, dependencies.toArray(new FileDescriptor[0])); + fileDescriptorCache.put(fileKey, relocatedFileDescriptor); - // 7. Parse the original message bytes using the relocated descriptor. - DynamicMessage relocatedMessage = DynamicMessage.parseFrom(relocatedDescriptor, messageBytes); - return relocatedMessage; + return relocatedFileDescriptor.findMessageTypeByName(originalDescriptor.getName()); } /** @@ -77,17 +116,25 @@ public static DynamicMessage convertToRelocatedDynamicMessage(Message message) t * @param message The incoming (unrelocated) protobuf message. */ public static String getMessage(Message message) { + if (message == null) { + log.debug("Cannot convert null message to JSON"); + return ""; + } + try { // Convert the unrelocated message into a relocated DynamicMessage. DynamicMessage relocatedMessage = convertToRelocatedDynamicMessage(message); // Use the relocated JsonFormat to print the message as JSON. - JsonFormat.Printer relocatedPrinter = JsonFormat.printer(); - String jsonOutput = relocatedPrinter.print(relocatedMessage); - - return jsonOutput; + JsonFormat.Printer relocatedPrinter = + JsonFormat.printer().includingDefaultValueFields().preservingProtoFieldNames(); + return relocatedPrinter.print(relocatedMessage); } catch (Exception e) { - log.error("Failed to convert message with relocated protobuf message: {}", e.getMessage()); + log.error("Failed to convert message to JSON: {}", e.getMessage(), e); + if (log.isDebugEnabled()) { + log.debug("Message type: {}", message.getClass().getName()); + log.debug("Message descriptor: {}", message.getDescriptorForType().getFullName()); + } } return ""; }