Skip to content

Commit ffe1119

Browse files
committed
Add EventSource and activity metadata to server
1 parent 2279d9a commit ffe1119

24 files changed

Lines changed: 801 additions & 97 deletions

examples/Server/Startup.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,8 @@
2020
using System.IdentityModel.Tokens.Jwt;
2121
using System.Security.Claims;
2222
using System.Security.Cryptography.X509Certificates;
23-
using System.Threading.Tasks;
2423
using Count;
2524
using Greet;
26-
using Microsoft.AspNetCore.Authentication.Certificate;
2725
using Microsoft.AspNetCore.Authentication.JwtBearer;
2826
using Microsoft.AspNetCore.Builder;
2927
using Microsoft.AspNetCore.Http;
@@ -44,14 +42,14 @@ public void ConfigureServices(IServiceCollection services)
4442
{
4543
// This registers a global interceptor with a Singleton lifetime. The interceptor must be added to the service collection in addition to being registered here.
4644
options.Interceptors.Add<MaxConcurrentCallsInterceptor>();
47-
// This registers a global interceptor with a Scoped lifetime.
48-
options.Interceptors.Add<MaxStreamingRequestTimeoutInterceptor>(TimeSpan.FromSeconds(30));
4945
})
5046
.AddServiceOptions<GreeterService>(options =>
5147
{
5248
// This registers an interceptor for the Greeter service with a Singleton lifetime.
5349
// NOTE: Not all calls should be cached. Since the response of this service only depends on the request and no other state, adding caching here is acceptable.
5450
options.Interceptors.Add<UnaryCachingInterceptor>();
51+
// This registers an interceptor for the Greeter service with a Scoped lifetime.
52+
options.Interceptors.Add<MaxStreamingRequestTimeoutInterceptor>(TimeSpan.FromSeconds(30));
5553
});
5654
services.AddGrpcReflection();
5755
services.AddSingleton(new MaxConcurrentCallsInterceptor(200));

perf/Grpc.AspNetCore.Microbenchmarks/Internal/MessageHelpers.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#endregion
1818

19+
using System.Diagnostics;
1920
using System.IO;
2021
using System.IO.Pipelines;
2122
using Google.Protobuf;
@@ -28,7 +29,11 @@ namespace Grpc.AspNetCore.Microbenchmarks.Internal
2829
{
2930
internal static class MessageHelpers
3031
{
31-
private static readonly HttpContextServerCallContext TestServerCallContext = new HttpContextServerCallContext(new DefaultHttpContext(), new GrpcServiceOptions(), NullLogger.Instance);
32+
private static readonly HttpContextServerCallContext TestServerCallContext = new HttpContextServerCallContext(
33+
new DefaultHttpContext(),
34+
new GrpcServiceOptions(),
35+
NullLogger.Instance,
36+
new DiagnosticListener("Test"));
3237

3338
static MessageHelpers()
3439
{

perf/Grpc.AspNetCore.Microbenchmarks/UnaryServerCallHandlerBenchmarkBase.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#endregion
1818

1919
using System.Buffers;
20+
using System.Diagnostics;
2021
using System.IO;
2122
using System.IO.Pipelines;
2223
using System.Threading.Tasks;
@@ -58,7 +59,8 @@ public void GlobalSetup()
5859
method,
5960
(service, request, context) => result,
6061
ServiceOptions,
61-
NullLoggerFactory.Instance);
62+
NullLoggerFactory.Instance,
63+
new DiagnosticListener("Test"));
6264

6365
_trailers = new HeaderDictionary();
6466

src/Grpc.AspNetCore.Server/Internal/CallHandlers/ClientStreamingServerCallHandler.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#endregion
1818

1919
using System;
20+
using System.Diagnostics;
2021
using System.Threading.Tasks;
2122
using Grpc.AspNetCore.Server.Model;
2223
using Grpc.Core;
@@ -38,8 +39,9 @@ public ClientStreamingServerCallHandler(
3839
Method<TRequest, TResponse> method,
3940
ClientStreamingServerMethod<TService, TRequest, TResponse> invoker,
4041
GrpcServiceOptions serviceOptions,
41-
ILoggerFactory loggerFactory)
42-
: base(method, serviceOptions, loggerFactory)
42+
ILoggerFactory loggerFactory,
43+
DiagnosticListener diagnosticListener)
44+
: base(method, serviceOptions, loggerFactory, diagnosticListener)
4345
{
4446
_invoker = invoker;
4547

@@ -113,6 +115,8 @@ protected override async Task HandleCallAsyncCore(HttpContext httpContext, HttpC
113115

114116
var responseBodyWriter = httpContext.Response.BodyWriter;
115117
await responseBodyWriter.WriteMessageAsync(response, serverCallContext, Method.ResponseMarshaller.ContextualSerializer, canFlush: false);
118+
119+
GrpcEventSource.Log.MessageSent();
116120
}
117121
}
118122
}

