Skip to content

Commit

Permalink
Add EventSource and activity metadata to server
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesNK committed Jul 15, 2019
1 parent 2279d9a commit ffe1119
Show file tree
Hide file tree
Showing 24 changed files with 801 additions and 97 deletions.
6 changes: 2 additions & 4 deletions examples/Server/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Count;
using Greet;
using Microsoft.AspNetCore.Authentication.Certificate;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
Expand All @@ -44,14 +42,14 @@ public void ConfigureServices(IServiceCollection services)
{
// This registers a global interceptor with a Singleton lifetime. The interceptor must be added to the service collection in addition to being registered here.
options.Interceptors.Add<MaxConcurrentCallsInterceptor>();
// This registers a global interceptor with a Scoped lifetime.
options.Interceptors.Add<MaxStreamingRequestTimeoutInterceptor>(TimeSpan.FromSeconds(30));
})
.AddServiceOptions<GreeterService>(options =>
{
// This registers an interceptor for the Greeter service with a Singleton lifetime.
// 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.
options.Interceptors.Add<UnaryCachingInterceptor>();
// This registers an interceptor for the Greeter service with a Scoped lifetime.
options.Interceptors.Add<MaxStreamingRequestTimeoutInterceptor>(TimeSpan.FromSeconds(30));
});
services.AddGrpcReflection();
services.AddSingleton(new MaxConcurrentCallsInterceptor(200));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#endregion

using System.Diagnostics;
using System.IO;
using System.IO.Pipelines;
using Google.Protobuf;
Expand All @@ -28,7 +29,11 @@ namespace Grpc.AspNetCore.Microbenchmarks.Internal
{
internal static class MessageHelpers
{
private static readonly HttpContextServerCallContext TestServerCallContext = new HttpContextServerCallContext(new DefaultHttpContext(), new GrpcServiceOptions(), NullLogger.Instance);
private static readonly HttpContextServerCallContext TestServerCallContext = new HttpContextServerCallContext(
new DefaultHttpContext(),
new GrpcServiceOptions(),
NullLogger.Instance,
new DiagnosticListener("Test"));

static MessageHelpers()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#endregion

using System.Buffers;
using System.Diagnostics;
using System.IO;
using System.IO.Pipelines;
using System.Threading.Tasks;
Expand Down Expand Up @@ -58,7 +59,8 @@ public void GlobalSetup()
method,
(service, request, context) => result,
ServiceOptions,
NullLoggerFactory.Instance);
NullLoggerFactory.Instance,
new DiagnosticListener("Test"));

