Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add instrumentation for GRPC 1.30 #92

Merged
merged 2 commits into from
Oct 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,8 @@

import com.newrelic.api.agent.NewRelic;
import com.newrelic.api.agent.Segment;
import com.newrelic.api.agent.Token;
import com.newrelic.api.agent.Trace;
import com.newrelic.api.agent.weaver.NewField;
import com.newrelic.api.agent.weaver.Weave;
import com.newrelic.api.agent.weaver.WeaveAllConstructors;
import com.newrelic.api.agent.weaver.Weaver;
import com.nr.agent.instrumentation.grpc.GrpcConfig;
import io.grpc.ClientCall_Instrumentation;
Expand All @@ -22,7 +19,8 @@
public final class ClientCalls_Instrumentation {

private static <ReqT, RespT> void startCall(ClientCall_Instrumentation<ReqT, RespT> call,
ClientCall_Instrumentation.Listener<RespT> responseListener, boolean streamingResponse) {
ClientCall_Instrumentation.Listener<RespT> responseListener,
boolean streamingResponse) {
Segment segment = NewRelic.getAgent().getTransaction().startSegment("gRPC", "External");
call.segment = segment;
Weaver.callOriginal();
Expand Down
55 changes: 55 additions & 0 deletions instrumentation/grpc-1.30.0/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.13'

}
}
apply plugin: 'com.google.protobuf'
compileJava.options.bootstrapClasspath = null

sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
compileTestJava {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

dependencies {
implementation(project(":agent-bridge"))
implementation("io.grpc:grpc-all:1.30.1")
implementation("com.google.protobuf:protobuf-java:3.7.0")
implementation("io.grpc:grpc-protobuf:1.30.1")
implementation("io.perfmark:perfmark-api:0.23.0")

}

jar {
manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.grpc-1.30.0' }
}

verifyInstrumentation {
passesOnly 'io.grpc:grpc-all:[1.30.0,)'
}

def grpcVersion = '1.30.1' // CURRENT_GRPC_VERSION
def protobufVersion = '3.6.1'
def protocVersion = protobufVersion

// to generate the proto classes, run ./gradlew generateTestProto
protobuf {
protoc { artifact = "com.google.protobuf:protoc:${protocVersion}" }
plugins {
grpc { artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}" }
}
generateProtoTasks {
all()*.plugins { grpc {} }
}
}

site {
title 'gRPC'
type 'Messaging'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
*
* * Copyright 2020 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package com.nr.agent.instrumentation.grpc;

import com.newrelic.api.agent.NewRelic;

public class GrpcConfig {

public static final boolean disributedTracingEnabled = NewRelic.getAgent().getConfig().getValue("grpc.distributed_tracing.enabled", true);

public static final boolean errorsEnabled = NewRelic.getAgent().getConfig().getValue("grpc.errors.enabled", true);

private GrpcConfig() {
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
*
* * Copyright 2020 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package com.nr.agent.instrumentation.grpc;

import com.newrelic.api.agent.ExtendedRequest;
import com.newrelic.api.agent.HeaderType;
import io.grpc.Metadata;
import io.grpc.internal.GrpcUtil;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;

public class GrpcRequest extends ExtendedRequest {

private final String fullMethodName;
private final String authority;
private final Metadata metadata;

public GrpcRequest(String fullMethodName, String authority, Metadata metadata) {
this.fullMethodName = fullMethodName;
this.authority = authority;
this.metadata = metadata;
}

@Override
public String getMethod() {
return GrpcUtil.HTTP_METHOD;
}

@Override
public String getRequestURI() {
try {
String path = "/" + fullMethodName;
return new URI("grpc", authority, path, null, null).toASCIIString();
} catch (URISyntaxException e) {
return null;
}
}

@Override
public String getRemoteUser() {
return null;
}

@Override
public Enumeration getParameterNames() {
return null;
}

@Override
public String[] getParameterValues(String name) {
return new String[0];
}

@Override
public Object getAttribute(String name) {
return null;
}

@Override
public String getCookieValue(String name) {
return null;
}

@Override
public HeaderType getHeaderType() {
return HeaderType.HTTP;
}

@Override
public String getHeader(String name) {
return metadata.get(Metadata.Key.of(name, Metadata.ASCII_STRING_MARSHALLER));
}

@Override
public List<String> getHeaders(String name) {
return iterableToList(metadata.getAll(Metadata.Key.of(name, Metadata.ASCII_STRING_MARSHALLER)));
}

private List<String> iterableToList(Iterable<String> headers) {
if (headers == null) {
return null;
}

List<String> result = new ArrayList<>();
for (String header : headers) {
result.add(header);
}
return result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
*
* * Copyright 2020 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package com.nr.agent.instrumentation.grpc;

import com.newrelic.api.agent.ExtendedResponse;
import com.newrelic.api.agent.HeaderType;
import io.grpc.Metadata;
import io.grpc.Status;

public class GrpcResponse extends ExtendedResponse {

private final Status status;
private final Metadata headers;

public GrpcResponse(Status status, Metadata headers) {
this.status = status;
this.headers = headers;
}

@Override
public long getContentLength() {
// There isn't a good value here for gRPC
return -1;
}

@Override
public int getStatus() throws Exception {
return status.getCode().value();
}

@Override
public String getStatusMessage() throws Exception {
return status.getDescription();
}

@Override
public String getContentType() {
return null;
}

@Override
public HeaderType getHeaderType() {
return HeaderType.HTTP;
}

@Override
public void setHeader(String name, String value) {
if (GrpcConfig.disributedTracingEnabled) {
headers.put(Metadata.Key.of(name, Metadata.ASCII_STRING_MARSHALLER), value);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
*
* * Copyright 2020 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package com.nr.agent.instrumentation.grpc;

import com.newrelic.api.agent.ExtendedInboundHeaders;
import com.newrelic.api.agent.HeaderType;
import io.grpc.Metadata;

import java.util.ArrayList;
import java.util.List;

public class InboundHeadersWrapper extends ExtendedInboundHeaders {

private final Metadata metadata;
private final Metadata trailers;

public InboundHeadersWrapper(Metadata metadata, Metadata trailers) {
this.metadata = metadata;
this.trailers = trailers;
}

@Override
public String getHeader(String key) {
String value = null;
if (metadata != null) {
value = metadata.get(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER));
}
// If we didn't find the value in the headers be sure to check the trailers as a fallback
if (value == null && trailers != null) {
value = trailers.get(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER));
}
return value;
}

@Override
public List<String> getHeaders(String name) {
List<String> result = iterableToList(metadata.getAll(Metadata.Key.of(name, Metadata.ASCII_STRING_MARSHALLER)));
if ((result == null || result.isEmpty()) && trailers != null) {
// If we didn't find the value in the headers be sure to check the trailers as a fallback
result = iterableToList(trailers.getAll(Metadata.Key.of(name, Metadata.ASCII_STRING_MARSHALLER)));
}
return result;
}

private List<String> iterableToList(Iterable<String> headers) {
if (headers == null) {
return null;
}

List<String> result = new ArrayList<>();
for (String header : headers) {
result.add(header);
}
return result;
}

@Override
public HeaderType getHeaderType() {
return HeaderType.HTTP;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
*
* * Copyright 2020 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package com.nr.agent.instrumentation.grpc;

import com.newrelic.api.agent.HeaderType;
import com.newrelic.api.agent.OutboundHeaders;
import io.grpc.Metadata;

public class OutboundHeadersWrapper implements OutboundHeaders {

private final Metadata metadata;

public OutboundHeadersWrapper(Metadata metadata) {
this.metadata = metadata;
}

@Override
public HeaderType getHeaderType() {
return HeaderType.HTTP;
}

@Override
public void setHeader(String key, String value) {
if (GrpcConfig.disributedTracingEnabled) {
metadata.put(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER), value);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
*
* * Copyright 2020 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package io.grpc;

import com.newrelic.api.agent.NewRelic;
import com.newrelic.api.agent.weaver.MatchType;
import com.newrelic.api.agent.weaver.Weave;
import com.newrelic.api.agent.weaver.Weaver;

@Weave(type = MatchType.BaseClass, originalName = "io.grpc.Channel")
public class Channel_Instrumentation {

public <RequestT, ResponseT> ClientCall_Instrumentation<RequestT, ResponseT> newCall(
MethodDescriptor<RequestT, ResponseT> methodDescriptor, CallOptions callOptions) {
ClientCall_Instrumentation<RequestT, ResponseT> result = Weaver.callOriginal();
result.methodDescriptor = methodDescriptor;
result.authority = authority();
if (methodDescriptor != null && methodDescriptor.getType() != null) {
NewRelic.addCustomParameter("grpc.type", methodDescriptor.getType().name());
}
return result;
}

public String authority() {
return Weaver.callOriginal();
}
}
Loading