1- #region Copyright notice and license
1+ #region Copyright notice and license
22
33// Copyright 2019 The gRPC Authors
44//
2020using System . Net . Http . Headers ;
2121using Greet ;
2222using Grpc . Core ;
23+ using Grpc . Core . Interceptors ;
2324using Grpc . Net . Client . Internal ;
2425using Grpc . Net . Client . Tests . Infrastructure ;
2526using Grpc . Shared ;
@@ -239,11 +240,12 @@ public enum ResponseHandleAction
239240 }
240241
241242 [ Test ]
242- [ TestCase ( 0 , ResponseHandleAction . ResponseAsync ) ]
243- [ TestCase ( 0 , ResponseHandleAction . ResponseHeadersAsync ) ]
244- [ TestCase ( 0 , ResponseHandleAction . Dispose ) ]
245- [ TestCase ( 1 , ResponseHandleAction . Nothing ) ]
246- public async Task AsyncUnaryCall_CallFailed_NoUnobservedExceptions ( int expectedUnobservedExceptions , ResponseHandleAction action )
243+ [ TestCase ( 0 , false , ResponseHandleAction . ResponseAsync ) ]
244+ [ TestCase ( 0 , true , ResponseHandleAction . ResponseAsync ) ]
245+ [ TestCase ( 0 , false , ResponseHandleAction . ResponseHeadersAsync ) ]
246+ [ TestCase ( 0 , false , ResponseHandleAction . Dispose ) ]
247+ [ TestCase ( 1 , false , ResponseHandleAction . Nothing ) ]
248+ public async Task AsyncUnaryCall_CallFailed_NoUnobservedExceptions ( int expectedUnobservedExceptions , bool addClientInterceptor , ResponseHandleAction action )
247249 {
248250 // Arrange
249251 var services = new ServiceCollection ( ) ;
@@ -267,7 +269,11 @@ public async Task AsyncUnaryCall_CallFailed_NoUnobservedExceptions(int expectedU
267269 {
268270 throw new Exception ( "Test error" ) ;
269271 } ) ;
270- var invoker = HttpClientCallInvokerFactory . Create ( httpClient , loggerFactory : loggerFactory ) ;
272+ CallInvoker invoker = HttpClientCallInvokerFactory . Create ( httpClient , loggerFactory : loggerFactory ) ;
273+ if ( addClientInterceptor )
274+ {
275+ invoker = invoker . Intercept ( new ClientLoggerInterceptor ( loggerFactory ) ) ;
276+ }
271277
272278 // Act
273279 logger . LogDebug ( "Starting call" ) ;
@@ -282,7 +288,7 @@ public async Task AsyncUnaryCall_CallFailed_NoUnobservedExceptions(int expectedU
282288 // Assert
283289 Assert . AreEqual ( expectedUnobservedExceptions , unobservedExceptions . Count ) ;
284290
285- static async Task MakeGrpcCallAsync ( ILogger logger , HttpClientCallInvoker invoker , ResponseHandleAction action )
291+ static async Task MakeGrpcCallAsync ( ILogger logger , CallInvoker invoker , ResponseHandleAction action )
286292 {
287293 var runTask = Task . Run ( async ( ) =>
288294 {
@@ -313,4 +319,157 @@ static async Task MakeGrpcCallAsync(ILogger logger, HttpClientCallInvoker invoke
313319 TaskScheduler . UnobservedTaskException -= onUnobservedTaskException ;
314320 }
315321 }
322+
323+ private class ClientLoggerInterceptor : Interceptor
324+ {
325+ private readonly ILogger < ClientLoggerInterceptor > _logger ;
326+
327+ public ClientLoggerInterceptor ( ILoggerFactory loggerFactory )
328+ {
329+ _logger = loggerFactory . CreateLogger < ClientLoggerInterceptor > ( ) ;
330+ }
331+
332+ public override TResponse BlockingUnaryCall < TRequest , TResponse > (
333+ TRequest request ,
334+ ClientInterceptorContext < TRequest , TResponse > context ,
335+ BlockingUnaryCallContinuation < TRequest , TResponse > continuation )
336+ {
337+ LogCall ( context . Method ) ;
338+ AddCallerMetadata ( ref context ) ;
339+
340+ try
341+ {
342+ return continuation ( request , context ) ;
343+ }
344+ catch ( Exception ex )
345+ {
346+ LogError ( ex ) ;
347+ throw ;
348+ }
349+ }
350+
351+ public override AsyncUnaryCall < TResponse > AsyncUnaryCall < TRequest , TResponse > (
352+ TRequest request ,
353+ ClientInterceptorContext < TRequest , TResponse > context ,
354+ AsyncUnaryCallContinuation < TRequest , TResponse > continuation )
355+ {
356+ LogCall ( context . Method ) ;
357+ AddCallerMetadata ( ref context ) ;
358+
359+ try
360+ {
361+ var call = continuation ( request , context ) ;
362+
363+ return new AsyncUnaryCall < TResponse > ( HandleResponse ( call . ResponseAsync ) , call . ResponseHeadersAsync , call . GetStatus , call . GetTrailers , call . Dispose ) ;
364+ }
365+ catch ( Exception ex )
366+ {
367+ LogError ( ex ) ;
368+ throw ;
369+ }
370+ }
371+
372+ private async Task < TResponse > HandleResponse < TResponse > ( Task < TResponse > t )
373+ {
374+ try
375+ {
376+ var response = await t ;
377+ _logger . LogInformation ( $ "Response received: { response } ") ;
378+ return response ;
379+ }
380+ catch ( Exception ex )
381+ {
382+ LogError ( ex ) ;
383+ throw ;
384+ }
385+ }
386+
387+ public override AsyncClientStreamingCall < TRequest , TResponse > AsyncClientStreamingCall < TRequest , TResponse > (
388+ ClientInterceptorContext < TRequest , TResponse > context ,
389+ AsyncClientStreamingCallContinuation < TRequest , TResponse > continuation )
390+ {
391+ LogCall ( context . Method ) ;
392+ AddCallerMetadata ( ref context ) ;
393+
394+ try
395+ {
396+ return continuation ( context ) ;
397+ }
398+ catch ( Exception ex )
399+ {
400+ LogError ( ex ) ;
401+ throw ;
402+ }
403+ }
404+
405+ public override AsyncServerStreamingCall < TResponse > AsyncServerStreamingCall < TRequest , TResponse > (
406+ TRequest request ,
407+ ClientInterceptorContext < TRequest , TResponse > context ,
408+ AsyncServerStreamingCallContinuation < TRequest , TResponse > continuation )
409+ {
410+ LogCall ( context . Method ) ;
411+ AddCallerMetadata ( ref context ) ;
412+
413+ try
414+ {
415+ return continuation ( request , context ) ;
416+ }
417+ catch ( Exception ex )
418+ {
419+ LogError ( ex ) ;
420+ throw ;
421+ }
422+ }
423+
424+ public override AsyncDuplexStreamingCall < TRequest , TResponse > AsyncDuplexStreamingCall < TRequest , TResponse > (
425+ ClientInterceptorContext < TRequest , TResponse > context ,
426+ AsyncDuplexStreamingCallContinuation < TRequest , TResponse > continuation )
427+ {
428+ LogCall ( context . Method ) ;
429+ AddCallerMetadata ( ref context ) ;
430+
431+ try
432+ {
433+ return continuation ( context ) ;
434+ }
435+ catch ( Exception ex )
436+ {
437+ LogError ( ex ) ;
438+ throw ;
439+ }
440+ }
441+
442+ private void LogCall < TRequest , TResponse > ( Method < TRequest , TResponse > method )
443+ where TRequest : class
444+ where TResponse : class
445+ {
446+ _logger . LogInformation ( $ "Starting call. Name: { method . Name } . Type: { method . Type } . Request: { typeof ( TRequest ) } . Response: { typeof ( TResponse ) } ") ;
447+ }
448+
449+ private void AddCallerMetadata < TRequest , TResponse > ( ref ClientInterceptorContext < TRequest , TResponse > context )
450+ where TRequest : class
451+ where TResponse : class
452+ {
453+ var headers = context . Options . Headers ;
454+
455+ // Call doesn't have a headers collection to add to.
456+ // Need to create a new context with headers for the call.
457+ if ( headers == null )
458+ {
459+ headers = new Metadata ( ) ;
460+ var options = context . Options . WithHeaders ( headers ) ;
461+ context = new ClientInterceptorContext < TRequest , TResponse > ( context . Method , context . Host , options ) ;
462+ }
463+
464+ // Add caller metadata to call headers
465+ headers . Add ( "caller-user" , Environment . UserName ) ;
466+ headers . Add ( "caller-machine" , Environment . MachineName ) ;
467+ headers . Add ( "caller-os" , Environment . OSVersion . ToString ( ) ) ;
468+ }
469+
470+ private void LogError ( Exception ex )
471+ {
472+ _logger . LogError ( ex , $ "Call error: { ex . Message } ") ;
473+ }
474+ }
316475}
0 commit comments