_trailers = new HeaderDictionary();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#endregion

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Grpc.AspNetCore.Server.Model;
using Grpc.Core;
Expand All @@ -38,8 +39,9 @@ public ClientStreamingServerCallHandler(
Method<TRequest, TResponse> method,
ClientStreamingServerMethod<TService, TRequest, TResponse> invoker,
GrpcServiceOptions serviceOptions,
ILoggerFactory loggerFactory)
: base(method, serviceOptions, loggerFactory)
ILoggerFactory loggerFactory,
DiagnosticListener diagnosticListener)
: base(method, serviceOptions, loggerFactory, diagnosticListener)
{
_invoker = invoker;

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

var responseBodyWriter = httpContext.Response.BodyWriter;
await responseBodyWriter.WriteMessageAsync(response, serverCallContext, Method.ResponseMarshaller.ContextualSerializer, canFlush: false);

GrpcEventSource.Log.MessageSent();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#endregion

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Grpc.AspNetCore.Server.Model;
using Grpc.Core;
Expand All @@ -38,8 +39,9 @@ public DuplexStreamingServerCallHandler(
Method<TRequest, TResponse> method,
DuplexStreamingServerMethod<TService, TRequest, TResponse> invoker,
GrpcServiceOptions serviceOptions,
ILoggerFactory loggerFactory)
: base(method, serviceOptions, loggerFactory)
ILoggerFactory loggerFactory,
DiagnosticListener diagnosticListener)
: base(method, serviceOptions, loggerFactory, diagnosticListener)
{
_invoker = invoker;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#endregion

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Grpc.AspNetCore.Server.Features;
using Grpc.Core;
Expand All @@ -26,16 +27,22 @@

namespace Grpc.AspNetCore.Server.Internal.CallHandlers
{
internal abstract class ServerCallHandlerBase<TService, TRequest, TResponse> : IServerCallHandler
internal abstract class ServerCallHandlerBase<TService, TRequest, TResponse>
{
protected Method<TRequest, TResponse> Method { get; }
protected GrpcServiceOptions ServiceOptions { get; }
protected DiagnosticListener DiagnosticListener { get; }
protected ILogger Logger { get; }

protected ServerCallHandlerBase(Method<TRequest, TResponse> method, GrpcServiceOptions serviceOptions, ILoggerFactory loggerFactory)
protected ServerCallHandlerBase(
Method<TRequest, TResponse> method,
GrpcServiceOptions serviceOptions,
ILoggerFactory loggerFactory,
DiagnosticListener diagnosticListener)
{
Method = method;
ServiceOptions = serviceOptions;
DiagnosticListener = diagnosticListener;
Logger = loggerFactory.CreateLogger(typeof(TService));
}

Expand All @@ -47,7 +54,7 @@ public Task HandleCallAsync(HttpContext httpContext)
return Task.CompletedTask;
}

var serverCallContext = new HttpContextServerCallContext(httpContext, ServiceOptions, Logger);
var serverCallContext = new HttpContextServerCallContext(httpContext, ServiceOptions, Logger, DiagnosticListener);
httpContext.Features.Set<IServerCallContextFeature>(serverCallContext);

GrpcProtocolHelpers.AddProtocolHeaders(httpContext.Response);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#endregion

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Grpc.AspNetCore.Server.Model;
using Grpc.Core;
Expand All @@ -38,8 +39,9 @@ public ServerStreamingServerCallHandler(
Method<TRequest, TResponse> method,
ServerStreamingServerMethod<TService, TRequest, TResponse> invoker,
GrpcServiceOptions serviceOptions,
ILoggerFactory loggerFactory)
: base(method, serviceOptions, loggerFactory)
ILoggerFactory loggerFactory,
DiagnosticListener diagnosticListener)
: base(method, serviceOptions, loggerFactory, diagnosticListener)
{
_invoker = invoker;

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

GrpcEventSource.Log.MessageReceived();

if (_pipelineInvoker == null)
{
var activator = httpContext.RequestServices.GetRequiredService<IGrpcServiceActivator<TService>>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#endregion

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Grpc.AspNetCore.Server.Model;
using Grpc.Core;
Expand All @@ -38,8 +39,9 @@ public UnaryServerCallHandler(
Method<TRequest, TResponse> method,
UnaryServerMethod<TService, TRequest, TResponse> invoker,
GrpcServiceOptions serviceOptions,
ILoggerFactory loggerFactory)
: base(method, serviceOptions, loggerFactory)
ILoggerFactory loggerFactory,
DiagnosticListener diagnosticListener)
: base(method, serviceOptions, loggerFactory, diagnosticListener)
{
_invoker = invoker;

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

GrpcEventSource.Log.MessageReceived();

TResponse? response = null;

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

var responseBodyWriter = httpContext.Response.BodyWriter;
await responseBodyWriter.WriteMessageAsync(response, serverCallContext, Method.ResponseMarshaller.ContextualSerializer, canFlush: false);

GrpcEventSource.Log.MessageSent();
}
}
}
159 changes: 159 additions & 0 deletions src/Grpc.AspNetCore.Server/Internal/GrpcEventSource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#region Copyright notice and license

// Copyright 2019 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#endregion

using System.Diagnostics.Tracing;
using System.Runtime.CompilerServices;
using System.Threading;
using Grpc.Core;