src/Grpc.AspNetCore.Server/Internal/CallHandlers/DuplexStreamingServerCallHandler.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#endregion
1818

1919
using System;
20+
using System.Diagnostics;
2021
using System.Threading.Tasks;
2122
using Grpc.AspNetCore.Server.Model;
2223
using Grpc.Core;
@@ -38,8 +39,9 @@ public DuplexStreamingServerCallHandler(
3839
Method<TRequest, TResponse> method,
3940
DuplexStreamingServerMethod<TService, TRequest, TResponse> invoker,
4041
GrpcServiceOptions serviceOptions,
41-
ILoggerFactory loggerFactory)
42-
: base(method, serviceOptions, loggerFactory)
42+
ILoggerFactory loggerFactory,
43+
DiagnosticListener diagnosticListener)
44+
: base(method, serviceOptions, loggerFactory, diagnosticListener)
4345
{
4446
_invoker = invoker;
4547

src/Grpc.AspNetCore.Server/Internal/CallHandlers/ServerCallHandlerBase.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#endregion
1818

1919
using System;
20+
using System.Diagnostics;
2021
using System.Threading.Tasks;
2122
using Grpc.AspNetCore.Server.Features;
2223
using Grpc.Core;
@@ -26,16 +27,22 @@
2627

2728
namespace Grpc.AspNetCore.Server.Internal.CallHandlers
2829
{
29-
internal abstract class ServerCallHandlerBase<TService, TRequest, TResponse> : IServerCallHandler
30+
internal abstract class ServerCallHandlerBase<TService, TRequest, TResponse>
3031
{
3132
protected Method<TRequest, TResponse> Method { get; }
3233
protected GrpcServiceOptions ServiceOptions { get; }
34+
protected DiagnosticListener DiagnosticListener { get; }
3335
protected ILogger Logger { get; }
3436

35-
protected ServerCallHandlerBase(Method<TRequest, TResponse> method, GrpcServiceOptions serviceOptions, ILoggerFactory loggerFactory)
37+
protected ServerCallHandlerBase(
38+
Method<TRequest, TResponse> method,
39+
GrpcServiceOptions serviceOptions,
40+
ILoggerFactory loggerFactory,
41+
DiagnosticListener diagnosticListener)
3642
{
3743
Method = method;
3844
ServiceOptions = serviceOptions;
45+
DiagnosticListener = diagnosticListener;
3946
Logger = loggerFactory.CreateLogger(typeof(TService));
4047
}
4148

@@ -47,7 +54,7 @@ public Task HandleCallAsync(HttpContext httpContext)
4754
return Task.CompletedTask;
4855
}
4956

50-
var serverCallContext = new HttpContextServerCallContext(httpContext, ServiceOptions, Logger);
57+
var serverCallContext = new HttpContextServerCallContext(httpContext, ServiceOptions, Logger, DiagnosticListener);
5158
httpContext.Features.Set<IServerCallContextFeature>(serverCallContext);
5259

5360
GrpcProtocolHelpers.AddProtocolHeaders(httpContext.Response);

src/Grpc.AspNetCore.Server/Internal/CallHandlers/ServerStreamingServerCallHandler.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#endregion
1818

1919
using System;
20+
using System.Diagnostics;
2021
using System.Threading.Tasks;
2122
using Grpc.AspNetCore.Server.Model;
2223
using Grpc.Core;
@@ -38,8 +39,9 @@ public ServerStreamingServerCallHandler(
3839
Method<TRequest, TResponse> method,
3940
ServerStreamingServerMethod<TService, TRequest, TResponse> invoker,
4041
GrpcServiceOptions serviceOptions,
41-
ILoggerFactory loggerFactory)
42-
: base(method, serviceOptions, loggerFactory)
42+
ILoggerFactory loggerFactory,
43+
DiagnosticListener diagnosticListener)
44+
: base(method, serviceOptions, loggerFactory, diagnosticListener)
4345
{
4446
_invoker = invoker;
4547

@@ -77,6 +79,8 @@ protected override async Task HandleCallAsyncCore(HttpContext httpContext, HttpC
7779
var request = Method.RequestMarshaller.ContextualDeserializer(serverCallContext.DeserializationContext);
7880
serverCallContext.DeserializationContext.SetPayload(null);
7981

82+
GrpcEventSource.Log.MessageReceived();
83+
8084
if (_pipelineInvoker == null)
8185
{
8286
var activator = httpContext.RequestServices.GetRequiredService<IGrpcServiceActivator<TService>>();

src/Grpc.AspNetCore.Server/Internal/CallHandlers/UnaryServerCallHandler.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#endregion
1818

1919
using System;
20+
using System.Diagnostics;
2021
using System.Threading.Tasks;
2122
using Grpc.AspNetCore.Server.Model;
2223
using Grpc.Core;
@@ -38,8 +39,9 @@ public UnaryServerCallHandler(
3839
Method<TRequest, TResponse> method,
3940
UnaryServerMethod<TService, TRequest, TResponse> invoker,
4041
GrpcServiceOptions serviceOptions,
41-
ILoggerFactory loggerFactory)
42-
: base(method, serviceOptions, loggerFactory)
42+
ILoggerFactory loggerFactory,
43+
DiagnosticListener diagnosticListener)
44+
: base(method, serviceOptions, loggerFactory, diagnosticListener)
4345
{
4446
_invoker = invoker;
4547

@@ -76,6 +78,8 @@ protected override async Task HandleCallAsyncCore(HttpContext httpContext, HttpC
7678
var request = Method.RequestMarshaller.ContextualDeserializer(serverCallContext.DeserializationContext);
7779
serverCallContext.DeserializationContext.SetPayload(null);
7880

81+
GrpcEventSource.Log.MessageReceived();
82+
7983
TResponse? response = null;
8084

8185
if (_pipelineInvoker == null)
@@ -108,6 +112,8 @@ protected override async Task HandleCallAsyncCore(HttpContext httpContext, HttpC
108112

109113
var responseBodyWriter = httpContext.Response.BodyWriter;
110114
await responseBodyWriter.WriteMessageAsync(response, serverCallContext, Method.ResponseMarshaller.ContextualSerializer, canFlush: false);
115+
116+
GrpcEventSource.Log.MessageSent();
111117
}
112118
}
113119
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
#region Copyright notice and license
2+
3+
// Copyright 2019 The gRPC Authors
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
#endregion
18+
19+
using System.Diagnostics.Tracing;
20+
using System.Runtime.CompilerServices;
21+
using System.Threading;
22+
using Grpc.Core;
23+
24+
namespace Grpc.AspNetCore.Server.Internal
25+
{
26+
internal class GrpcEventSource : EventSource
27+
{
28+
public static readonly GrpcEventSource Log = new GrpcEventSource();
29+
30+
private PollingCounter? _totalCallsCounter;
31+
private PollingCounter? _currentCallsCounter;
32+
private PollingCounter? _messagesSentCounter;
33+
private PollingCounter? _messagesReceivedCounter;
34+
private PollingCounter? _callsFailedCounter;
35+
private PollingCounter? _callsDeadlineExceededCounter;
36+
private PollingCounter? _callsUnimplementedCounter;
37+
38+
private long _totalCalls;
39+
private long _currentCalls;
40+
private long _messageSent;
41+
private long _messageReceived;
42+
private long _callsFailed;
43+
private long _callsDeadlineExceeded;
44+
private long _callsUnimplemented;
45+
46+
internal GrpcEventSource()
47+
: base("Grpc.AspNetCore.Server")
48+
{
49+
}
50+
51+
// Used for testing
52+
internal GrpcEventSource(string eventSourceName)
53+
: base(eventSourceName)
54+
{
55+
}
56+
57+
[MethodImpl(MethodImplOptions.NoInlining)]
58+
[Event(eventId: 1, Level = EventLevel.Verbose)]
59+
public void CallStart(string method)
60+
{
61+
Interlocked.Increment(ref _totalCalls);
62+
Interlocked.Increment(ref _currentCalls);
63+
64+
WriteEvent(1, method);
65+
}
66+
67+
[MethodImpl(MethodImplOptions.NoInlining)]
68+
[Event(eventId: 2, Level = EventLevel.Verbose)]
69+
public void CallStop()
70+
{
71+
Interlocked.Decrement(ref _currentCalls);
72+
73+
WriteEvent(2);
74+
}
75+
76+
[MethodImpl(MethodImplOptions.NoInlining)]
77+
[Event(eventId: 3, Level = EventLevel.Error)]
78+
public void CallFailed(StatusCode statusCode)
79+
{
80+
Interlocked.Increment(ref _callsFailed);
81+
82+
WriteEvent(3, (int)statusCode);
83+
}
84+
85+
[MethodImpl(MethodImplOptions.NoInlining)]
86+
[Event(eventId: 4, Level = EventLevel.Error)]
87+
public void CallDeadlineExceeded()
88+
{
89+
Interlocked.Increment(ref _callsDeadlineExceeded);
90+
91+
WriteEvent(4);
92+
}
93+
94+
[MethodImpl(MethodImplOptions.NoInlining)]
95+
[Event(eventId: 5, Level = EventLevel.Verbose)]
96+
public void MessageSent()
97+
{
98+
Interlocked.Increment(ref _messageSent);
99+
100+
WriteEvent(5);
101+
}
102+
103+
[MethodImpl(MethodImplOptions.NoInlining)]
104+
[Event(eventId: 6, Level = EventLevel.Verbose)]
105+
public void MessageReceived()
106+
{
107+
Interlocked.Increment(ref _messageReceived);
108+
109+
WriteEvent(6);
110+
}
111+
112+
[MethodImpl(MethodImplOptions.NoInlining)]
113+
[Event(eventId: 7, Level = EventLevel.Verbose)]
114+
public void CallUnimplemented(string method)
115+
{
116+
Interlocked.Increment(ref _callsUnimplemented);
117+
118+
WriteEvent(7, method);
119+
}
120+
121+
protected override void OnEventCommand(EventCommandEventArgs command)
122+
{
123+
if (command.Command == EventCommand.Enable)
124+
{
125+
// This is the convention for initializing counters in the RuntimeEventSource (lazily on the first enable command).
126+
// They aren't disabled afterwards...
127+
128+
_totalCallsCounter ??= new PollingCounter("total-calls", this, () => _totalCalls)
129+
{
130+
DisplayName = "Total Calls",
131+
};
132+
_currentCallsCounter ??= new PollingCounter("current-calls", this, () => _currentCalls)
133+
{
134+
DisplayName = "Current Calls"
135+
};
136+
_callsFailedCounter ??= new PollingCounter("calls-failed", this, () => _callsFailed)
137+
{
138+
DisplayName = "Total Calls Failed",
139+
};
140+
_callsDeadlineExceededCounter ??= new PollingCounter("calls-deadline-exceeded", this, () => _callsDeadlineExceeded)
141+
{
142+
DisplayName = "Total Calls Deadline Exceeded",
143+
};
144+
_messagesSentCounter ??= new PollingCounter("messages-sent", this, () => _messageSent)
145+
{
146+
DisplayName = "Total Messages Sent",
147+
};
148+
_messagesReceivedCounter ??= new PollingCounter("messages-received", this, () => _messageReceived)
149+
{
150+
DisplayName = "Total Messages Received",
151+
};
152+
_callsUnimplementedCounter ??= new PollingCounter("calls-unimplemented", this, () => _callsUnimplemented)
153+
{
154+
DisplayName = "Total Calls Unimplemented",
155+
};
156+
}
157+
}
158+
}
159+
}

0 commit comments

Comments
 (0)