Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open Telemetry Support #4603

Merged
merged 30 commits into from
Jan 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
fe656fb
Started work on open telemetry
michaelstaib Jan 2, 2022
6fcaa82
Added more infos
michaelstaib Jan 2, 2022
f197ef1
integrated more events
michaelstaib Jan 2, 2022
23e82d2
Fixed compile issue
michaelstaib Jan 2, 2022
93e0e9e
cleanup
michaelstaib Jan 2, 2022
e00486b
started work on the setup code
michaelstaib Jan 2, 2022
530080a
added more instrumentations
michaelstaib Jan 3, 2022
defbcf3
Server Diagnostic
michaelstaib Jan 3, 2022
260fcef
Added DI integration
michaelstaib Jan 3, 2022
da362f1
restructured projects
michaelstaib Jan 3, 2022
cdfef9a
Format
michaelstaib Jan 3, 2022
038d917
Included Diagnostics to build
michaelstaib Jan 3, 2022
5752ec2
Added stubs
michaelstaib Jan 3, 2022
f774588
Changed TargetFrameworks
michaelstaib Jan 3, 2022
5666424
Added basic tests
michaelstaib Jan 4, 2022
3d3b199
refinements
michaelstaib Jan 4, 2022
878c504
refinements
michaelstaib Jan 4, 2022
ebb3509
cleanup
michaelstaib Jan 4, 2022
2320ce4
Added more data
michaelstaib Jan 4, 2022
63b9a68
Added more tests
michaelstaib Jan 4, 2022
9c6cb0e
Added more server tests
michaelstaib Jan 4, 2022
ab2c5a5
Fixed issues
michaelstaib Jan 4, 2022
1374fe6
Fixed more status issues
michaelstaib Jan 4, 2022
060d7b8
Reworked Diagnostic Events
michaelstaib Jan 4, 2022
c7ed98a
Cleanup
michaelstaib Jan 4, 2022
bde957f
cleanup
michaelstaib Jan 5, 2022
cf95ddc
Added more docs
michaelstaib Jan 5, 2022
7f8e26c
Fixed more issues
michaelstaib Jan 5, 2022
95c7118
cleanup
michaelstaib Jan 5, 2022
0e9f45f
Fixed more
michaelstaib Jan 5, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .build/Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class Helpers
Path.Combine("HotChocolate", "AzureFunctions"),
Path.Combine("HotChocolate", "Core"),
Path.Combine("HotChocolate", "CodeGeneration"),
Path.Combine("HotChocolate", "Diagnostics"),
Path.Combine("HotChocolate", "Language"),
Path.Combine("HotChocolate", "PersistedQueries"),
Path.Combine("HotChocolate", "Utilities"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using HotChocolate.AspNetCore.Subscriptions;
using HotChocolate.AspNetCore.Subscriptions.Messages;
using HotChocolate.Execution;
using Microsoft.AspNetCore.Http;

namespace HotChocolate.AspNetCore;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using HotChocolate.Execution;
using Microsoft.AspNetCore.Http;

namespace HotChocolate.AspNetCore;
Expand Down
14 changes: 7 additions & 7 deletions src/HotChocolate/AspNetCore/src/AspNetCore/ErrorHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ namespace HotChocolate.AspNetCore;
/// </summary>
internal static class ErrorHelper
{
public static IError InvalidRequest()
public static IError InvalidRequest()
=> ErrorBuilder.New()
.SetMessage(AspNetCoreResources.ErrorHelper_InvalidRequest)
.SetCode(ErrorCodes.Server.RequestInvalid)
.Build();

public static IError RequestHasNoElements()
public static IError RequestHasNoElements()
=> ErrorBuilder.New()
.SetMessage(AspNetCoreResources.ErrorHelper_RequestHasNoElements)
.SetCode(ErrorCodes.Server.RequestInvalid)
.Build();

public static IQueryResult ResponseTypeNotSupported()
public static IQueryResult ResponseTypeNotSupported()
=> QueryResultBuilder.CreateError(
ErrorBuilder.New()
.SetMessage(AspNetCoreResources.ErrorHelper_ResponseTypeNotSupported)
Expand All @@ -45,18 +45,18 @@ public static IQueryResult InvalidTypeName(string typeName)
new Error(
"The type name is invalid.",
code: ErrorCodes.Server.InvalidTypeName,
extensions: new Dictionary<string, object?>
extensions: new Dictionary<string, object?>
{
{ "typeName", typeName }
{ "typeName", typeName }
}));

public static IQueryResult TypeNotFound(string typeName)
=> QueryResultBuilder.CreateError(
new Error(
$"The type `{typeName}` does not exist.",
code: ErrorCodes.Server.TypeDoesNotExist,
extensions: new Dictionary<string, object?>
extensions: new Dictionary<string, object?>
{
{ "typeName", typeName }
{ "typeName", typeName }
}));
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using Microsoft.AspNetCore.Builder;

namespace HotChocolate.AspNetCore.Extensions;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using HotChocolate.AspNetCore;
using HotChocolate.AspNetCore.Extensions;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.FileProviders;
using HotChocolate.AspNetCore;
using HotChocolate.AspNetCore.Extensions;
using static Microsoft.AspNetCore.Routing.Patterns.RoutePatternFactory;

namespace Microsoft.AspNetCore.Builder;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using Microsoft.AspNetCore.Builder;

namespace HotChocolate.AspNetCore.Extensions;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using Microsoft.AspNetCore.Builder;

namespace HotChocolate.AspNetCore.Extensions;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
using System;
using System.Text.Json;
using Microsoft.Extensions.DependencyInjection.Extensions;
using HotChocolate.AspNetCore;
using HotChocolate.AspNetCore.Serialization;
using HotChocolate.Execution.Configuration;
using HotChocolate.Utilities;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Microsoft.Extensions.DependencyInjection;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
using System;
using Microsoft.Extensions.DependencyInjection.Extensions;
using HotChocolate.AspNetCore;
using HotChocolate.AspNetCore.Subscriptions;
using HotChocolate.AspNetCore.Subscriptions.Messages;
using HotChocolate.Execution.Configuration;
using HotChocolate.Utilities;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Microsoft.Extensions.DependencyInjection;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using HotChocolate.AspNetCore.Warmup;
using HotChocolate.Execution.Configuration;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using System;
using HotChocolate;
using Microsoft.Extensions.DependencyInjection.Extensions;
using HotChocolate.AspNetCore.Instrumentation;
using HotChocolate.AspNetCore.Serialization;
using HotChocolate.Execution.Configuration;
using HotChocolate.Language;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Microsoft.Extensions.DependencyInjection;

Expand Down Expand Up @@ -45,6 +44,17 @@ public static IServiceCollection AddGraphQLServerCore(
sp.GetRequiredService<IDocumentHashProvider>(),
maxAllowedRequestSize,
sp.GetRequiredService<ParserOptions>()));
services.TryAddSingleton<IServerDiagnosticEvents>(sp =>
{
IServerDiagnosticEventListener[] listeners =
sp.GetServices<IServerDiagnosticEventListener>().ToArray();
return listeners.Length switch
{
0 => new NoopServerDiagnosticEventListener(),
1 => listeners[0],
_ => new AggregateServerDiagnosticEventListener(listeners)
};
});
return services;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using static System.String;
using static System.Globalization.CultureInfo;
using static System.String;

namespace HotChocolate.AspNetCore;

Expand Down Expand Up @@ -42,7 +39,7 @@ public static IHeaderDictionary SetContentDisposition(
this IHeaderDictionary headers,
string fileName)
{
headers[_contentDepositionHeader] =
headers[_contentDepositionHeader] =
Format(InvariantCulture, _contentDepositionValue, fileName);
return headers;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using HotChocolate.Execution;
using Microsoft.Extensions.DependencyInjection;

namespace HotChocolate.AspNetCore;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace HotChocolate.AspNetCore;

[Serializable]
public class GraphQLRequestException
: GraphQLException
public class GraphQLRequestException : GraphQLException
{
public GraphQLRequestException(string message)
: base(message)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<InternalsVisibleTo Include="HotChocolate.AspNetCore.Tests" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\Core\src\Core\HotChocolate.Core.csproj" />
<ProjectReference Include="..\..\..\Core\src\Types.Scalars.Upload\HotChocolate.Types.Scalars.Upload.csproj" />
Expand Down
83 changes: 67 additions & 16 deletions src/HotChocolate/AspNetCore/src/AspNetCore/HttpGetMiddleware.cs
Original file line number Diff line number Diff line change
@@ -1,39 +1,49 @@
using System;
using System.Diagnostics;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using HotChocolate.AspNetCore.Instrumentation;
using HotChocolate.AspNetCore.Serialization;
using HotChocolate.Execution;
using HotChocolate.Language;
using Microsoft.AspNetCore.Http;
using HttpRequestDelegate = Microsoft.AspNetCore.Http.RequestDelegate;

namespace HotChocolate.AspNetCore;

public class HttpGetMiddleware : MiddlewareBase
public sealed class HttpGetMiddleware : MiddlewareBase
{
private static readonly OperationType[] _onlyQueries = { OperationType.Query };

private readonly IHttpRequestParser _requestParser;
private readonly IServerDiagnosticEvents _diagnosticEvents;

public HttpGetMiddleware(
HttpRequestDelegate next,
IRequestExecutorResolver executorResolver,
IHttpResultSerializer resultSerializer,
IHttpRequestParser requestParser,
IServerDiagnosticEvents diagnosticEvents,
NameString schemaName)
: base(next, executorResolver, resultSerializer, schemaName)
{
_requestParser = requestParser ??
throw new ArgumentNullException(nameof(requestParser));
_diagnosticEvents = diagnosticEvents ??
throw new ArgumentNullException(nameof(diagnosticEvents));
}

public async Task InvokeAsync(HttpContext context)
{
if (HttpMethods.IsGet(context.Request.Method) &&
(context.GetGraphQLServerOptions()?.EnableGetRequests ?? true))
{
await HandleRequestAsync(context);
if (!IsDefaultSchema)
{
context.Items[WellKnownContextData.SchemaName] = SchemaName.Value;
}

using (_diagnosticEvents.ExecuteHttpRequest(context, HttpRequestKind.HttpGet))
{
await HandleRequestAsync(context);
}
}
else
{
Expand All @@ -49,32 +59,54 @@ private async Task HandleRequestAsync(HttpContext context)
IRequestExecutor requestExecutor = await GetExecutorAsync(context.RequestAborted);
IHttpRequestInterceptor requestInterceptor = requestExecutor.GetRequestInterceptor();
IErrorHandler errorHandler = requestExecutor.GetErrorHandler();
context.Items[WellKnownContextData.RequestExecutor] = requestExecutor;

HttpStatusCode? statusCode = null;
IExecutionResult? result;

// next we parse the GraphQL request.
GraphQLRequest request;
using (_diagnosticEvents.ParseHttpRequest(context))
{
try
{
request = _requestParser.ReadParamsRequest(context.Request.Query);
}
catch (GraphQLRequestException ex)
{
// A GraphQL request exception is thrown if the HTTP request body couldn't be
// parsed. In this case we will return HTTP status code 400 and return a
// GraphQL error result.
statusCode = HttpStatusCode.BadRequest;
IReadOnlyList<IError> errors = errorHandler.Handle(ex.Errors);
result = QueryResultBuilder.CreateError(errors);
_diagnosticEvents.ParserErrors(context, errors);
goto HANDLE_RESULT;
}
catch (Exception ex)
{
statusCode = HttpStatusCode.InternalServerError;
IError error = errorHandler.CreateUnexpectedError(ex).Build();
result = QueryResultBuilder.CreateError(error);
_diagnosticEvents.HttpRequestError(context, error);
goto HANDLE_RESULT;
}
}

// after successfully parsing the request we now will attempt to execute the request.
try
{
// next we parse the GraphQL request.
GraphQLRequest request = _requestParser.ReadParamsRequest(context.Request.Query);
GraphQLServerOptions? options = context.GetGraphQLServerOptions();
result = await ExecuteSingleAsync(
context,
requestExecutor,
requestInterceptor,
_diagnosticEvents,
request,
options is null or { AllowedGetOperations: AllowedGetOperations.Query }
? _onlyQueries
: null);
}
catch (GraphQLRequestException ex)
{
// A GraphQL request exception is thrown if the HTTP request body couldn't be
// parsed. In this case we will return HTTP status code 400 and return a
// GraphQL error result.
statusCode = HttpStatusCode.BadRequest;
result = QueryResultBuilder.CreateError(errorHandler.Handle(ex.Errors));
}
catch (GraphQLException ex)
{
// This allows extensions to throw GraphQL exceptions in the GraphQL interceptor.
Expand All @@ -88,15 +120,34 @@ private async Task HandleRequestAsync(HttpContext context)
result = QueryResultBuilder.CreateError(error);
}

HANDLE_RESULT:
IDisposable? formatScope = null;

try
{
// if cancellation is requested we will not try to attempt to write the result to the
// response stream.
if (context.RequestAborted.IsCancellationRequested)
{
return;
}

// in any case we will have a valid GraphQL result at this point that can be written
// to the HTTP response stream.
Debug.Assert(result is not null, "No GraphQL result was created.");

if (result is IQueryResult queryResult)
{
formatScope = _diagnosticEvents.FormatHttpResponse(context, queryResult);
}

await WriteResultAsync(context.Response, result, statusCode, context.RequestAborted);
}
finally
{
// we must dispose the diagnostic scope first.
formatScope?.Dispose();

// query results use pooled memory an need to be disposed after we have
// used them.
if (result is IAsyncDisposable asyncDisposable)
Expand Down
Loading