Frequently Asked Questions

How can I trace gRPC issues?

For libraries that use gRPC, it can be very useful to hook into the gRPC logging framework. There are two aspects to this:

  • Setting environment variables
  • Directing logs

The environment variables affecting gRPC are listed in the gRPC repository. The important ones for diagnostics are GRPC_TRACE and GRPC_VERBOSITY. For example, you might want to start off with GRPC_TRACE=all and GRPC_VERBOSITY=DEBUG which will dump a lot of information, then tweak them to reduce this to only useful data... or start with one kind of tracing (e.g. GRPC_TRACE=call_error) and add more as required.

By default, the gRPC logs will not be displayed anywhere. The simplest way of seeing gRPC logs in many cases will be to send them to the console:

using Grpc.Core;
using Grpc.Core.Logging;
...
// Call this before you do any gRPC work
GrpcEnvironment.SetLogger(new ConsoleLogger());

Other ILogger implementations are available, or you can implement it yourself to integrate with other systems - see the Grpc.Core.Logging namespace for details.

How can I trace requests and responses in REST-based APIs?

For libraries that use HTTP1.1 and REST, it can be useful to perfom request and response logging. There are two aspects to this:

  • Registering a global logger
  • Configuring the events to log in a specific service

The underlying service is available via the Service property in each XyzClient class. Within that service, you need to configure the HttpClient's message handler. As a complete example, here's a call to the Translation API, listing all the available languages, and logging the request headers and the response body:

// Required using directives:
// using static Google.Apis.Http.ConfigurableMessageHandler;
// using Google.Apis.Logging;
// using Google.Cloud.Translation.V2;

// Register a verbose console logger
ApplicationContext.RegisterLogger(new ConsoleLogger(LogLevel.All));

// Create a translation client
TranslationClient client = TranslationClient.Create();

// Configure which events the message handler will log.
client.Service.HttpClient.MessageHandler.LogEvents =
    LogEventType.RequestHeaders | LogEventType.ResponseBody;

// Make the request
client.ListLanguages();

To log all events from the message handler, you can set the LogEvents property to ~LogEventType.None.

Why aren't the gRPC native libraries being found?

The native libraries that gRPC relies on are present in Grpc.Core, and the NuGet package has targets to copy them to appropriate output directories. However, due to the way NuGet dependencies are generated with .NET Core, you may find that with transitive dependencies, the targets aren't executed.

We've set up our client libraries (e.g. Google.Cloud.Datastore.V1) so that if you directly depend on any of them, everything should work - but if your application only has transitive dependencies, you could run into errors like this:

Unhandled Exception: System.IO.FileNotFoundException:
  Error loading native library. Not found in any of the possible locations: [...]
   at Grpc.Core.Internal.UnmanagedLibrary.FirstValidLibraryPath(String[] libraryPathAlternatives)
   at Grpc.Core.Internal.UnmanagedLibrary..ctor(String[] libraryPathAlternatives)
   at Grpc.Core.Internal.NativeExtension.Load()
   at Grpc.Core.Internal.NativeExtension..ctor()
   at Grpc.Core.Internal.NativeExtension.Get()
   at Grpc.Core.GrpcEnvironment.GrpcNativeInit()
   at Grpc.Core.GrpcEnvironment..ctor()
   ...

In that case, the simplest fix is to add a direct dependency to Grpc.Core from your application, which will ensure that the native libraries are copied appropriately.

How can I modify repeated fields and maps in protobuf messages?

The generated C# code for protobuf messages makes simple properties read/write, but repeated fields and map fields are read-only. That doesn't stop you from populating them, though: it just means you can't change the property to refer to a different list or map.

Typically you'll populate this using a collection initializer nested within an object initializer. As an example, let's look at how we might create a BatchAnnotateImagesRequest message in the Vision API. (This is just an easy-to-understand example; the Google.Cloud.Vision.V1 package provides helper methods to avoid you having to create batches yourself in most cases.)

The protobuf description looks like this:

// Multiple image annotation requests are batched into a single service call.
message BatchAnnotateImagesRequest {
  // Individual image annotation requests for this batch.
  repeated AnnotateImageRequest requests = 1;
}

In the generated C# code, the Requests property of BatchAnnotateImagesRequest is read-only, but you can populate it with a collection initializer:

// In normal code you'd populate these individual requests with more
// information.
AnnotateImageRequest request1 = new AnnotateImageRequest();
AnnotateImageRequest request2 = new AnnotateImageRequest();

// Create the batch request using an object initializer
BatchAnnotateImagesRequest batch = new BatchAnnotateImagesRequest
{
    // Populate the repeated field with a collection initializer
    Requests = { request1, request2 }
};

You don't have to use a collection initializer though, and sometimes it would be inconvenient to do so. It's perfectly valid to add to the repeated field after other initialization:

// In normal code you'd populate these individual requests with more
// information.
AnnotateImageRequest request1 = new AnnotateImageRequest();
AnnotateImageRequest request2 = new AnnotateImageRequest();

// Populate the batch without using an object initializer, just by calling
// Add on the repeated field
BatchAnnotateImagesRequest batch = new BatchAnnotateImagesRequest();
batch.Requests.Add(request1);
batch.Requests.Add(request2);

Finally, it's worth being aware that RepeatedField<T> has an Add overload accepting an IEnumerable<T>. This allows you to use a collection initializer to copy items out of another collection, or a LINQ query result:

// In normal code you'd populate these individual requests with more
// information.
List<AnnotateImageRequest> requests = new List<AnnotateImageRequest>
{
    new AnnotateImageRequest(),
    new AnnotateImageRequest()
};

// Create the batch request using an object initializer
BatchAnnotateImagesRequest batch = new BatchAnnotateImagesRequest
{
    // Populate the repeated field using the Add overload that accepts
    // an IEnumerable<T>
    Requests = { requests }
};

Likewise for map fields (which are significantly less common) you can use collection initializers, or (from C# 6 onwards) the indexer syntax within an object initializer. As an example of this, let's consider the Scheduler V1 API, which contains a message like this:

message HttpTarget {
  // Other fields omitted

  // The user can specify HTTP request headers to send with the job's
  // HTTP request. (Further documentation omitted here.)
  map<string, string> headers = 3;
}

Again, the Headers property in the generated message is read-only, but you can populate it with a collection initializer:

HttpTarget target = new HttpTarget
{
    Headers =
    {
        {  "X-Custom-Header1", "Value1" },
        {  "X-Custom-Header2", "Value2" },
    }
};

Or an indexer in an object initializer:

HttpTarget target = new HttpTarget
{
    Headers =
    {
        ["X-Custom-Header1"] = "Value1",
        ["X-Custom-Header2"] = "Value2",
    }
};

Or modify it after other initialization steps:

HttpTarget target = new HttpTarget();
target.Headers["X-Custom-Header1"] = "Value1";
target.Headers["X-Custom-Header2"] = "Value2";

Why is System.EntryPointNotFoundException being thrown?

While there are various potential causes for this, the most likely cause is that you have a dependency on Grpc.Core 2.x, but you're still depending on Google Cloud libraries that depend on Grpc.Core 1.x.

We have now released client libraries (some of them still in beta, but most GA) which use GAX 3.x, which depends on Grpc.Core 2.x. If you update to the latest version of the client library, that should fix the issue.