namespace Grpc.AspNetCore.Server.Internal
{
internal class GrpcEventSource : EventSource
{
public static readonly GrpcEventSource Log = new GrpcEventSource();

private PollingCounter? _totalCallsCounter;
private PollingCounter? _currentCallsCounter;
private PollingCounter? _messagesSentCounter;
private PollingCounter? _messagesReceivedCounter;
private PollingCounter? _callsFailedCounter;
private PollingCounter? _callsDeadlineExceededCounter;
private PollingCounter? _callsUnimplementedCounter;

private long _totalCalls;
private long _currentCalls;
private long _messageSent;
private long _messageReceived;
private long _callsFailed;
private long _callsDeadlineExceeded;
private long _callsUnimplemented;

internal GrpcEventSource()
: base("Grpc.AspNetCore.Server")
{
}

// Used for testing
internal GrpcEventSource(string eventSourceName)
: base(eventSourceName)
{
}

[MethodImpl(MethodImplOptions.NoInlining)]
[Event(eventId: 1, Level = EventLevel.Verbose)]
public void CallStart(string method)
{
Interlocked.Increment(ref _totalCalls);
Interlocked.Increment(ref _currentCalls);

WriteEvent(1, method);
}

[MethodImpl(MethodImplOptions.NoInlining)]
[Event(eventId: 2, Level = EventLevel.Verbose)]
public void CallStop()
{
Interlocked.Decrement(ref _currentCalls);

WriteEvent(2);
}

[MethodImpl(MethodImplOptions.NoInlining)]
[Event(eventId: 3, Level = EventLevel.Error)]
public void CallFailed(StatusCode statusCode)
{
Interlocked.Increment(ref _callsFailed);

WriteEvent(3, (int)statusCode);
}

[MethodImpl(MethodImplOptions.NoInlining)]
[Event(eventId: 4, Level = EventLevel.Error)]
public void CallDeadlineExceeded()
{
Interlocked.Increment(ref _callsDeadlineExceeded);

WriteEvent(4);
}

[MethodImpl(MethodImplOptions.NoInlining)]
[Event(eventId: 5, Level = EventLevel.Verbose)]
public void MessageSent()
{
Interlocked.Increment(ref _messageSent);

WriteEvent(5);
}

[MethodImpl(MethodImplOptions.NoInlining)]
[Event(eventId: 6, Level = EventLevel.Verbose)]
public void MessageReceived()
{
Interlocked.Increment(ref _messageReceived);

WriteEvent(6);
}

[MethodImpl(MethodImplOptions.NoInlining)]
[Event(eventId: 7, Level = EventLevel.Verbose)]
public void CallUnimplemented(string method)
{
Interlocked.Increment(ref _callsUnimplemented);

WriteEvent(7, method);
}

protected override void OnEventCommand(EventCommandEventArgs command)
{
if (command.Command == EventCommand.Enable)
{
// This is the convention for initializing counters in the RuntimeEventSource (lazily on the first enable command).
// They aren't disabled afterwards...

_totalCallsCounter ??= new PollingCounter("total-calls", this, () => _totalCalls)
{
DisplayName = "Total Calls",
};
_currentCallsCounter ??= new PollingCounter("current-calls", this, () => _currentCalls)
{
DisplayName = "Current Calls"
};
_callsFailedCounter ??= new PollingCounter("calls-failed", this, () => _callsFailed)
{
DisplayName = "Total Calls Failed",
};
_callsDeadlineExceededCounter ??= new PollingCounter("calls-deadline-exceeded", this, () => _callsDeadlineExceeded)
{
DisplayName = "Total Calls Deadline Exceeded",
};
_messagesSentCounter ??= new PollingCounter("messages-sent", this, () => _messageSent)
{
DisplayName = "Total Messages Sent",
};
_messagesReceivedCounter ??= new PollingCounter("messages-received", this, () => _messageReceived)
{
DisplayName = "Total Messages Received",
};
_callsUnimplementedCounter ??= new PollingCounter("calls-unimplemented", this, () => _callsUnimplemented)
{
DisplayName = "Total Calls Unimplemented",
};
}
}
}
}
Loading

0 comments on commit ffe1119

Please sign in to comment.