/// <summary> /// Initializes a new instance of the <see cref="ClientTracingInterceptor"/> class. /// </summary> /// <param name="options">The options.</param> public ClientTracingInterceptor(ClientTracingInterceptorOptions options) { this.options = options ?? throw new ArgumentNullException(nameof(options)); }
/// <summary> /// Initializes a new instance of the <see cref="ClientRpcScope{TRequest, TResponse}" /> class. /// </summary> /// <param name="context">The context.</param> /// <param name="options">The options.</param> public ClientRpcScope(ClientInterceptorContext <TRequest, TResponse> context, ClientTracingInterceptorOptions options) : base(context.Method?.FullName, options.RecordMessageEvents) { this.context = context; // Capture the current activity. this.parentActivity = Activity.Current; // Short-circuit if nobody is listening if (!GrpcCoreInstrumentation.ActivitySource.HasListeners()) { return; } // This if block is for unit testing only. IEnumerable <KeyValuePair <string, object> > customTags = null; if (options.ActivityIdentifierValue != default) { customTags = new List <KeyValuePair <string, object> > { new KeyValuePair <string, object>(SemanticConventions.AttributeActivityIdentifier, options.ActivityIdentifierValue), }; } // We want to start an activity but don't activate it. // After calling StartActivity, Activity.Current will be the new Activity. // This scope is created synchronously before the RPC invocation starts and so this new Activity will overwrite // the callers current Activity which isn't what we want. We need to restore the original immediately after doing this. // If this call happened after some kind of async context await then a restore wouldn't be necessary. // gRPC Core just doesn't have the hooks to do this as far as I can tell. var rpcActivity = GrpcCoreInstrumentation.ActivitySource.StartActivity( this.FullServiceName, ActivityKind.Client, this.parentActivity == default ? default : this.parentActivity.Context, tags: customTags); if (rpcActivity == null) { return; } var callOptions = context.Options; // Do NOT mutate incoming call headers, make a new copy. // Retry mechanisms that may sit above this interceptor rely on an original set of call headers. var metadata = new Metadata(); if (callOptions.Headers != null) { for (var i = 0; i < callOptions.Headers.Count; i++) { metadata.Add(callOptions.Headers[i]); } } // replace the CallOptions callOptions = callOptions.WithHeaders(metadata); this.SetActivity(rpcActivity); options.Propagator.Inject(new PropagationContext(rpcActivity.Context, Baggage.Current), callOptions.Headers, MetadataSetter); this.context = new ClientInterceptorContext <TRequest, TResponse>(context.Method, context.Host, callOptions); }