diff --git a/src/All.slnx b/src/All.slnx index 5e0b2b5508e..2603e79eaf4 100644 --- a/src/All.slnx +++ b/src/All.slnx @@ -173,6 +173,7 @@ + diff --git a/src/HotChocolate/Core/src/Execution.Abstractions/Path.cs b/src/HotChocolate/Core/src/Execution.Abstractions/Path.cs index 506e38d3417..e4f1644a208 100644 --- a/src/HotChocolate/Core/src/Execution.Abstractions/Path.cs +++ b/src/HotChocolate/Core/src/Execution.Abstractions/Path.cs @@ -131,7 +131,7 @@ public string Print() { if (this is RootPathSegment) { - return "/"; + return string.Empty; } // On first pass we calculate the total length @@ -147,7 +147,11 @@ public string Print() break; case NamePathSegment name: - totalLength += 1 + name.Name.Length; // '/' + name + totalLength += name.Name.Length; + if (current.Parent is not RootPathSegment) + { + totalLength++; + } break; default: @@ -158,10 +162,13 @@ public string Print() } // On second pass we fill from right to left using string.Create - return string.Create(totalLength, this, static (span, path) => + return string.Create( + totalLength, + this, + static (span, state) => { var pos = span.Length; - var current = path; + var current = state; while (current is not RootPathSegment) { @@ -188,7 +195,10 @@ public string Print() case NamePathSegment name: pos -= name.Name.Length; name.Name.AsSpan().CopyTo(span[pos..]); - span[--pos] = '/'; + if (current.Parent is not RootPathSegment) + { + span[--pos] = '.'; + } break; } diff --git a/src/HotChocolate/Core/src/Types/Execution/Instrumentation/AggregateExecutionDiagnosticEvents.cs b/src/HotChocolate/Core/src/Types/Execution/Instrumentation/AggregateExecutionDiagnosticEvents.cs index 6b7d1a7c81e..b05de0028f0 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Instrumentation/AggregateExecutionDiagnosticEvents.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Instrumentation/AggregateExecutionDiagnosticEvents.cs @@ -186,30 +186,6 @@ public IDisposable ExecuteOperation(RequestContext context) return new AggregateActivityScope(scopes); } - public IDisposable ExecuteStream(IOperation operation) - { - var scopes = new IDisposable[_listeners.Length]; - - for (var i = 0; i < _listeners.Length; i++) - { - scopes[i] = _listeners[i].ExecuteStream(operation); - } - - return new AggregateActivityScope(scopes); - } - - public IDisposable ExecuteDeferredTask() - { - var scopes = new IDisposable[_listeners.Length]; - - for (var i = 0; i < _listeners.Length; i++) - { - scopes[i] = _listeners[i].ExecuteDeferredTask(); - } - - return new AggregateActivityScope(scopes); - } - public IDisposable ResolveFieldValue(IMiddlewareContext context) { if (_resolverListener.Length == 0) @@ -235,14 +211,6 @@ public void ResolverError(IMiddlewareContext context, IError error) } } - public void ResolverError(RequestContext context, ISelection selection, IError error) - { - for (var i = 0; i < _listeners.Length; i++) - { - _listeners[i].ResolverError(context, selection, error); - } - } - public IDisposable RunTask(IExecutionTask task) { if (_resolverListener.Length == 0) @@ -316,18 +284,6 @@ public void SubscriptionEventError(RequestContext context, ulong subscriptionId, } } - public IDisposable DispatchBatch(RequestContext context) - { - var scopes = new IDisposable[_listeners.Length]; - - for (var i = 0; i < _listeners.Length; i++) - { - scopes[i] = _listeners[i].DispatchBatch(context); - } - - return new AggregateActivityScope(scopes); - } - public void ExecutorCreated(string name, IRequestExecutor executor) { for (var i = 0; i < _listeners.Length; i++) diff --git a/src/HotChocolate/Core/src/Types/Execution/Instrumentation/IExecutionDiagnosticEvents.cs b/src/HotChocolate/Core/src/Types/Execution/Instrumentation/IExecutionDiagnosticEvents.cs index c5082be738b..99d7bcd44c9 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Instrumentation/IExecutionDiagnosticEvents.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Instrumentation/IExecutionDiagnosticEvents.cs @@ -79,29 +79,6 @@ public interface IExecutionDiagnosticEvents : ICoreExecutionDiagnosticEvents /// IDisposable CompileOperation(RequestContext context); - /// - /// Called within the execute operation scope when the result is a streamed result. - /// The ExecuteStream scope will run longer than the ExecuteOperation scope. - /// The ExecuteOperation scope completes once the initial operation is executed, - /// while all deferred elements are executed and delivered within the ExecuteStream scope. - /// - /// - /// The compiled operation that is being streamed. - /// - /// - /// A scope that will be disposed when the streaming execution has finished. - /// - IDisposable ExecuteStream(IOperation operation); - - /// - /// Called when starting to execute a deferred part of an operation - /// within the ExecuteStream scope or ExecuteSubscription scope. - /// - /// - /// A scope that will be disposed when the deferred task execution has finished. - /// - IDisposable ExecuteDeferredTask(); - /// /// Called when starting to resolve a field value. /// @@ -131,26 +108,6 @@ public interface IExecutionDiagnosticEvents : ICoreExecutionDiagnosticEvents /// void ResolverError(IMiddlewareContext context, IError error); - /// - /// Called for field errors that occur outside the resolver task execution, - /// typically during result processing or validation. - /// - /// - /// The request context encapsulates all GraphQL-specific information about an - /// individual GraphQL request. - /// - /// - /// The field selection that is affected by the error. - /// - /// - /// The error that occurred during field processing. - /// - /// - /// Some field-level errors are handled after the resolver completes and these - /// are processed in the request scope rather than the resolver scope. - /// - void ResolverError(RequestContext context, ISelection selection, IError error); - /// /// Called when starting to execute an execution engine task. /// @@ -215,19 +172,4 @@ public interface IExecutionDiagnosticEvents : ICoreExecutionDiagnosticEvents /// individual GraphQL request. /// void RetrievedOperationFromCache(RequestContext context); - - /// - /// Called when the execution engine dispatches deferred execution batches. - /// During execution, components like DataLoader defer data resolver execution - /// to be processed in batches. When the execution engine has no immediate work, - /// these batches are dispatched for execution. - /// - /// - /// The request context encapsulates all GraphQL-specific information about an - /// individual GraphQL request. - /// - /// - /// A scope that will be disposed when the batch dispatch has finished. - /// - IDisposable DispatchBatch(RequestContext context); } diff --git a/src/HotChocolate/Core/test/Execution.Abstractions.Tests/ErrorBuilderTests.cs b/src/HotChocolate/Core/test/Execution.Abstractions.Tests/ErrorBuilderTests.cs index f4ef80d374f..b057bc5eeb4 100644 --- a/src/HotChocolate/Core/test/Execution.Abstractions.Tests/ErrorBuilderTests.cs +++ b/src/HotChocolate/Core/test/Execution.Abstractions.Tests/ErrorBuilderTests.cs @@ -192,7 +192,7 @@ public void SetPath_Foo_PathIsFooWithCount1() .Build(); // assert - Assert.Equal("/foo", error.Path?.Print()); + Assert.Equal("foo", error.Path?.Print()); } [Fact] @@ -206,7 +206,7 @@ public void SetPathObject_Foo_PathIsFooWithCount1() .Build(); // assert - Assert.Equal("/foo", error.Path?.Print()); + Assert.Equal("foo", error.Path?.Print()); } [Fact] diff --git a/src/HotChocolate/Core/test/Execution.Abstractions.Tests/ErrorTests.cs b/src/HotChocolate/Core/test/Execution.Abstractions.Tests/ErrorTests.cs index c3492e593dc..545a35c5fe6 100644 --- a/src/HotChocolate/Core/test/Execution.Abstractions.Tests/ErrorTests.cs +++ b/src/HotChocolate/Core/test/Execution.Abstractions.Tests/ErrorTests.cs @@ -204,6 +204,6 @@ public void WithPath() error = error.WithPath(Path.FromList(["foo"])); // assert - Assert.Equal("/foo", error.Path!.Print()); + Assert.Equal("foo", error.Path!.Print()); } } diff --git a/src/HotChocolate/Core/test/Execution.Abstractions.Tests/PathTests.cs b/src/HotChocolate/Core/test/Execution.Abstractions.Tests/PathTests.cs index e1edf4dada5..7b8058db7e4 100644 --- a/src/HotChocolate/Core/test/Execution.Abstractions.Tests/PathTests.cs +++ b/src/HotChocolate/Core/test/Execution.Abstractions.Tests/PathTests.cs @@ -28,7 +28,30 @@ public void Path_ToString() var result = path.ToString(); // assert - Assert.Equal("/hero/friends[0]/name", result); + Assert.Equal("hero.friends[0].name", result); + } + + [Fact] + public void Path_Print() + { + // arrange + var path = Path.Root.Append("person").Append(0).Append("address"); + + // act + var result = path.Print(); + + // assert + Assert.Equal("person[0].address", result); + } + + [Fact] + public void Path_Print_Root() + { + // act + var result = Path.Root.Print(); + + // assert + Assert.Equal(string.Empty, result); } [Fact] @@ -172,12 +195,12 @@ public void Complex_Ordering() string[] expected = [ - "/bar", - "/bar[3]", - "/bar[3][2]", - "/bar[3]/foo", - "/foo", - "/foo[0]" + "bar", + "bar[3]", + "bar[3][2]", + "bar[3].foo", + "foo", + "foo[0]" ]; for (var i = 0; i < paths.Length; i++) diff --git a/src/HotChocolate/Core/test/Execution.Tests/ArgumentNonNullValidatorTests.cs b/src/HotChocolate/Core/test/Execution.Tests/ArgumentNonNullValidatorTests.cs index 204f3bc70c7..b18252aa979 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/ArgumentNonNullValidatorTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/ArgumentNonNullValidatorTests.cs @@ -62,6 +62,6 @@ input Bar { // assert Assert.True(report.HasErrors); - Assert.Equal("/root/a", report.Path.ToString()); + Assert.Equal("root.a", report.Path.ToString()); } } diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/DirectiveTypeTests.Directive_ValidateArgs_ArgMissing.snap b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/DirectiveTypeTests.Directive_ValidateArgs_ArgMissing.snap index f15f7688408..8d6c72af09c 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/DirectiveTypeTests.Directive_ValidateArgs_ArgMissing.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/DirectiveTypeTests.Directive_ValidateArgs_ArgMissing.snap @@ -1,2 +1,2 @@ -The directive arguments have invalid values: 'The required input field `d` is missing.' at /d. +The directive arguments have invalid values: 'The required input field `d` is missing.' at d. @a diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/DirectiveTypeTests.Directive_ValidateArgs_InvalidArg.snap b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/DirectiveTypeTests.Directive_ValidateArgs_InvalidArg.snap index f0be062aa3e..800f228a1b0 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/DirectiveTypeTests.Directive_ValidateArgs_InvalidArg.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/DirectiveTypeTests.Directive_ValidateArgs_InvalidArg.snap @@ -1,2 +1,2 @@ -The directive arguments have invalid values: 'Int cannot coerce the given literal of type `BooleanValue` to a runtime value.' at /e. +The directive arguments have invalid values: 'Int cannot coerce the given literal of type `BooleanValue` to a runtime value.' at e. @a(d: 1, e: true) diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/DirectiveTypeTests.Directive_ValidateArgs_NonNullArgIsNull.snap b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/DirectiveTypeTests.Directive_ValidateArgs_NonNullArgIsNull.snap index 5f94dc0e628..1e61efe51b6 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/DirectiveTypeTests.Directive_ValidateArgs_NonNullArgIsNull.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/DirectiveTypeTests.Directive_ValidateArgs_NonNullArgIsNull.snap @@ -1,2 +1,2 @@ -The directive arguments have invalid values: 'Cannot accept null for non-nullable input.' at /d. +The directive arguments have invalid values: 'Cannot accept null for non-nullable input.' at d. @a(d: null) diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/DirectiveTypeTests.Directive_ValidateArgs_Overflow.snap b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/DirectiveTypeTests.Directive_ValidateArgs_Overflow.snap index 47d5dd351d8..1bba165cbe6 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/DirectiveTypeTests.Directive_ValidateArgs_Overflow.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/DirectiveTypeTests.Directive_ValidateArgs_Overflow.snap @@ -1,2 +1,2 @@ -The directive arguments have invalid values: 'Int cannot coerce the given literal of type `IntValue` to a runtime value.' at /d. +The directive arguments have invalid values: 'Int cannot coerce the given literal of type `IntValue` to a runtime value.' at d. @a(d: 9223372036854775807) diff --git a/src/HotChocolate/Diagnostics/HotChocolate.Diagnostics.slnx b/src/HotChocolate/Diagnostics/HotChocolate.Diagnostics.slnx index 3c4f93bf3f9..a1b11b2c50d 100644 --- a/src/HotChocolate/Diagnostics/HotChocolate.Diagnostics.slnx +++ b/src/HotChocolate/Diagnostics/HotChocolate.Diagnostics.slnx @@ -1,6 +1,7 @@ + diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics.Core/ActivityEnricherBase.cs b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/ActivityEnricherBase.cs new file mode 100644 index 00000000000..a5f9fab1732 --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/ActivityEnricherBase.cs @@ -0,0 +1,103 @@ +using System.Diagnostics; +using Microsoft.AspNetCore.Http; +using HotChocolate.AspNetCore.Instrumentation; +using HotChocolate.Execution; +using HotChocolate.Language; + +namespace HotChocolate.Diagnostics; + +/// +/// Base class for activity enrichers that allows adding additional information +/// to the activity spans created by the diagnostics system. +/// +public abstract class ActivityEnricherBase +{ + public virtual void EnrichExecuteHttpRequest( + HttpContext httpContext, + HttpRequestKind kind, + Activity activity) { } + + public virtual void EnrichSingleRequest( + HttpContext httpContext, + GraphQLRequest request, + Activity activity) { } + + public virtual void EnrichBatchRequest( + HttpContext httpContext, + IReadOnlyList batch, + Activity activity) { } + + public virtual void EnrichOperationBatchRequest( + HttpContext httpContext, + GraphQLRequest request, + IReadOnlyList operations, + Activity activity) { } + + public virtual void EnrichHttpRequestError( + HttpContext httpContext, + IError error, + Activity activity) { } + + public virtual void EnrichHttpRequestError( + HttpContext httpContext, + Exception exception, + Activity activity) { } + + public virtual void EnrichParseHttpRequest( + HttpContext httpContext, + Activity activity) { } + + public virtual void EnrichParserErrors( + HttpContext httpContext, + IReadOnlyList errors, + Activity activity) { } + + public virtual void EnrichFormatHttpResponse( + HttpContext httpContext, + Activity activity) { } + + public virtual void EnrichExecuteRequest( + RequestContext context, + Activity activity) { } + + public virtual void EnrichRequestError( + RequestContext context, + Exception exception, + Activity activity) { } + + public virtual void EnrichRequestError( + RequestContext context, + IError error, + Activity activity) { } + + public virtual void EnrichParseDocument( + RequestContext context, + Activity activity) { } + + public virtual void EnrichValidateDocument( + RequestContext context, + Activity activity) { } + + public virtual void EnrichValidationErrors( + RequestContext context, + IReadOnlyList errors, + Activity activity) { } + + public virtual void EnrichAnalyzeOperationCost( + RequestContext context, + Activity activity) { } + + public virtual void EnrichOperationCost( + RequestContext context, + double fieldCost, + double typeCost, + Activity activity) { } + + public virtual void EnrichCoerceVariables( + RequestContext context, + Activity activity) { } + + public virtual void EnrichExecuteOperation( + RequestContext context, + Activity activity) { } +} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Extensions/ActivityExtensions.cs b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Extensions/ActivityExtensions.cs new file mode 100644 index 00000000000..7a1e602c086 --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Extensions/ActivityExtensions.cs @@ -0,0 +1,105 @@ +using System.Diagnostics; +using System.Runtime.CompilerServices; +using HotChocolate.Execution; +using HotChocolate.Language; + +namespace HotChocolate.Diagnostics; + +internal static class ActivityExtensions +{ + extension(Activity activity) + { +#if !NET9_0_OR_GREATER + public void AddException(Exception exception) + { + activity.AddEvent( + new ActivityEvent( + "exception", + tags: new ActivityTagsCollection + { + { "exception.message", exception.Message }, + { "exception.stacktrace", exception.ToString() }, + { "exception.type", exception.GetType().ToString() } + })); + } +#endif + + public void AddGraphQLError(IError error) + { + var tags = new ActivityTagsCollection + { + [SemanticConventions.GraphQL.Error.Message] = error.Message + }; + + if (error.Exception is { } exception) + { + tags["exception.message"] = exception.Message; + tags["exception.stacktrace"] = exception.ToString(); + tags["exception.type"] = exception.GetType().ToString(); + } + + if (error.Path is not null) + { + tags[SemanticConventions.GraphQL.Error.Path] = error.Path.Print(); + } + + if (!string.IsNullOrEmpty(error.Code)) + { + tags[SemanticConventions.GraphQL.Error.Code] = error.Code; + } + + if (error.Locations is { Count: > 0 }) + { + var locations = new object[error.Locations.Count]; + for (var i = 0; i < error.Locations.Count; i++) + { + var location = error.Locations[i]; + locations[i] = new Dictionary + { + [SemanticConventions.GraphQL.Error.Location.Line] = location.Line, + [SemanticConventions.GraphQL.Error.Location.Column] = location.Column + }; + } + + tags[SemanticConventions.GraphQL.Error.Locations] = locations; + } + + activity.AddEvent(new ActivityEvent("exception", default, tags)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void EnrichDocumentInfo(OperationDocumentInfo documentInfo) + { + var hash = documentInfo.Hash; + + if (!hash.IsEmpty) + { + activity.SetTag( + SemanticConventions.GraphQL.Document.Hash, + $"{hash.AlgorithmName}:{hash.Value}"); + } + + if (documentInfo is { IsPersisted: true, Id.HasValue: true }) + { + activity.SetTag( + SemanticConventions.GraphQL.Document.Id, + documentInfo.Id.Value); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void EnrichOperation(OperationType operationType, string? operationName) + { + activity.SetTag( + SemanticConventions.GraphQL.Operation.Type, + SemanticConventions.GraphQL.Operation.TypeValues[operationType]); + + if (!string.IsNullOrEmpty(operationName)) + { + activity.SetTag( + SemanticConventions.GraphQL.Operation.Name, + operationName); + } + } + } +} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics.Core/HotChocolate.Diagnostics.Core.csproj b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/HotChocolate.Diagnostics.Core.csproj new file mode 100644 index 00000000000..51a678de338 --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/HotChocolate.Diagnostics.Core.csproj @@ -0,0 +1,26 @@ + + + + HotChocolate.Diagnostics.Core + HotChocolate.Diagnostics.Core + HotChocolate.Diagnostics + + + + + + + + + + + + + + + + + + + + diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics.Core/InstrumentationOptionsBase.cs b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/InstrumentationOptionsBase.cs new file mode 100644 index 00000000000..adbdac41253 --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/InstrumentationOptionsBase.cs @@ -0,0 +1,16 @@ +namespace HotChocolate.Diagnostics; + +public abstract class InstrumentationOptionsBase +{ + /// + /// Specifies the request details that shall be included into the tracing activities. + /// + public RequestDetails RequestDetails { get; set; } = RequestDetails.Default; + + /// + /// Specifies if the parsed document shall be included into the tracing data. + /// + public bool IncludeDocument { get; set; } + + internal bool IncludeRequestDetails => RequestDetails is not RequestDetails.None; +} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics.Core/RequestDetails.cs b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/RequestDetails.cs new file mode 100644 index 00000000000..5ae53788ea1 --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/RequestDetails.cs @@ -0,0 +1,15 @@ +namespace HotChocolate.Diagnostics; + +[Flags] +public enum RequestDetails +{ + None = 0, + Id = 1, + Hash = 2, + OperationName = 4, + Variables = 8, + Extensions = 16, + Document = 32, + Default = Id | Hash | OperationName | Extensions, + All = Id | Hash | OperationName | Variables | Extensions | Document +} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics.Core/SemanticConventions.cs b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/SemanticConventions.cs new file mode 100644 index 00000000000..a51c1e5d158 --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/SemanticConventions.cs @@ -0,0 +1,202 @@ +using System.Collections.Frozen; +using HotChocolate.Language; + +namespace HotChocolate.Diagnostics; + +internal static class SemanticConventions +{ + public static class GraphQL + { + public static class Document + { + public const string Id = "graphql.document.id"; + public const string Hash = "graphql.document.hash"; + + // Note: This is not part of the OTEL semantic conventions + public const string Body = "graphql.document.body"; + + // Note: This is not part of the OTEL semantic conventions + public const string Valid = "graphql.document.valid"; + } + + public static class Operation + { + // Note: This is not part of the OTEL semantic conventions + public const string Id = "graphql.operation.id"; + + public const string Name = "graphql.operation.name"; + public const string Type = "graphql.operation.type"; + + // Note: This is not part of the OTEL semantic conventions + public const string FieldCost = "graphql.operation.fieldCost"; + + // Note: This is not part of the OTEL semantic conventions + public const string TypeCost = "graphql.operation.typeCost"; + + public static FrozenDictionary TypeValues { get; } = + new Dictionary + { + [OperationType.Query] = "query", + [OperationType.Mutation] = "mutation", + [OperationType.Subscription] = "subscription" + }.ToFrozenDictionary(); + + public static class Step + { + public const string Id = "graphql.operation.step.id"; + public const string Kind = "graphql.operation.step.kind"; + + // Note: This is not part of the OTEL semantic conventions + public static class KindValues + { + public const string Operation = "operation"; + public const string OperationBatch = "operation_batch"; + public const string Introspection = "introspection"; + public const string Node = "node"; + } + + public static class Plan + { + public const string Id = "graphql.operation.step.plan.id"; + } + } + } + + public static class Processing + { + public const string Type = "graphql.processing.type"; + + public static class TypeValues + { + public const string Parse = "parse"; + public const string Validate = "validate"; + public const string VariableCoercion = "variable_coercion"; + public const string Plan = "plan"; + public const string Execute = "execute"; + public const string StepExecute = "step_execute"; + public const string Resolve = "resolve"; + public const string DataLoaderDispatch = "dataloader_dispatch"; + public const string DataLoaderBatch = "dataloader_batch"; + } + } + + public static class Selection + { + public const string Name = "graphql.selection.name"; + public const string Path = "graphql.selection.path"; + + // Note: This is not part of the OTEL semantic conventions + public const string Hierarchy = "graphql.selection.hierarchy"; + + public static class Field + { + public const string Name = "graphql.selection.field.name"; + public const string ParentType = "graphql.selection.field.parent_type"; + public const string Coordinate = "graphql.selection.field.coordinate"; + + // Note: This is not part of the OTEL semantic conventions + public const string IsDeprecated = "graphql.selection.field.isDeprecated"; + + // Note: This is not part of the OTEL semantic conventions + public const string Type = "graphql.selection.type"; + } + } + + public static class DataLoader + { + public const string Name = "graphql.dataloader.name"; + + public static class Batch + { + public const string Size = "graphql.dataloader.batch.size"; + public const string Keys = "graphql.dataloader.batch.keys"; + } + + public static class Cache + { + public const string HitCount = "graphql.dataloader.cache.hit_count"; + public const string MissCount = "graphql.dataloader.cache.miss_count"; + } + } + + public static class Source + { + public const string Name = "graphql.source.name"; + public const string Id = "graphql.source.id"; + + public static class Operation + { + public const string Name = "graphql.source.operation.name"; + public const string Kind = "graphql.source.operation.kind"; + public const string Hash = "graphql.source.operation.hash"; + } + } + + public static class Error + { + public const string Message = "graphql.error.message"; + public const string Locations = "graphql.error.locations"; + public const string Path = "graphql.error.path"; + public const string Code = "graphql.error.code"; + + public static class Location + { + public const string Column = "column"; + public const string Line = "line"; + } + } + + public static class Subscription + { + public const string Id = "graphql.subscription.id"; + } + + // Note: This is not part of the OTEL semantic conventions + public static class Http + { + public const string Kind = "graphql.http.kind"; + + public static class Request + { + public const string Type = "graphql.http.request.type"; + public const string QueryId = "graphql.http.request.query.id"; + public const string QueryHash = "graphql.http.request.query.hash"; + public const string QueryBody = "graphql.http.request.query.body"; + public const string OperationName = "graphql.http.request.operation"; + public const string Operations = "graphql.http.request.operations"; + public const string Variables = "graphql.http.request.variables"; + public const string Extensions = "graphql.http.request.extensions"; + + // Note: This is not part of the OTEL semantic conventions + public static class Types + { + public const string Single = "single"; + public const string Batch = "batch"; + public const string OperationBatch = "operation_batch"; + } + + // Note: This is not part of the OTEL semantic conventions + public static class BatchRequest + { + public static string QueryId(int index) => $"graphql.http.request[{index}].query.id"; + + public static string QueryHash(int index) => $"graphql.http.request[{index}].query.hash"; + + public static string QueryBody(int index) => $"graphql.http.request[{index}].query.body"; + + public static string OperationName(int index) => $"graphql.http.request[{index}].operation"; + + public static string Variables(int index) => $"graphql.http.request[{index}].variables"; + + public static string Extensions(int index) => $"graphql.http.request[{index}].extensions"; + } + } + } + + // Note: This is not part of the OTEL semantic conventions + public static class Schema + { + public const string Name = "graphql.schema.name"; + } + } +} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/AnalyzeOperationComplexitySpan.cs b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/AnalyzeOperationComplexitySpan.cs new file mode 100644 index 00000000000..ad2a778f7c7 --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/AnalyzeOperationComplexitySpan.cs @@ -0,0 +1,50 @@ +using System.Diagnostics; +using HotChocolate.Execution; +using static HotChocolate.Diagnostics.SemanticConventions; + +namespace HotChocolate.Diagnostics; + +internal sealed class AnalyzeOperationComplexitySpan( + Activity activity, + RequestContext context, + ActivityEnricherBase enricher) : SpanBase(activity) +{ + private bool _costSet; + + public static AnalyzeOperationComplexitySpan? Start( + ActivitySource source, + RequestContext context, + ActivityEnricherBase enricher) + { + var activity = source.StartActivity("GraphQL Complexity Analysis"); + + if (activity is null) + { + return null; + } + + activity.EnrichDocumentInfo(context.OperationDocumentInfo); + + return new AnalyzeOperationComplexitySpan(activity, context, enricher); + } + + public void SetCost(double fieldCost, double typeCost) + { + Activity.SetTag(GraphQL.Operation.FieldCost, fieldCost); + Activity.SetTag(GraphQL.Operation.TypeCost, typeCost); + + _costSet = true; + + enricher.EnrichOperationCost(context, fieldCost, typeCost, Activity); + } + + protected override void OnComplete() + { + if (_costSet) + { + Activity.SetStatus(ActivityStatusCode.Ok); + } + + enricher.EnrichAnalyzeOperationCost(context, Activity); + } +} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/ExecuteOperationSpan.cs b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/ExecuteOperationSpan.cs new file mode 100644 index 00000000000..50d3604bc2b --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/ExecuteOperationSpan.cs @@ -0,0 +1,49 @@ +using System.Diagnostics; +using HotChocolate.Execution; +using HotChocolate.Language; +using OpenTelemetry.Trace; +using static HotChocolate.Diagnostics.SemanticConventions; + +namespace HotChocolate.Diagnostics; + +internal sealed class ExecuteOperationSpan( + Activity activity, + RequestContext context, + ActivityEnricherBase enricher) : SpanBase(activity) +{ + public static ExecuteOperationSpan? Start( + ActivitySource source, + RequestContext context, + OperationType operationType, + string? operationName, + ActivityEnricherBase enricher) + { + var activity = source.StartActivity("GraphQL Operation Execution"); + + if (activity is null) + { + return null; + } + + activity.SetTag(GraphQL.Processing.Type, GraphQL.Processing.TypeValues.Execute); + + activity.EnrichOperation(operationType, operationName); + activity.EnrichDocumentInfo(context.OperationDocumentInfo); + + return new ExecuteOperationSpan(activity, context, enricher); + } + + protected override void OnComplete() + { + if (context.Result is null or OperationResult { Errors: [_, ..] }) + { + Activity.SetStatus(ActivityStatusCode.Error); + } + else + { + Activity.SetStatus(ActivityStatusCode.Ok); + } + + enricher.EnrichExecuteOperation(context, Activity); + } +} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/ExecuteRequestSpanBase.cs b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/ExecuteRequestSpanBase.cs new file mode 100644 index 00000000000..d3cc3bef269 --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/ExecuteRequestSpanBase.cs @@ -0,0 +1,56 @@ +using System.Diagnostics; +using HotChocolate.Execution; +using HotChocolate.Language; +using HotChocolate.Language.Utilities; +using static HotChocolate.Diagnostics.SemanticConventions; + +namespace HotChocolate.Diagnostics; + +internal abstract class ExecuteRequestSpanBase( + Activity activity, + RequestContext context, + InstrumentationOptionsBase options, + ActivityEnricherBase? enricher, + bool shouldDisposeActivity) : SpanBase(activity, shouldDisposeActivity) +{ + public RequestContext Context { get; } = context; + + protected static Activity? StartActivity(ActivitySource source) + { + return source.StartActivity("GraphQL Operation", ActivityKind.Server); + } + + protected abstract bool TryGetOperationInfo( + out OperationType operationType, + out string? operationName); + + protected override void OnComplete() + { + if (TryGetOperationInfo(out var operationType, out var operationName)) + { + var operationTypeValue = GraphQL.Operation.TypeValues[operationType]; + Activity.DisplayName = operationTypeValue; + Activity.EnrichOperation(operationType, operationName); + } + + var documentInfo = Context.OperationDocumentInfo; + + Activity.EnrichDocumentInfo(documentInfo); + + if (options.IncludeDocument && documentInfo.Document is not null) + { + Activity.SetTag(GraphQL.Document.Body, documentInfo.Document.Print()); + } + + if (Context.Result is null or OperationResult { Errors: [_, ..] }) + { + Activity.SetStatus(ActivityStatusCode.Error); + } + else if (Activity.Status != ActivityStatusCode.Error) + { + Activity.SetStatus(ActivityStatusCode.Ok); + } + + enricher?.EnrichExecuteRequest(Context, Activity); + } +} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/Http/ExecuteHttpRequestSpan.cs b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/Http/ExecuteHttpRequestSpan.cs new file mode 100644 index 00000000000..94373731cf5 --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/Http/ExecuteHttpRequestSpan.cs @@ -0,0 +1,249 @@ +using System.Diagnostics; +using Microsoft.AspNetCore.Http; +using HotChocolate.AspNetCore.Instrumentation; +using HotChocolate.Execution; +using HotChocolate.Language; +using HotChocolate.Language.Utilities; +using OpenTelemetry.Trace; +using static HotChocolate.Diagnostics.SemanticConventions; +using static HotChocolate.WellKnownContextData; + +namespace HotChocolate.Diagnostics; + +internal sealed class ExecuteHttpRequestSpan( + Activity activity, + HttpContext httpContext, + HttpRequestKind kind, + ActivityEnricherBase enricher, + InstrumentationOptionsBase options) : SpanBase(activity) +{ + public static ExecuteHttpRequestSpan? Start( + ActivitySource source, + HttpContext httpContext, + HttpRequestKind kind, + ActivityEnricherBase enricher, + InstrumentationOptionsBase options) + { + var activity = source.StartActivity("ExecuteHttpRequest"); + + if (activity is null) + { + return null; + } + + switch (kind) + { + case HttpRequestKind.HttpPost: + activity.DisplayName = "GraphQL HTTP POST"; + break; + case HttpRequestKind.HttpMultiPart: + activity.DisplayName = "GraphQL HTTP POST MultiPart"; + break; + case HttpRequestKind.HttpGet: + activity.DisplayName = "GraphQL HTTP GET"; + break; + case HttpRequestKind.HttpGetSchema: + activity.DisplayName = "GraphQL HTTP GET SDL"; + break; + } + + activity.SetTag(GraphQL.Http.Kind, kind.ToString()); + + if (!(httpContext.Items.TryGetValue(SchemaName, out var value) + && value is string schemaName)) + { + schemaName = ISchemaDefinition.DefaultName; + } + + activity.SetTag(GraphQL.Schema.Name, schemaName); + + return new ExecuteHttpRequestSpan(activity, httpContext, kind, enricher, options); + } + + public void SetSingleRequestDetails(GraphQLRequest request) + { + Activity.SetTag(GraphQL.Http.Request.Type, GraphQL.Http.Request.Types.Single); + + if (request.DocumentId is not null + && (options.RequestDetails & RequestDetails.Id) == RequestDetails.Id) + { + Activity.SetTag(GraphQL.Http.Request.QueryId, request.DocumentId.Value.Value); + } + + if (request.DocumentHash is not null + && (options.RequestDetails & RequestDetails.Hash) == RequestDetails.Hash) + { + Activity.SetTag(GraphQL.Http.Request.QueryHash, request.DocumentHash.Value.Value); + } + + if (request.Document is not null + && (options.RequestDetails & RequestDetails.Document) == RequestDetails.Document) + { + Activity.SetTag(GraphQL.Http.Request.QueryBody, request.Document.Print()); + } + + if (request.OperationName is not null + && (options.RequestDetails & RequestDetails.OperationName) == RequestDetails.OperationName) + { + Activity.SetTag(GraphQL.Http.Request.OperationName, request.OperationName); + } + + if (request.Variables is not null + && (options.RequestDetails & RequestDetails.Variables) == RequestDetails.Variables) + { + Activity.SetTag(GraphQL.Http.Request.Variables, request.Variables.RootElement.ToString()); + } + + if (request.Extensions is not null + && (options.RequestDetails & RequestDetails.Extensions) == RequestDetails.Extensions) + { + try + { + Activity.SetTag(GraphQL.Http.Request.Extensions, request.Extensions.RootElement.ToString()); + } + catch + { + // Ignore any errors + } + } + + enricher.EnrichSingleRequest(httpContext, request, Activity); + } + + public void SetBatchRequestDetails(IReadOnlyList batch) + { + Activity.SetTag(GraphQL.Http.Request.Type, GraphQL.Http.Request.Types.Batch); + + for (var i = 0; i < batch.Count; i++) + { + var request = batch[i]; + + if (request.DocumentId is not null + && (options.RequestDetails & RequestDetails.Id) == RequestDetails.Id) + { + Activity.SetTag(GraphQL.Http.Request.BatchRequest.QueryId(i), request.DocumentId.Value); + } + + if (request.DocumentHash is not null + && (options.RequestDetails & RequestDetails.Hash) == RequestDetails.Hash) + { + Activity.SetTag(GraphQL.Http.Request.BatchRequest.QueryHash(i), request.DocumentHash.Value); + } + + if (request.Document is not null + && (options.RequestDetails & RequestDetails.Document) == RequestDetails.Document) + { + Activity.SetTag(GraphQL.Http.Request.BatchRequest.QueryBody(i), request.Document.Print()); + } + + if (request.OperationName is not null + && (options.RequestDetails & RequestDetails.OperationName) == RequestDetails.OperationName) + { + Activity.SetTag(GraphQL.Http.Request.BatchRequest.OperationName(i), request.OperationName); + } + + if (request.Variables is not null + && (options.RequestDetails & RequestDetails.Variables) == RequestDetails.Variables) + { + Activity.SetTag( + GraphQL.Http.Request.BatchRequest.Variables(i), + request.Variables.RootElement.ToString()); + } + + if (request.Extensions is not null + && (options.RequestDetails & RequestDetails.Extensions) == RequestDetails.Extensions) + { + try + { + Activity.SetTag( + GraphQL.Http.Request.BatchRequest.Extensions(i), + request.Extensions.RootElement.ToString()); + } + catch + { + // Ignore any errors + } + } + } + + enricher.EnrichBatchRequest(httpContext, batch, Activity); + } + + public void SetOperationBatchRequestDetails( + GraphQLRequest request, + IReadOnlyList operations) + { + Activity.SetTag(GraphQL.Http.Request.Type, GraphQL.Http.Request.Types.OperationBatch); + + if (request.DocumentId is not null + && (options.RequestDetails & RequestDetails.Id) == RequestDetails.Id) + { + Activity.SetTag(GraphQL.Http.Request.QueryId, request.DocumentId.Value.Value); + } + + if (request.DocumentHash is not null + && (options.RequestDetails & RequestDetails.Hash) == RequestDetails.Hash) + { + Activity.SetTag(GraphQL.Http.Request.QueryHash, request.DocumentHash.Value.Value); + } + + if (request.Document is not null + && (options.RequestDetails & RequestDetails.Document) == RequestDetails.Document) + { + Activity.SetTag(GraphQL.Http.Request.QueryBody, request.Document.Print()); + } + + if (request.OperationName is not null + && (options.RequestDetails & RequestDetails.OperationName) == RequestDetails.OperationName) + { + Activity.SetTag(GraphQL.Http.Request.Operations, string.Join(" -> ", operations)); + } + + if (request.Variables is not null + && (options.RequestDetails & RequestDetails.Variables) == RequestDetails.Variables) + { + Activity.SetTag(GraphQL.Http.Request.Variables, request.Variables.RootElement.ToString()); + } + + if (request.Extensions is not null + && (options.RequestDetails & RequestDetails.Extensions) == RequestDetails.Extensions) + { + try + { + Activity.SetTag(GraphQL.Http.Request.Extensions, request.Extensions.RootElement.ToString()); + } + catch + { + // Ignore any errors + } + } + + enricher.EnrichOperationBatchRequest(httpContext, request, operations, Activity); + } + + protected override void OnComplete() + { + if (Activity.Status != ActivityStatusCode.Error) + { + Activity.SetStatus(ActivityStatusCode.Ok); + } + + enricher.EnrichExecuteHttpRequest(httpContext, kind, Activity); + } + + public void RecordError(IError error) + { + Activity.SetStatus(ActivityStatusCode.Error); + Activity.AddGraphQLError(error); + + enricher.EnrichHttpRequestError(httpContext, error, Activity); + } + + public void RecordError(Exception exception) + { + Activity.SetStatus(ActivityStatusCode.Error); + Activity.AddException(exception); + + enricher.EnrichHttpRequestError(httpContext, exception, Activity); + } +} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/Http/FormatHttpResponseSpan.cs b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/Http/FormatHttpResponseSpan.cs new file mode 100644 index 00000000000..e8cf5d8e580 --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/Http/FormatHttpResponseSpan.cs @@ -0,0 +1,36 @@ +using System.Diagnostics; +using Microsoft.AspNetCore.Http; +using OpenTelemetry.Trace; + +namespace HotChocolate.Diagnostics; + +internal sealed class FormatHttpResponseSpan( + Activity activity, + HttpContext httpContext, + ActivityEnricherBase enricher) : SpanBase(activity) +{ + public static FormatHttpResponseSpan? Start( + ActivitySource source, + HttpContext httpContext, + ActivityEnricherBase enricher) + { + var activity = source.StartActivity("Format HTTP Response"); + + if (activity is null) + { + return null; + } + + return new FormatHttpResponseSpan(activity, httpContext, enricher); + } + + protected override void OnComplete() + { + if (Activity.Status != ActivityStatusCode.Error) + { + Activity.SetStatus(ActivityStatusCode.Ok); + } + + enricher.EnrichFormatHttpResponse(httpContext, Activity); + } +} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/Http/ParseHttpRequestSpan.cs b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/Http/ParseHttpRequestSpan.cs new file mode 100644 index 00000000000..8f809d2b0df --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/Http/ParseHttpRequestSpan.cs @@ -0,0 +1,48 @@ +using System.Diagnostics; +using Microsoft.AspNetCore.Http; +using OpenTelemetry.Trace; + +namespace HotChocolate.Diagnostics; + +internal sealed class ParseHttpRequestSpan( + Activity activity, + HttpContext httpContext, + ActivityEnricherBase enricher) : SpanBase(activity) +{ + public static ParseHttpRequestSpan? Start( + ActivitySource source, + HttpContext httpContext, + ActivityEnricherBase enricher) + { + var activity = source.StartActivity("Parse HTTP Request"); + + if (activity is null) + { + return null; + } + + return new ParseHttpRequestSpan(activity, httpContext, enricher); + } + + public void RecordErrors(IReadOnlyList errors) + { + Activity.SetStatus(ActivityStatusCode.Error); + + foreach (var error in errors) + { + Activity.AddGraphQLError(error); + } + + enricher.EnrichParserErrors(httpContext, errors, Activity); + } + + protected override void OnComplete() + { + if (Activity.Status != ActivityStatusCode.Error) + { + Activity.SetStatus(ActivityStatusCode.Ok); + } + + enricher.EnrichParseHttpRequest(httpContext, Activity); + } +} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/ParsingSpan.cs b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/ParsingSpan.cs new file mode 100644 index 00000000000..7cedcfbc343 --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/ParsingSpan.cs @@ -0,0 +1,45 @@ +using System.Diagnostics; +using HotChocolate.Execution; +using OpenTelemetry.Trace; +using static HotChocolate.Diagnostics.SemanticConventions; + +namespace HotChocolate.Diagnostics; + +internal sealed class ParsingSpan( + Activity activity, + RequestContext context, + ActivityEnricherBase enricher) : SpanBase(activity) +{ + public static ParsingSpan? Start( + ActivitySource source, + RequestContext context, + ActivityEnricherBase enricher) + { + var activity = source.StartActivity("GraphQL Document Parsing"); + + if (activity is null) + { + return null; + } + + // We do not set this here, as parsing can happen in the HTTP middleware + // or the HotChocolate pipeline. + // For the moment we just track both as regular spans. + // Maybe in the future we can reconcile this. + // activity.SetTag(GraphQL.Processing.Type, GraphQL.Processing.TypeValues.Parse); + + return new ParsingSpan(activity, context, enricher); + } + + protected override void OnComplete() + { + if (context.TryGetOperationDocument(out _, out _)) + { + Activity.SetStatus(ActivityStatusCode.Ok); + } + + Activity.EnrichDocumentInfo(context.OperationDocumentInfo); + + enricher.EnrichParseDocument(context, Activity); + } +} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/SpanBase.cs b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/SpanBase.cs new file mode 100644 index 00000000000..a5fd63b5965 --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/SpanBase.cs @@ -0,0 +1,27 @@ +using System.Diagnostics; + +namespace HotChocolate.Diagnostics; + +internal abstract class SpanBase(Activity activity, bool shouldDisposeActivity = true) : IDisposable +{ + private bool _disposed; + + public Activity Activity { get; } = activity; + + protected virtual void OnComplete() { } + + public void Dispose() + { + if (!_disposed) + { + _disposed = true; + + OnComplete(); + + if (shouldDisposeActivity) + { + Activity.Dispose(); + } + } + } +} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/SubscriptionEventSpan.cs b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/SubscriptionEventSpan.cs new file mode 100644 index 00000000000..31e83e1e6e5 --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/SubscriptionEventSpan.cs @@ -0,0 +1,42 @@ +using System.Diagnostics; +using System.Globalization; +using HotChocolate.Execution; +using HotChocolate.Language; +using static HotChocolate.Diagnostics.SemanticConventions; + +namespace HotChocolate.Diagnostics; + +internal sealed class SubscriptionEventSpan(Activity activity) : SpanBase(activity) +{ + public static SubscriptionEventSpan? Start( + ActivitySource source, + RequestContext context, + string? operationName, + ulong subscriptionId, + ActivityContext? subscriptionContext = null) + { + var activity = subscriptionContext is { } parent + ? source.StartActivity("GraphQL Subscription Event", ActivityKind.Internal, parent) + : source.StartActivity("GraphQL Subscription Event"); + + if (activity is null) + { + return null; + } + + activity.SetTag(GraphQL.Processing.Type, GraphQL.Processing.TypeValues.Execute); + activity.EnrichOperation(OperationType.Subscription, operationName); + activity.EnrichDocumentInfo(context.OperationDocumentInfo); + activity.SetTag(GraphQL.Subscription.Id, subscriptionId.ToString(CultureInfo.InvariantCulture)); + + return new SubscriptionEventSpan(activity); + } + + protected override void OnComplete() + { + if (Activity.Status != ActivityStatusCode.Error) + { + Activity.SetStatus(ActivityStatusCode.Ok); + } + } +} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/ValidationSpan.cs b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/ValidationSpan.cs new file mode 100644 index 00000000000..62df9746771 --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/ValidationSpan.cs @@ -0,0 +1,41 @@ +using System.Diagnostics; +using HotChocolate.Execution; +using OpenTelemetry.Trace; +using static HotChocolate.Diagnostics.SemanticConventions; + +namespace HotChocolate.Diagnostics; + +internal sealed class ValidationSpan( + Activity activity, + RequestContext context, + ActivityEnricherBase enricher) : SpanBase(activity) +{ + public static ValidationSpan? Start( + ActivitySource source, + RequestContext context, + ActivityEnricherBase enricher) + { + var activity = source.StartActivity("GraphQL Document Validation"); + + if (activity is null) + { + return null; + } + + activity.SetTag(GraphQL.Processing.Type, GraphQL.Processing.TypeValues.Validate); + + activity.EnrichDocumentInfo(context.OperationDocumentInfo); + + return new ValidationSpan(activity, context, enricher); + } + + protected override void OnComplete() + { + if (context.IsOperationDocumentValid()) + { + Activity.SetStatus(ActivityStatusCode.Ok); + } + + enricher.EnrichValidateDocument(context, Activity); + } +} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/VariableCoercionSpan.cs b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/VariableCoercionSpan.cs new file mode 100644 index 00000000000..c54786d9387 --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/Spans/VariableCoercionSpan.cs @@ -0,0 +1,45 @@ +using System.Diagnostics; +using HotChocolate.Execution; +using HotChocolate.Language; +using OpenTelemetry.Trace; +using static HotChocolate.Diagnostics.SemanticConventions; + +namespace HotChocolate.Diagnostics; + +internal sealed class VariableCoercionSpan( + Activity activity, + RequestContext context, + ActivityEnricherBase enricher) : SpanBase(activity) +{ + public static VariableCoercionSpan? Start( + ActivitySource source, + RequestContext context, + OperationType operationType, + string? operationName, + ActivityEnricherBase enricher) + { + var activity = source.StartActivity("GraphQL Variable Coercion"); + + if (activity is null) + { + return null; + } + + activity.SetTag(GraphQL.Processing.Type, GraphQL.Processing.TypeValues.VariableCoercion); + + activity.EnrichOperation(operationType, operationName); + activity.EnrichDocumentInfo(context.OperationDocumentInfo); + + return new VariableCoercionSpan(activity, context, enricher); + } + + protected override void OnComplete() + { + if (context.VariableValues.Length > 0) + { + Activity.SetStatus(ActivityStatusCode.Ok); + } + + enricher.EnrichCoerceVariables(context, Activity); + } +} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/ActivityEnricher.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/ActivityEnricher.cs index 41b16cac147..896c1b3fcfc 100644 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/ActivityEnricher.cs +++ b/src/HotChocolate/Diagnostics/src/Diagnostics/ActivityEnricher.cs @@ -1,623 +1,47 @@ using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Text; -using System.Text.Json; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.ObjectPool; using GreenDonut; -using HotChocolate.AspNetCore.Instrumentation; using HotChocolate.Execution; -using HotChocolate.Execution.Processing; -using HotChocolate.Language; -using HotChocolate.Language.Utilities; using HotChocolate.Resolvers; -using HotChocolate.Types; -using OpenTelemetry.Trace; -using static HotChocolate.Diagnostics.SemanticConventions; -using static HotChocolate.WellKnownContextData; namespace HotChocolate.Diagnostics; /// -/// The activity enricher is used to add information to the activity spans. -/// You can inherit from this class and override the enricher methods to provide more or -/// less information. +/// The activity enricher allows adding additional information to the activity spans +/// created by the Hot Chocolate diagnostics system. +/// You can inherit from this class and override the enricher methods to add +/// additional information to the spans. /// -public class ActivityEnricher +public class ActivityEnricher(InstrumentationOptions options) : ActivityEnricherBase { - private readonly InstrumentationOptions _options; - private readonly ConditionalWeakTable _queryCache = []; + protected InstrumentationOptions Options { get; } = options; - /// - /// Initializes a new instance of . - /// - /// - /// - protected ActivityEnricher( - ObjectPool stringBuilderPool, - InstrumentationOptions options) - { - StringBuilderPool = stringBuilderPool; - _options = options; - } - - /// - /// Gets the pool used by this enricher. - /// - protected ObjectPool StringBuilderPool { get; } - - public virtual void EnrichExecuteHttpRequest( - HttpContext context, - HttpRequestKind kind, - Activity activity) - { - switch (kind) - { - case HttpRequestKind.HttpPost: - activity.DisplayName = "GraphQL HTTP POST"; - break; - case HttpRequestKind.HttpMultiPart: - activity.DisplayName = "GraphQL HTTP POST MultiPart"; - break; - case HttpRequestKind.HttpGet: - activity.DisplayName = "GraphQL HTTP GET"; - break; - case HttpRequestKind.HttpGetSchema: - activity.DisplayName = "GraphQL HTTP GET SDL"; - break; - } - - if (_options.RenameRootActivity) - { - UpdateRootActivityName(activity, $"Begin {activity.DisplayName}"); - } - - activity.SetTag("graphql.http.kind", kind); - - var isDefault = false; - if (!(context.Items.TryGetValue(SchemaName, out var value) - && value is string schemaName)) - { - schemaName = ISchemaDefinition.DefaultName; - isDefault = true; - } - - activity.SetTag("graphql.schema.name", schemaName); - activity.SetTag("graphql.schema.isDefault", isDefault); - } - - public virtual void EnrichSingleRequest( - HttpContext context, - GraphQLRequest request, - Activity activity) - { - activity.SetTag("graphql.http.request.type", "single"); - - if (request.DocumentId is not null - && (_options.RequestDetails & RequestDetails.Id) == RequestDetails.Id) - { - activity.SetTag("graphql.http.request.query.id", request.DocumentId.Value); - } - - if (request.DocumentHash is not null - && (_options.RequestDetails & RequestDetails.Hash) == RequestDetails.Hash) - { - activity.SetTag("graphql.http.request.query.hash", request.DocumentHash.Value); - } - - if (request.Document is not null - && (_options.RequestDetails & RequestDetails.Query) == RequestDetails.Query) - { - if (!_queryCache.TryGetValue(request.Document, out var query)) - { - query = request.Document.Print(); - _queryCache.Add(request.Document, query); - } - - activity.SetTag("graphql.http.request.query.body", query); - } - - if (request.OperationName is not null - && (_options.RequestDetails & RequestDetails.Operation) == RequestDetails.Operation) - { - activity.SetTag("graphql.http.request.operation", request.OperationName); - } - - if (request.Variables is not null - && (_options.RequestDetails & RequestDetails.Variables) == RequestDetails.Variables) - { - EnrichRequestVariables(context, request, request.Variables, activity); - } - - if (request.Extensions is not null - && (_options.RequestDetails & RequestDetails.Extensions) == RequestDetails.Extensions) - { - EnrichRequestExtensions(context, request, request.Extensions, activity); - } - } - - public virtual void EnrichBatchRequest( - HttpContext context, - IReadOnlyList batch, - Activity activity) - { - activity.SetTag("graphql.http.request.type", "batch"); - - for (var i = 0; i < batch.Count; i++) - { - var request = batch[i]; - - if (request.DocumentId is not null - && (_options.RequestDetails & RequestDetails.Id) == RequestDetails.Id) - { - activity.SetTag($"graphql.http.request[{i}].query.id", request.DocumentId.Value); - } - - if (request.DocumentHash is not null - && (_options.RequestDetails & RequestDetails.Hash) == RequestDetails.Hash) - { - activity.SetTag($"graphql.http.request[{i}].query.hash", request.DocumentHash.Value); - } - - if (request.Document is not null - && (_options.RequestDetails & RequestDetails.Query) == RequestDetails.Query) - { - activity.SetTag($"graphql.http.request[{i}].query.body", request.Document.Print()); - } - - if (request.OperationName is not null - && (_options.RequestDetails & RequestDetails.Operation) == RequestDetails.Operation) - { - activity.SetTag($"graphql.http.request[{i}].operation", request.OperationName); - } - - if (request.Variables is not null - && (_options.RequestDetails & RequestDetails.Variables) == RequestDetails.Variables) - { - EnrichBatchVariables(context, request, request.Variables, i, activity); - } - - if (request.Extensions is not null - && (_options.RequestDetails & RequestDetails.Extensions) == RequestDetails.Extensions) - { - EnrichBatchExtensions(context, request, request.Extensions, i, activity); - } - } - } - - public virtual void EnrichOperationBatchRequest( - HttpContext context, - GraphQLRequest request, - IReadOnlyList operations, - Activity activity) - { - activity.SetTag("graphql.http.request.type", "operationBatch"); - - if (request.DocumentId is not null - && (_options.RequestDetails & RequestDetails.Id) == RequestDetails.Id) - { - activity.SetTag("graphql.http.request.query.id", request.DocumentId.Value); - } - - if (request.DocumentHash is not null - && (_options.RequestDetails & RequestDetails.Hash) == RequestDetails.Hash) - { - activity.SetTag("graphql.http.request.query.hash", request.DocumentHash.Value); - } - - if (request.Document is not null - && (_options.RequestDetails & RequestDetails.Query) == RequestDetails.Query) - { - activity.SetTag("graphql.http.request.query.body", request.Document.Print()); - } - - if (request.OperationName is not null - && (_options.RequestDetails & RequestDetails.Operation) == RequestDetails.Operation) - { - activity.SetTag("graphql.http.request.operations", string.Join(" -> ", operations)); - } - - if (request.Variables is not null - && (_options.RequestDetails & RequestDetails.Variables) == RequestDetails.Variables) - { - EnrichRequestVariables(context, request, request.Variables, activity); - } - - if (request.Extensions is not null - && (_options.RequestDetails & RequestDetails.Extensions) == RequestDetails.Extensions) - { - EnrichRequestExtensions(context, request, request.Extensions, activity); - } - } - - protected virtual void EnrichRequestVariables( - HttpContext context, - GraphQLRequest request, - JsonDocument variables, - Activity activity) - => activity.SetTag("graphql.http.request.variables", variables.RootElement.ToString()); - - protected virtual void EnrichBatchVariables( - HttpContext context, - GraphQLRequest request, - JsonDocument variables, - int index, - Activity activity) - => activity.SetTag($"graphql.http.request[{index}].variables", variables.RootElement.ToString()); - - protected virtual void EnrichRequestExtensions( - HttpContext context, - GraphQLRequest request, - JsonDocument extensions, - Activity activity) - { - try - { - activity.SetTag( - "graphql.http.request.extensions", - extensions.RootElement.ToString()); - } - catch - { - // Ignore any errors - } - } - - protected virtual void EnrichBatchExtensions( - HttpContext context, - GraphQLRequest request, - JsonDocument extensions, - int index, - Activity activity) - { - try - { - activity.SetTag( - $"graphql.http.request[{index}].extensions", - extensions.RootElement.ToString()); - } - catch - { - // Ignore any errors - } - } - - public virtual void EnrichHttpRequestError( - HttpContext context, - IError error, - Activity activity) - => EnrichError(error, activity); - - public virtual void EnrichHttpRequestError( - HttpContext context, - Exception exception, - Activity activity) - { - } - - public virtual void EnrichParseHttpRequest(HttpContext context, Activity activity) - { - activity.DisplayName = "Parse HTTP Request"; - - if (_options.RenameRootActivity) - { - UpdateRootActivityName(activity, $"Begin {activity.DisplayName}"); - } - } - - public virtual void EnrichParserErrors(HttpContext context, IError error, Activity activity) - => EnrichError(error, activity); - - public virtual void EnrichFormatHttpResponse(HttpContext context, Activity activity) - { - activity.DisplayName = "Format HTTP Response"; - } - - public virtual void EnrichExecuteRequest(RequestContext context, Activity activity) - { - context.TryGetOperation(out var operation); - var documentInfo = context.OperationDocumentInfo; - var operationDisplayName = CreateOperationDisplayName(context, operation); - - if (_options.RenameRootActivity && operationDisplayName is not null) - { - UpdateRootActivityName(activity, operationDisplayName); - } - - activity.DisplayName = operationDisplayName ?? "Execute Request"; - activity.SetTag("graphql.document.id", documentInfo.Id.Value); - activity.SetTag("graphql.document.hash", documentInfo.Hash.Value); - activity.SetTag("graphql.document.valid", documentInfo.IsValidated); - activity.SetTag("graphql.operation.id", operation?.Id); - activity.SetTag("graphql.operation.kind", operation?.Kind); - activity.SetTag("graphql.operation.name", operation?.Name); - - if (_options.IncludeDocument && documentInfo.Document is not null) - { - activity.SetTag("graphql.document.body", documentInfo.Document.Print()); - } - - if (context.Result is OperationResult result) - { - var errorCount = result.Errors.Count; - activity.SetTag("graphql.errors.count", errorCount); - } - } - protected virtual string? CreateOperationDisplayName(RequestContext context, Operation? operation) - { - if (operation is null) - { - return null; - } - - var displayName = StringBuilderPool.Get(); - - try - { - var rootSelectionSet = operation.RootSelectionSet; - var selectionCount = rootSelectionSet.Selections.Length; - - displayName.Append('{'); - displayName.Append(' '); - - foreach (var selection in rootSelectionSet.Selections[..Math.Min(3, selectionCount)]) - { - if (displayName.Length > 2) - { - displayName.Append(' '); - } - - displayName.Append(selection.ResponseName); - } - - if (rootSelectionSet.Selections.Length > 3) - { - displayName.Append(' '); - displayName.Append('.'); - displayName.Append('.'); - displayName.Append('.'); - } - - displayName.Append(' '); - displayName.Append('}'); - - if (operation.Name is { } name) - { - displayName.Insert(0, ' '); - displayName.Insert(0, name); - } - - displayName.Insert(0, ' '); - displayName.Insert(0, operation.Definition.Operation.ToString().ToLowerInvariant()); - - return displayName.ToString(); - } - finally - { - StringBuilderPool.Return(displayName); - } - } - - private void UpdateRootActivityName(Activity activity, string displayName) - { - var current = activity; - - while (current.Parent is not null) - { - current = current.Parent; - } - - if (current != activity) - { - current.DisplayName = CreateRootActivityName(activity, current, displayName); - } - } - - protected virtual string CreateRootActivityName( - Activity activity, - Activity root, - string displayName) - { - const string key = "originalDisplayName"; - - if (root.GetCustomProperty(key) is not string rootDisplayName) - { - rootDisplayName = root.DisplayName; - root.SetCustomProperty(key, rootDisplayName); - } - - return $"{rootDisplayName}: {displayName}"; - } - - public virtual void EnrichParseDocument(RequestContext context, Activity activity) - { - activity.DisplayName = "Parse Document"; - - if (_options.RenameRootActivity) - { - UpdateRootActivityName(activity, $"Begin {activity.DisplayName}"); - } - } - - public virtual void EnrichRequestError( - RequestContext context, - Activity activity, - Exception error) - => EnrichError(ErrorBuilder.FromException(error).Build(), activity); - - public virtual void EnrichRequestError( - RequestContext context, - Activity activity, - IError error) - => EnrichError(error, activity); - - public virtual void EnrichValidateDocument(RequestContext context, Activity activity) - { - activity.DisplayName = "Validate Document"; - - if (_options.RenameRootActivity) - { - UpdateRootActivityName(activity, $"Begin {activity.DisplayName}"); - } - - var documentInfo = context.OperationDocumentInfo; - activity.SetTag("graphql.document.id", documentInfo.Id.Value); - activity.SetTag("graphql.document.hash", documentInfo.Hash.Value); - } - - public virtual void EnrichValidationError( + public virtual void EnrichCompileOperation( RequestContext context, - Activity activity, - IError error) - => EnrichError(error, activity); - - public virtual void EnrichAnalyzeOperationComplexity(RequestContext context, Activity activity) - { - activity.DisplayName = "Analyze Operation Complexity"; - } - - public virtual void EnrichCoerceVariables(RequestContext context, Activity activity) - { - activity.DisplayName = "Coerce Variable"; - } - - public virtual void EnrichCompileOperation(RequestContext context, Activity activity) - { - activity.DisplayName = "Compile Operation"; - } + Activity activity) { } - public virtual void EnrichExecuteOperation(RequestContext context, Activity activity) - { - context.TryGetOperation(out var operation); - activity.DisplayName = - operation?.Name is { } op - ? $"Execute Operation {op}" - : "Execute Operation"; - } - - public virtual void EnrichResolveFieldValue(IMiddlewareContext context, Activity activity) - { - string path; - string hierarchy; - BuildPath(); - - var selection = context.Selection; - var coordinate = selection.Field.Coordinate; - - activity.DisplayName = path; - activity.SetTag("graphql.selection.name", selection.ResponseName); - activity.SetTag("graphql.selection.type", selection.Field.Type.Print()); - activity.SetTag("graphql.selection.path", path); - activity.SetTag("graphql.selection.hierarchy", hierarchy); - activity.SetTag("graphql.selection.field.name", coordinate.MemberName); - activity.SetTag("graphql.selection.field.coordinate", coordinate.ToString()); - activity.SetTag("graphql.selection.field.declaringType", coordinate.Name); - activity.SetTag("graphql.selection.field.isDeprecated", selection.Field.IsDeprecated); - - void BuildPath() - { - var p = StringBuilderPool.Get(); - var h = StringBuilderPool.Get(); - var index = StringBuilderPool.Get(); - - var current = context.Path; - - do - { - if (current is NamePathSegment n) - { - p.Insert(0, '/'); - h.Insert(0, '/'); - p.Insert(1, n.Name); - h.Insert(1, n.Name); - - if (index.Length > 0) - { - p.Insert(1 + n.Name.Length, index); - } - - index.Clear(); - } - - if (current is IndexerPathSegment i) - { - var number = i.Index.ToString(); - index.Insert(0, '['); - index.Insert(1, number); - index.Insert(1 + number.Length, ']'); - } - - current = current.Parent; - } while (!current.IsRoot); - - path = p.ToString(); - hierarchy = h.ToString(); - - StringBuilderPool.Return(p); - StringBuilderPool.Return(h); - StringBuilderPool.Return(index); - } - } + public virtual void EnrichResolveFieldValue( + IMiddlewareContext context, + Activity activity) { } public virtual void EnrichResolverError( - RequestContext context, - IError error, - Activity activity) - => EnrichError(error, activity); - - public virtual void EnrichResolverError( - IMiddlewareContext middlewareContext, + IMiddlewareContext context, IError error, - Activity activity) - => EnrichError(error, activity); + Activity activity) { } - public virtual void EnrichDataLoaderBatch( + public virtual void EnrichExecuteBatch( IDataLoader dataLoader, IReadOnlyList keys, - Activity activity) - where TKey : notnull - { - activity.DisplayName = $"Execute {dataLoader.GetType().Name} Batch"; - activity.SetTag("graphql.dataLoader.keys.count", keys.Count); - - if (_options.IncludeDataLoaderKeys) - { - var temp = keys.Select(t => t.ToString()).ToArray(); - activity.SetTag("graphql.dataLoader.keys", temp); - } - } - - protected virtual void EnrichError(IError error, Activity activity) - { - if (error.Exception is { } exception) - { - activity.RecordException(exception); - } + Activity activity) where TKey : notnull { } - var tags = new ActivityTagsCollection - { - new(AttributeExceptionMessage, error.Message), - new(AttributeExceptionType, error.Code ?? "GRAPHQL_ERROR") - }; + public virtual void EnrichRunBatchDispatchCoordinator( + Activity activity) { } - if (error.Path is not null) - { - tags["graphql.error.path"] = error.Path.ToString(); - } - - if (error.Locations is { Count: > 0 }) - { - tags["graphql.error.location.column"] = error.Locations[0].Column; - tags["graphql.error.location.line"] = error.Locations[0].Line; - } - - activity.AddEvent(new ActivityEvent(AttributeExceptionEventName, default, tags)); - } -} + public virtual void EnrichBatchDispatchError( + Exception exception, + Activity activity) { } -file static class SemanticConventions -{ - public const string AttributeExceptionEventName = "exception"; - public const string AttributeExceptionType = "exception.type"; - public const string AttributeExceptionMessage = "exception.message"; + public virtual void EnrichOnSubscriptionEvent( + RequestContext context, + ulong subscriptionId, + Activity activity) { } } diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/ContextKeys.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/ContextKeys.cs deleted file mode 100644 index d2771522e7a..00000000000 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/ContextKeys.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace HotChocolate.Diagnostics; - -internal static class ContextKeys -{ - public const string HttpRequestActivity = "HotChocolate.Diagnostics.HttpRequest"; - public const string ParseHttpRequestActivity = "HotChocolate.Diagnostics.ParseHttpRequest"; - public const string FormatHttpResponseActivity = "HotChocolate.Diagnostics.FormatHttpResponse"; - public const string WebSocketSessionActivity = "HotChocolate.Diagnostics.WebSocketSession"; - public const string RequestActivity = "HotChocolate.Diagnostics.Request"; - public const string ValidateActivity = "HotChocolate.Diagnostics.Validate"; - public const string ComplexityActivity = "HotChocolate.Diagnostics.AnalyzeOperationComplexity"; - public const string ResolverActivity = "HotChocolate.Diagnostics.Resolver"; -} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/Extensions/DiagnosticsRequestExecutorBuilderExtensions.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/Extensions/DiagnosticsRequestExecutorBuilderExtensions.cs index 47493fa3a0b..d3b40ac99cd 100644 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/Extensions/DiagnosticsRequestExecutorBuilderExtensions.cs +++ b/src/HotChocolate/Diagnostics/src/Diagnostics/Extensions/DiagnosticsRequestExecutorBuilderExtensions.cs @@ -80,8 +80,5 @@ public static IRequestExecutorBuilder AddInstrumentation( return builder; } - private sealed class InternalActivityEnricher( - ObjectPool stringBuilderPool, - InstrumentationOptions options) - : ActivityEnricher(stringBuilderPool, options); + private sealed class InternalActivityEnricher(InstrumentationOptions options) : ActivityEnricher(options); } diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/HotChocolate.Diagnostics.csproj b/src/HotChocolate/Diagnostics/src/Diagnostics/HotChocolate.Diagnostics.csproj index 49885604251..a1a4cf24a90 100644 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/HotChocolate.Diagnostics.csproj +++ b/src/HotChocolate/Diagnostics/src/Diagnostics/HotChocolate.Diagnostics.csproj @@ -16,6 +16,7 @@ + diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/InstrumentationOptions.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/InstrumentationOptions.cs index a28fe4fbd41..1da0a36eb94 100644 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/InstrumentationOptions.cs +++ b/src/HotChocolate/Diagnostics/src/Diagnostics/InstrumentationOptions.cs @@ -5,35 +5,18 @@ namespace HotChocolate.Diagnostics; /// /// The Hot Chocolate instrumentation options. /// -public sealed class InstrumentationOptions +public sealed class InstrumentationOptions : InstrumentationOptionsBase { - /// - /// Specifies the request detail that shall be included into the tracing activities. - /// - public RequestDetails RequestDetails { get; set; } = RequestDetails.Default; - /// /// Specifies the activity scopes that shall be instrumented. /// public ActivityScopes Scopes { get; set; } = Default; - /// - /// Specifies if the parsed document shall be included into the tracing data. - /// - public bool IncludeDocument { get; set; } - /// /// Specifies if DataLoader batch keys shall be included into the tracing data. /// public bool IncludeDataLoaderKeys { get; set; } - /// - /// Defines if the operation display name shall be included in the root activity. - /// - public bool RenameRootActivity { get; set; } - - internal bool IncludeRequestDetails => RequestDetails is not RequestDetails.None; - internal bool SkipExecuteHttpRequest => (Scopes & ExecuteHttpRequest) != ExecuteHttpRequest; internal bool SkipParseHttpRequest => (Scopes & ParseHttpRequest) != ParseHttpRequest; diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityDataLoaderDiagnosticListener.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityDataLoaderDiagnosticListener.cs index a0eeffcedd6..785291a1891 100644 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityDataLoaderDiagnosticListener.cs +++ b/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityDataLoaderDiagnosticListener.cs @@ -1,56 +1,55 @@ using System.Diagnostics; using GreenDonut; -using HotChocolate.Diagnostics.Scopes; using static HotChocolate.Diagnostics.HotChocolateActivitySource; namespace HotChocolate.Diagnostics.Listeners; -internal sealed class ActivityDataLoaderDiagnosticListener : DataLoaderDiagnosticEventListener +internal sealed class ActivityDataLoaderDiagnosticListener( + ActivityEnricher enricher, + InstrumentationOptions options) + : DataLoaderDiagnosticEventListener { - private readonly InstrumentationOptions _options; - private readonly ActivityEnricher _enricher; - - public ActivityDataLoaderDiagnosticListener( - ActivityEnricher enricher, - InstrumentationOptions options) - { - _enricher = enricher ?? throw new ArgumentNullException(nameof(enricher)); - _options = options ?? throw new ArgumentNullException(nameof(options)); - } - public override IDisposable ExecuteBatch( IDataLoader dataLoader, IReadOnlyList keys) { - if (_options.SkipDataLoaderBatch) + if (options.SkipDataLoaderBatch) { return EmptyScope; } - var activity = Source.StartActivity(); + var span = DataLoaderBatchSpan.Start(Source, dataLoader, keys, enricher); - if (activity is null) + if (span is null) { return EmptyScope; } - return new DataLoaderBatchScope(_enricher, dataLoader, keys, activity); + if (options.IncludeDataLoaderKeys) + { + var temp = keys.Select(t => t.ToString()).ToArray(); + span.Activity.SetTag(SemanticConventions.GraphQL.DataLoader.Batch.Keys, temp); + } + + return span; } public override IDisposable RunBatchDispatchCoordinator() { - var activity = Source.StartActivity("BatchCoordinator"); - activity?.DisplayName = "Coordinate DataLoader Batches"; - return activity ?? EmptyScope; + var span = DataLoaderDispatchSpan.Start(Source, enricher); + + return span ?? EmptyScope; } public override void BatchDispatchError(Exception error) { -#if NET9_0_OR_GREATER - Activity.Current?.AddException(error); -#else - Activity.Current?.SetStatus(ActivityStatusCode.Error, error.Message); -#endif + if (Activity.Current is { } activity) + { + activity.SetStatus(ActivityStatusCode.Error); + activity.AddException(error); + + enricher.EnrichBatchDispatchError(error, activity); + } } public override void BatchEvaluated(int openBatches) diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityExecutionDiagnosticListener.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityExecutionDiagnosticListener.cs index e9450965209..8c5cdb9cbea 100644 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityExecutionDiagnosticListener.cs +++ b/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityExecutionDiagnosticListener.cs @@ -1,46 +1,32 @@ using System.Diagnostics; -using HotChocolate.Diagnostics.Scopes; using HotChocolate.Execution; using HotChocolate.Execution.Instrumentation; using HotChocolate.Resolvers; using Microsoft.AspNetCore.Http; using OpenTelemetry.Trace; -using static HotChocolate.Diagnostics.ContextKeys; using static HotChocolate.Diagnostics.HotChocolateActivitySource; namespace HotChocolate.Diagnostics.Listeners; -internal sealed class ActivityExecutionDiagnosticListener : ExecutionDiagnosticEventListener +internal sealed class ActivityExecutionDiagnosticListener( + ActivityEnricher enricher, + InstrumentationOptions options) : ExecutionDiagnosticEventListener { - private readonly InstrumentationOptions _options; - private readonly ActivityEnricher _enricher; - - public ActivityExecutionDiagnosticListener( - ActivityEnricher enricher, - InstrumentationOptions options) - { - ArgumentNullException.ThrowIfNull(enricher); - ArgumentNullException.ThrowIfNull(options); - - _enricher = enricher; - _options = options; - } + private const string ResolveFieldSpanKey = "HotChocolate.Diagnostics.ResolveFieldSpan"; public override bool EnableResolveFieldValue => true; public override IDisposable ExecuteRequest(RequestContext context) { - Activity? activity = null; + Activity? httpContextActivity = null; - if (_options.SkipExecuteRequest) + if (options.SkipExecuteRequest) { - if (!_options.SkipExecuteHttpRequest - && context.ContextData.TryGetValue(nameof(HttpContext), out var value) - && value is HttpContext httpContext - && httpContext.Items.TryGetValue(HttpRequestActivity, out value) - && value is not null) + if (!options.SkipExecuteHttpRequest + && context.Features.TryGet(out var httpContext) + && httpContext.Features.Get() is { } httpRequestSpan) { - activity = (Activity)value; + httpContextActivity = httpRequestSpan.Activity; } else { @@ -48,291 +34,303 @@ public override IDisposable ExecuteRequest(RequestContext context) } } - activity ??= Source.StartActivity(); + var span = httpContextActivity is not null + ? new ExecuteRequestSpan(httpContextActivity, context, options, enricher, false) + : ExecuteRequestSpan.Start(Source, context, options, enricher); - if (activity is null) + if (span is null) { return EmptyScope; } - context.ContextData[RequestActivity] = activity; + context.Features.Set(span); - return new ExecuteRequestScope(_enricher, context, activity); + return span; } - public override void RetrievedDocumentFromCache(RequestContext context) + public override void RequestError(RequestContext context, Exception error) { - if (context.ContextData.TryGetValue(RequestActivity, out var activity)) + if (context.Features.TryGet(out var span)) { - Debug.Assert(activity is not null, "The activity mustn't be null!"); - ((Activity)activity).AddEvent(new(nameof(RetrievedDocumentFromCache))); - } - } + var activity = span.Activity; - public override void RetrievedDocumentFromStorage(RequestContext context) - { - if (context.ContextData.TryGetValue(RequestActivity, out var activity)) - { - Debug.Assert(activity is not null, "The activity mustn't be null!"); - ((Activity)activity).AddEvent(new(nameof(RetrievedDocumentFromStorage))); + activity.SetStatus(ActivityStatusCode.Error); + activity.AddException(error); + + enricher.EnrichRequestError(context, error, activity); } } - public override void AddedDocumentToCache(RequestContext context) + public override void RequestError(RequestContext context, IError error) { - if (context.ContextData.TryGetValue(RequestActivity, out var activity)) + if (context.Features.TryGet(out var span)) { - Debug.Assert(activity is not null, "The activity mustn't be null!"); - ((Activity)activity).AddEvent(new(nameof(AddedDocumentToCache))); + var activity = span.Activity; + + activity.SetStatus(ActivityStatusCode.Error); + activity.AddGraphQLError(error); + + enricher.EnrichRequestError(context, error, activity); } } - public override void AddedOperationToCache(RequestContext context) + public override IDisposable ParseDocument(RequestContext context) { - if (context.ContextData.TryGetValue(RequestActivity, out var activity)) + if (options.SkipParseDocument) { - Debug.Assert(activity is not null, "The activity mustn't be null!"); - ((Activity)activity).AddEvent(new(nameof(AddedOperationToCache))); + return EmptyScope; } + + var span = ParsingSpan.Start(Source, context, enricher); + + return span ?? EmptyScope; } - public override IDisposable ParseDocument(RequestContext context) + public override IDisposable ValidateDocument(RequestContext context) { - if (_options.SkipParseDocument) + if (options.SkipValidateDocument) { return EmptyScope; } - var activity = Source.StartActivity(); + var span = ValidationSpan.Start(Source, context, enricher); - if (activity is null) + if (span is null) { return EmptyScope; } - context.ContextData[RequestActivity] = activity; + context.Features.Set(span); - return new ParseDocumentScope(_enricher, context, activity); + return span; } - public override void RequestError(RequestContext context, Exception error) + public override void ValidationErrors(RequestContext context, IReadOnlyList errors) { - if (context.ContextData.TryGetValue(RequestActivity, out var value)) + if (!context.Features.TryGet(out var span)) { - Debug.Assert(value is not null, "The activity mustn't be null!"); - - var activity = (Activity)value; - _enricher.EnrichRequestError(context, activity, error); - activity.SetStatus(Status.Error); - activity.SetStatus(ActivityStatusCode.Error); + return; } - } - public override void RequestError(RequestContext context, IError error) - { - if (context.ContextData.TryGetValue(RequestActivity, out var value)) - { - Debug.Assert(value is not null, "The activity mustn't be null!"); + var activity = span.Activity; - var activity = (Activity)value; - _enricher.EnrichRequestError(context, activity, error); - activity.SetStatus(Status.Error); - activity.SetStatus(ActivityStatusCode.Error); + activity.SetStatus(ActivityStatusCode.Error); + + foreach (var error in errors) + { + activity.AddGraphQLError(error); } + + enricher.EnrichValidationErrors(context, errors, activity); } - public override void ValidationErrors(RequestContext context, IReadOnlyList errors) + public override IDisposable AnalyzeOperationCost(RequestContext context) { - if (context.ContextData.TryGetValue(ValidateActivity, out var value)) + if (options.SkipAnalyzeComplexity) { - Debug.Assert(value is not null, "The activity mustn't be null!"); - - var activity = (Activity)value; + return EmptyScope; + } - foreach (var error in errors) - { - _enricher.EnrichValidationError(context, activity, error); - } + var span = AnalyzeOperationComplexitySpan.Start(Source, context, enricher); - activity.SetStatus(Status.Error); - activity.SetStatus(ActivityStatusCode.Error); + if (span is null) + { + return EmptyScope; } + + context.Features.Set(span); + + return span; } - public override void ResolverError(IMiddlewareContext context, IError error) + public override void OperationCost(RequestContext context, double fieldCost, double typeCost) { - if (context.LocalContextData.TryGetValue(ResolverActivity, out var localValue) - && localValue is Activity activity) + if (!context.Features.TryGet(out var span)) { - _enricher.EnrichResolverError(context, error, activity); - activity.SetStatus(Status.Error); - activity.SetStatus(ActivityStatusCode.Error); + return; } + + span.SetCost(fieldCost, typeCost); } - public override void ResolverError(RequestContext context, ISelection selection, IError error) + public override IDisposable CompileOperation(RequestContext context) { - if (context.ContextData.TryGetValue(RequestActivity, out var value)) + if (options.SkipCompileOperation) { - Debug.Assert(value is not null, "The activity mustn't be null!"); - - var activity = (Activity)value; + return EmptyScope; + } - _enricher.EnrichResolverError(context, error, activity); + var span = CompileOperationSpan.Start(Source, context, enricher); - activity.SetStatus(Status.Error); - activity.SetStatus(ActivityStatusCode.Error); - } + return span ?? EmptyScope; } - public override IDisposable ValidateDocument(RequestContext context) + public override IDisposable CoerceVariables(RequestContext context) { - if (_options.SkipValidateDocument) + if (options.SkipCoerceVariables) { return EmptyScope; } - var activity = Source.StartActivity(); - - if (activity is null) + if (!context.TryGetOperation(out var operation)) { return EmptyScope; } - context.ContextData[ValidateActivity] = activity; + var span = VariableCoercionSpan.Start( + Source, + context, + operation.Kind, + operation.Name, + enricher); - return new ValidateDocumentScope(_enricher, context, activity); + return span ?? EmptyScope; } - public override IDisposable AnalyzeOperationCost(RequestContext context) + public override IDisposable ExecuteOperation(RequestContext context) { - if (_options.SkipAnalyzeComplexity) + if (options.SkipExecuteOperation) { return EmptyScope; } - var activity = Source.StartActivity(); - - if (activity is null) + if (!context.TryGetOperation(out var operation)) { return EmptyScope; } - context.ContextData[ComplexityActivity] = activity; + var span = ExecuteOperationSpan.Start( + Source, + context, + operation.Kind, + operation.Name, + enricher); - return new AnalyzeOperationComplexityScope(_enricher, context, activity); - } - - public override void OperationCost(RequestContext context, double fieldCost, double typeCost) - { - if (context.ContextData.TryGetValue(ComplexityActivity, out var value)) - { - Debug.Assert(value is not null, "The activity mustn't be null!"); - - var activity = (Activity)value; - - var documentInfo = context.OperationDocumentInfo; - activity.SetTag("graphql.operation.id", documentInfo.Id.Value); - activity.SetTag("graphql.operation.fieldCost", fieldCost); - activity.SetTag("graphql.operation.typeCost", typeCost); - } + return span ?? EmptyScope; } - public override IDisposable CoerceVariables(RequestContext context) + public override IDisposable ResolveFieldValue(IMiddlewareContext context) { - if (_options.SkipCoerceVariables) + if (options.SkipResolveFieldValue) { return EmptyScope; } - var activity = Source.StartActivity(); + var span = ResolveFieldSpan.Start(Source, context, enricher); - if (activity is null) + if (span is null) { return EmptyScope; } - return new CoerceVariablesScope(_enricher, context, activity); + context.LocalContextData = context.LocalContextData.SetItem(ResolveFieldSpanKey, span); + + return span; } - public override IDisposable CompileOperation(RequestContext context) + public override void ResolverError(IMiddlewareContext context, IError error) { - if (_options.SkipCompileOperation) + if (context.LocalContextData.TryGetValue(ResolveFieldSpanKey, out var value) + && value is ResolveFieldSpan span) { - return EmptyScope; - } + span.Activity.SetStatus(ActivityStatusCode.Error); + span.Activity.AddGraphQLError(error); - var activity = Source.StartActivity(); + enricher.EnrichResolverError(context, error, span.Activity); + } + } - if (activity is null) + public override IDisposable ExecuteSubscription( + RequestContext context, + ulong subscriptionId) + { + if (Activity.Current is not { } currentActivity) { return EmptyScope; } - return new CompileOperationScope(_enricher, context, activity); + context.Features.Set( + new SubscriptionContextFeature + { + SubscriptionContext = currentActivity.Context + }); + + return EmptyScope; } - public override IDisposable ExecuteOperation(RequestContext context) + public override IDisposable OnSubscriptionEvent(RequestContext context, ulong subscriptionId) { - if (_options.SkipExecuteOperation) + ActivityContext? subscriptionContext = null; + + if (context.Features.TryGet(out var feature) + && feature.SubscriptionContext is { } storedSubscriptionContext) { - return EmptyScope; + subscriptionContext = storedSubscriptionContext; } - var activity = Source.StartActivity(); + var span = SubscriptionEventSpan.Start( + Source, + context, + context.TryGetOperation(out var operation) ? operation.Name : null, + subscriptionId, + subscriptionContext); - if (activity is null) + if (span is null) { return EmptyScope; } - return new ExecuteOperationScope(_enricher, context, activity); + enricher.EnrichOnSubscriptionEvent(context, subscriptionId, span.Activity); + + return span; } - public override IDisposable ExecuteStream(IOperation operation) + public override void SubscriptionEventError( + RequestContext context, + ulong subscriptionId, + Exception exception) { - var activity = Source.StartActivity(); - - if (activity is null) + if (Activity.Current is { } activity) { - return EmptyScope; + activity.SetStatus(ActivityStatusCode.Error); + activity.AddException(exception); } - - return activity; } - public override IDisposable OnSubscriptionEvent(RequestContext context, ulong subscriptionId) + public override void RetrievedDocumentFromCache(RequestContext context) { - var activity = Source.StartActivity(); - - if (activity is null) + if (context.Features.TryGet(out var span)) { - return EmptyScope; + span.Activity.AddEvent(new(nameof(RetrievedDocumentFromCache))); } - - return activity; } - public override IDisposable ResolveFieldValue(IMiddlewareContext context) + public override void RetrievedDocumentFromStorage(RequestContext context) { - if (_options.SkipResolveFieldValue) + if (context.Features.TryGet(out var span)) { - return EmptyScope; + span.Activity.AddEvent(new(nameof(RetrievedDocumentFromStorage))); } + } - var activity = Source.StartActivity(); - - if (activity is null) + public override void AddedDocumentToCache(RequestContext context) + { + if (context.Features.TryGet(out var span)) { - return EmptyScope; + span.Activity.AddEvent(new(nameof(AddedDocumentToCache))); } + } - _enricher.EnrichResolveFieldValue(context, activity); - activity.SetStatus(Status.Ok); - activity.SetStatus(ActivityStatusCode.Ok); - - context.SetLocalState(ResolverActivity, activity); + public override void AddedOperationToCache(RequestContext context) + { + if (context.Features.TryGet(out var span)) + { + span.Activity.AddEvent(new(nameof(AddedOperationToCache))); + } + } - return activity; + private sealed class SubscriptionContextFeature + { + public ActivityContext? SubscriptionContext { get; set; } } } diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityServerDiagnosticListener.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityServerDiagnosticListener.cs index 7f803428364..b10d244b12a 100644 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityServerDiagnosticListener.cs +++ b/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityServerDiagnosticListener.cs @@ -1,62 +1,50 @@ -using System.Diagnostics; using Microsoft.AspNetCore.Http; using HotChocolate.AspNetCore.Instrumentation; using HotChocolate.Execution; using HotChocolate.Language; -using OpenTelemetry.Trace; -using static HotChocolate.Diagnostics.ContextKeys; +using static HotChocolate.Diagnostics.HotChocolateActivitySource; namespace HotChocolate.Diagnostics.Listeners; -internal sealed class ActivityServerDiagnosticListener : ServerDiagnosticEventListener +internal sealed class ActivityServerDiagnosticListener( + ActivityEnricher enricher, + InstrumentationOptions options) + : ServerDiagnosticEventListener { - private readonly InstrumentationOptions _options; - private readonly ActivityEnricher _enricher; - - public ActivityServerDiagnosticListener( - ActivityEnricher enricher, - InstrumentationOptions options) - { - _enricher = enricher ?? throw new ArgumentNullException(nameof(enricher)); - _options = options ?? throw new ArgumentNullException(nameof(options)); - } - public override IDisposable ExecuteHttpRequest(HttpContext context, HttpRequestKind kind) { - if (_options.SkipExecuteHttpRequest) + if (options.SkipExecuteHttpRequest) { return EmptyScope; } - var activity = HotChocolateActivitySource.Source.StartActivity(); + var span = ExecuteHttpRequestSpan.Start(Source, context, kind, enricher, options); - if (activity is null) + if (span is null) { return EmptyScope; } - _enricher.EnrichExecuteHttpRequest(context, kind, activity); - activity.SetStatus(ActivityStatusCode.Ok); - context.Items[HttpRequestActivity] = activity; + context.Features.Set(span); - return activity; + return span; } public override void StartSingleRequest(HttpContext context, GraphQLRequest request) { - if (_options.IncludeRequestDetails - && context.Items.TryGetValue(HttpRequestActivity, out var activity)) + if (options.IncludeRequestDetails + && context.Features.Get() is { } span) { - _enricher.EnrichSingleRequest(context, request, (Activity)activity!); + span.SetSingleRequestDetails(request); } } public override void StartBatchRequest(HttpContext context, IReadOnlyList batch) { - if (_options.IncludeRequestDetails - && context.Items.TryGetValue(HttpRequestActivity, out var activity)) + if (options.IncludeRequestDetails + && context.Features.Get() is { } span) { - _enricher.EnrichBatchRequest(context, batch, (Activity)activity!); + span.SetBatchRequestDetails(batch); } } @@ -65,93 +53,65 @@ public override void StartOperationBatchRequest( GraphQLRequest request, IReadOnlyList operations) { - if (_options.IncludeRequestDetails - && context.Items.TryGetValue(HttpRequestActivity, out var activity)) + if (options.IncludeRequestDetails + && context.Features.Get() is { } span) { - _enricher.EnrichOperationBatchRequest( - context, - request, - operations, - (Activity)activity!); + span.SetOperationBatchRequestDetails(request, operations); } } public override void HttpRequestError(HttpContext context, IError error) { - if (context.Items.TryGetValue(HttpRequestActivity, out var value)) + if (context.Features.Get() is { } span) { - var activity = (Activity)value!; - _enricher.EnrichHttpRequestError(context, error, activity); - activity.SetStatus(Status.Error); + span.RecordError(error); } } public override void HttpRequestError(HttpContext context, Exception exception) { - if (context.Items.TryGetValue(HttpRequestActivity, out var value)) + if (context.Features.Get() is { } span) { - var activity = (Activity)value!; - _enricher.EnrichHttpRequestError(context, exception, activity); - activity.SetStatus(Status.Error); + span.RecordError(exception); } } public override IDisposable ParseHttpRequest(HttpContext context) { - if (_options.SkipParseHttpRequest) + if (options.SkipParseHttpRequest) { return EmptyScope; } - var activity = HotChocolateActivitySource.Source.StartActivity(); + var span = ParseHttpRequestSpan.Start(Source, context, enricher); - if (activity is null) + if (span is null) { return EmptyScope; } - _enricher.EnrichParseHttpRequest(context, activity); - activity.SetStatus(Status.Ok); - activity.SetStatus(ActivityStatusCode.Ok); - context.Items[ParseHttpRequestActivity] = activity; + context.Features.Set(span); - return activity; + return span; } public override void ParserErrors(HttpContext context, IReadOnlyList errors) { - if (context.Items.TryGetValue(ParseHttpRequestActivity, out var value)) + if (context.Features.Get() is { } span) { - var activity = (Activity)value!; - - foreach (var error in errors) - { - _enricher.EnrichParserErrors(context, error, activity); - } - - activity.SetStatus(Status.Error); - activity.SetStatus(ActivityStatusCode.Error); + span.RecordErrors(errors); } } public override IDisposable FormatHttpResponse(HttpContext context, OperationResult result) { - if (_options.SkipFormatHttpResponse) - { - return EmptyScope; - } - - var activity = HotChocolateActivitySource.Source.StartActivity(); - - if (activity is null) + if (options.SkipFormatHttpResponse) { return EmptyScope; } - _enricher.EnrichFormatHttpResponse(context, activity); - activity.SetStatus(ActivityStatusCode.Ok); - context.Items[FormatHttpResponseActivity] = activity; + var span = FormatHttpResponseSpan.Start(Source, context, enricher); - return activity; + return span ?? EmptyScope; } } diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/RequestDetails.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/RequestDetails.cs deleted file mode 100644 index 58a8e5d33e1..00000000000 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/RequestDetails.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace HotChocolate.Diagnostics; - -[Flags] -public enum RequestDetails -{ - None = 0, - Id = 1, - Hash = 2, - Operation = 4, - Variables = 8, - Extensions = 16, - Query = 32, - Default = Id | Hash | Operation | Extensions, - All = Id | Hash | Operation | Variables | Extensions | Query -} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/AnalyzeOperationComplexityScope.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/AnalyzeOperationComplexityScope.cs deleted file mode 100644 index f4845164f4b..00000000000 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/AnalyzeOperationComplexityScope.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Execution; - -namespace HotChocolate.Diagnostics.Scopes; - -internal sealed class AnalyzeOperationComplexityScope : RequestScopeBase -{ - public AnalyzeOperationComplexityScope( - ActivityEnricher enricher, - RequestContext context, - Activity activity) - : base(enricher, context, activity) - { - } - - protected override void EnrichActivity() - => Enricher.EnrichAnalyzeOperationComplexity(Context, Activity); -} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/CoerceVariablesScope.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/CoerceVariablesScope.cs deleted file mode 100644 index 258f8d78059..00000000000 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/CoerceVariablesScope.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Execution; -using OpenTelemetry.Trace; - -namespace HotChocolate.Diagnostics.Scopes; - -internal sealed class CoerceVariablesScope : RequestScopeBase -{ - public CoerceVariablesScope( - ActivityEnricher enricher, - RequestContext context, - Activity activity) - : base(enricher, context, activity) - { - } - - protected override void EnrichActivity() - => Enricher.EnrichCoerceVariables(Context, Activity); - - protected override void SetStatus() - { - if (Context.VariableValues.Length > 0) - { - Activity.SetStatus(Status.Ok); - Activity.SetStatus(ActivityStatusCode.Ok); - } - } -} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/CompileOperationScope.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/CompileOperationScope.cs deleted file mode 100644 index ed07a17dea6..00000000000 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/CompileOperationScope.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Execution; -using OpenTelemetry.Trace; - -namespace HotChocolate.Diagnostics.Scopes; - -internal sealed class CompileOperationScope : RequestScopeBase -{ - public CompileOperationScope( - ActivityEnricher enricher, - RequestContext context, - Activity activity) - : base(enricher, context, activity) - { - } - - protected override void EnrichActivity() - => Enricher.EnrichCompileOperation(Context, Activity); - - protected override void SetStatus() - { - if (Context.TryGetOperation(out _)) - { - Activity.SetStatus(Status.Ok); - Activity.SetStatus(ActivityStatusCode.Ok); - } - } -} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/DataLoaderBatchScope.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/DataLoaderBatchScope.cs deleted file mode 100644 index 245b0481fac..00000000000 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/DataLoaderBatchScope.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Diagnostics; -using GreenDonut; - -namespace HotChocolate.Diagnostics.Scopes; - -internal sealed class DataLoaderBatchScope : IDisposable where TKey : notnull -{ - private readonly ActivityEnricher _enricher; - private readonly IDataLoader _dataLoader; - private readonly IReadOnlyList _keys; - private readonly Activity _activity; - private bool _disposed; - - public DataLoaderBatchScope( - ActivityEnricher enricher, - IDataLoader dataLoader, - IReadOnlyList keys, - Activity activity) - { - _enricher = enricher; - _dataLoader = dataLoader; - _keys = keys; - _activity = activity; - } - - public void Dispose() - { - if (!_disposed) - { - _enricher.EnrichDataLoaderBatch(_dataLoader, _keys, _activity); - _activity.Dispose(); - _disposed = true; - } - } -} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/ExecuteOperationScope.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/ExecuteOperationScope.cs deleted file mode 100644 index 2c7915ad6d0..00000000000 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/ExecuteOperationScope.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Execution; -using OpenTelemetry.Trace; - -namespace HotChocolate.Diagnostics.Scopes; - -internal sealed class ExecuteOperationScope : RequestScopeBase -{ - public ExecuteOperationScope( - ActivityEnricher enricher, - RequestContext context, - Activity activity) - : base(enricher, context, activity) - { - } - - protected override void EnrichActivity() - => Enricher.EnrichExecuteOperation(Context, Activity); - - protected override void SetStatus() - { - if (Context.Result is null or OperationResult { Errors: [_, ..] }) - { - Activity.SetStatus(Status.Error); - Activity.SetStatus(ActivityStatusCode.Error); - } - else - { - Activity.SetStatus(Status.Ok); - Activity.SetStatus(ActivityStatusCode.Ok); - } - } -} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/ExecuteRequestScope.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/ExecuteRequestScope.cs deleted file mode 100644 index 32c4affad3f..00000000000 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/ExecuteRequestScope.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Execution; -using OpenTelemetry.Trace; - -namespace HotChocolate.Diagnostics.Scopes; - -internal sealed class ExecuteRequestScope : RequestScopeBase -{ - public ExecuteRequestScope( - ActivityEnricher enricher, - RequestContext context, - Activity activity) - : base(enricher, context, activity) - { - } - - protected override void EnrichActivity() - => Enricher.EnrichExecuteRequest(Context, Activity); - - protected override void SetStatus() - { - if (Context.Result is null or OperationResult { Errors: [_, ..] }) - { - Activity.SetStatus(Status.Error); - Activity.SetStatus(ActivityStatusCode.Error); - } - } -} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/ParseDocumentScope.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/ParseDocumentScope.cs deleted file mode 100644 index 3a3d26c166a..00000000000 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/ParseDocumentScope.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Execution; -using OpenTelemetry.Trace; - -namespace HotChocolate.Diagnostics.Scopes; - -internal sealed class ParseDocumentScope : RequestScopeBase -{ - public ParseDocumentScope( - ActivityEnricher enricher, - RequestContext context, - Activity activity) - : base(enricher, context, activity) - { - } - - protected override void EnrichActivity() - => Enricher.EnrichParseDocument(Context, Activity); - - protected override void SetStatus() - { - if (Context.TryGetOperationDocument(out _, out _)) - { - Activity.SetStatus(Status.Ok); - Activity.SetStatus(ActivityStatusCode.Ok); - } - } -} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/RequestScopeBase.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/RequestScopeBase.cs deleted file mode 100644 index 00e263db91a..00000000000 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/RequestScopeBase.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Execution; - -namespace HotChocolate.Diagnostics.Scopes; - -internal class RequestScopeBase : IDisposable -{ - private bool _disposed; - - protected RequestScopeBase( - ActivityEnricher enricher, - RequestContext context, - Activity activity) - { - Enricher = enricher ?? throw new ArgumentNullException(nameof(enricher)); - Context = context ?? throw new ArgumentNullException(nameof(context)); - Activity = activity ?? throw new ArgumentNullException(nameof(activity)); - } - - protected ActivityEnricher Enricher { get; } - - protected RequestContext Context { get; } - - protected Activity Activity { get; } - - protected virtual void EnrichActivity() { } - - protected virtual void SetStatus() { } - - public void Dispose() - { - if (!_disposed) - { - EnrichActivity(); - SetStatus(); - Activity.Dispose(); - _disposed = true; - } - } -} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/ValidateDocumentScope.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/ValidateDocumentScope.cs deleted file mode 100644 index 291126ba469..00000000000 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/ValidateDocumentScope.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Execution; -using OpenTelemetry.Trace; - -namespace HotChocolate.Diagnostics.Scopes; - -internal sealed class ValidateDocumentScope : RequestScopeBase -{ - public ValidateDocumentScope( - ActivityEnricher enricher, - RequestContext context, - Activity activity) - : base(enricher, context, activity) - { - } - - protected override void EnrichActivity() - => Enricher.EnrichValidateDocument(Context, Activity); - - protected override void SetStatus() - { - if (Context.IsOperationDocumentValid()) - { - Activity.SetStatus(Status.Ok); - Activity.SetStatus(ActivityStatusCode.Ok); - } - } -} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/Spans/CompileOperationSpan.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/Spans/CompileOperationSpan.cs new file mode 100644 index 00000000000..78d40a07a6b --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics/Spans/CompileOperationSpan.cs @@ -0,0 +1,42 @@ +using System.Diagnostics; +using HotChocolate.Execution; +using static HotChocolate.Diagnostics.SemanticConventions; + +namespace HotChocolate.Diagnostics; + +internal sealed class CompileOperationSpan( + Activity activity, + RequestContext context, + ActivityEnricher enricher) : SpanBase(activity) +{ + public static CompileOperationSpan? Start( + ActivitySource source, + RequestContext context, + ActivityEnricher enricher) + { + var activity = source.StartActivity("GraphQL Operation Planning"); + + if (activity is null) + { + return null; + } + + activity.SetTag(GraphQL.Processing.Type, GraphQL.Processing.TypeValues.Plan); + + activity.EnrichDocumentInfo(context.OperationDocumentInfo); + + return new CompileOperationSpan(activity, context, enricher); + } + + protected override void OnComplete() + { + if (context.TryGetOperation(out var operation)) + { + Activity.SetStatus(ActivityStatusCode.Ok); + + Activity.EnrichOperation(operation.Kind, operation.Name); + } + + enricher.EnrichCompileOperation(context, Activity); + } +} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/Spans/DataLoaderBatchSpan.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/Spans/DataLoaderBatchSpan.cs new file mode 100644 index 00000000000..108a2e35cf6 --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics/Spans/DataLoaderBatchSpan.cs @@ -0,0 +1,45 @@ +using System.Diagnostics; +using GreenDonut; +using static HotChocolate.Diagnostics.SemanticConventions; + +namespace HotChocolate.Diagnostics; + +internal sealed class DataLoaderBatchSpan( + Activity activity, + IDataLoader dataLoader, + IReadOnlyList keys, + ActivityEnricher enricher) : SpanBase(activity) where TKey : notnull +{ + public static DataLoaderBatchSpan? Start( + ActivitySource source, + IDataLoader dataLoader, + IReadOnlyList keys, + ActivityEnricher enricher) + { + var dataLoaderName = dataLoader.GetType().Name; + + var activity = source.StartActivity($"GraphQL DataLoader Batch {dataLoaderName}"); + + if (activity is null) + { + return null; + } + + activity.SetTag(GraphQL.Processing.Type, GraphQL.Processing.TypeValues.DataLoaderBatch); + + activity.SetTag(GraphQL.DataLoader.Name, dataLoaderName); + activity.SetTag(GraphQL.DataLoader.Batch.Size, keys.Count); + + return new DataLoaderBatchSpan(activity, dataLoader, keys, enricher); + } + + protected override void OnComplete() + { + if (Activity.Status != ActivityStatusCode.Error) + { + Activity.SetStatus(ActivityStatusCode.Ok); + } + + enricher.EnrichExecuteBatch(dataLoader, keys, Activity); + } +} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/Spans/DataLoaderDispatchSpan.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/Spans/DataLoaderDispatchSpan.cs new file mode 100644 index 00000000000..d7f8b1d8f9f --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics/Spans/DataLoaderDispatchSpan.cs @@ -0,0 +1,35 @@ +using System.Diagnostics; +using static HotChocolate.Diagnostics.SemanticConventions; + +namespace HotChocolate.Diagnostics; + +internal sealed class DataLoaderDispatchSpan( + Activity activity, + ActivityEnricher enricher) : SpanBase(activity) +{ + public static DataLoaderDispatchSpan? Start( + ActivitySource source, + ActivityEnricher enricher) + { + var activity = source.StartActivity("GraphQL DataLoader Dispatch"); + + if (activity is null) + { + return null; + } + + activity.SetTag(GraphQL.Processing.Type, GraphQL.Processing.TypeValues.DataLoaderDispatch); + + return new DataLoaderDispatchSpan(activity, enricher); + } + + protected override void OnComplete() + { + if (Activity.Status != ActivityStatusCode.Error) + { + Activity.SetStatus(ActivityStatusCode.Ok); + } + + enricher.EnrichRunBatchDispatchCoordinator(Activity); + } +} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/Spans/ExecuteRequestSpan.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/Spans/ExecuteRequestSpan.cs new file mode 100644 index 00000000000..8a60d10a264 --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics/Spans/ExecuteRequestSpan.cs @@ -0,0 +1,51 @@ +using System.Diagnostics; +using HotChocolate.Execution; +using HotChocolate.Language; + +namespace HotChocolate.Diagnostics; + +internal sealed class ExecuteRequestSpan( + Activity activity, + RequestContext context, + InstrumentationOptionsBase options, + ActivityEnricherBase? enricher, + bool shouldDisposeActivity) + : ExecuteRequestSpanBase(activity, context, options, enricher, shouldDisposeActivity) +{ + public static ExecuteRequestSpan? Start( + ActivitySource source, + RequestContext context, + InstrumentationOptionsBase options, + ActivityEnricherBase enricher) + { + var activity = StartActivity(source); + + if (activity is null) + { + return null; + } + + return new ExecuteRequestSpan( + activity, + context, + options, + enricher, + true); + } + + protected override bool TryGetOperationInfo( + out OperationType operationType, + out string? operationName) + { + if (Context.TryGetOperation(out var operation)) + { + operationType = operation.Kind; + operationName = operation.Name; + return true; + } + + operationType = default; + operationName = null; + return false; + } +} diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/Spans/ResolveFieldSpan.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/Spans/ResolveFieldSpan.cs new file mode 100644 index 00000000000..5efaf83d4ef --- /dev/null +++ b/src/HotChocolate/Diagnostics/src/Diagnostics/Spans/ResolveFieldSpan.cs @@ -0,0 +1,49 @@ +using System.Diagnostics; +using HotChocolate.Execution; +using HotChocolate.Resolvers; +using OpenTelemetry.Trace; +using static HotChocolate.Diagnostics.SemanticConventions; + +namespace HotChocolate.Diagnostics; + +internal sealed class ResolveFieldSpan( + Activity activity, + IMiddlewareContext context, + ActivityEnricher enricher) : SpanBase(activity) +{ + public static ResolveFieldSpan? Start( + ActivitySource source, + IMiddlewareContext context, + ActivityEnricher enricher) + { + var selection = context.Selection; + var coordinate = selection.Field.Coordinate; + + var activity = source.StartActivity(coordinate.ToString()); + + if (activity is null) + { + return null; + } + + activity.SetTag(GraphQL.Processing.Type, GraphQL.Processing.TypeValues.Resolve); + + activity.SetTag(GraphQL.Selection.Name, selection.ResponseName); + activity.SetTag(GraphQL.Selection.Path, context.Path.Print()); + activity.SetTag(GraphQL.Selection.Field.Name, coordinate.MemberName); + activity.SetTag(GraphQL.Selection.Field.Coordinate, activity.DisplayName); + activity.SetTag(GraphQL.Selection.Field.ParentType, coordinate.Name); + + return new ResolveFieldSpan(activity, context, enricher); + } + + protected override void OnComplete() + { + if (Activity.Status != ActivityStatusCode.Error) + { + Activity.SetStatus(ActivityStatusCode.Ok); + } + + enricher.EnrichResolveFieldValue(context, Activity); + } +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/ActivityDataLoaderDiagnosticListenerTests.cs b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/ActivityDataLoaderDiagnosticListenerTests.cs new file mode 100644 index 00000000000..c8c6558cd1f --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/ActivityDataLoaderDiagnosticListenerTests.cs @@ -0,0 +1,49 @@ +using static HotChocolate.Diagnostics.ActivityTestHelper; + +namespace HotChocolate.Diagnostics; + +[Collection("Instrumentation")] +public class ActivityDataLoaderDiagnosticListenerTests +{ + [Fact] + public void Run_Batch_Dispatch_Coordinator_Emits_Activity() + { + using (CaptureActivities(out var activities)) + { + var listener = CreateListener(ActivityScopes.DataLoaderBatch); + + using (listener.RunBatchDispatchCoordinator()) + { + } + + activities.MatchSnapshot(); + } + } + + [Fact] + public void Run_Batch_Dispatch_Coordinator_Tracks_Dispatch_Events() + { + using (CaptureActivities(out var activities)) + { + var listener = CreateListener(ActivityScopes.DataLoaderBatch); + + using (listener.RunBatchDispatchCoordinator()) + { + listener.BatchEvaluated(2); + listener.BatchDispatched(1); + } + + activities.MatchSnapshot(); + } + } + + private static Listeners.ActivityDataLoaderDiagnosticListener CreateListener(ActivityScopes scopes) + { + var options = new InstrumentationOptions + { + Scopes = scopes + }; + var enricher = new ActivityEnricher(options); + return new Listeners.ActivityDataLoaderDiagnosticListener(enricher, options); + } +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/ActivityExecutionDiagnosticListenerTests.cs b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/ActivityExecutionDiagnosticListenerTests.cs new file mode 100644 index 00000000000..0afb95988a2 --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/ActivityExecutionDiagnosticListenerTests.cs @@ -0,0 +1,524 @@ +using Microsoft.Extensions.DependencyInjection; +using HotChocolate.Execution; +using HotChocolate.Language; +using HotChocolate.PersistedOperations; +using HotChocolate.Subscriptions; +using HotChocolate.Types; +using static HotChocolate.Diagnostics.ActivityTestHelper; + +namespace HotChocolate.Diagnostics; + +[Collection("Instrumentation")] +public partial class ActivityExecutionDiagnosticListenerTests +{ + [Fact] + public async Task Track_Events_Of_A_Simple_Query_Default() + { + using (CaptureActivities(out var activities)) + { + // arrange & act + await new ServiceCollection() + .AddGraphQL() + .AddInstrumentation() + .AddQueryType() + .ExecuteRequestAsync("{ sayHello }"); + + // assert + activities.MatchSnapshot(); + } + } + + [Fact] + public async Task Allow_Document_To_Be_Captured() + { + using (CaptureActivities(out var activities)) + { + // arrange & act + await new ServiceCollection() + .AddGraphQL() + .AddInstrumentation(o => + { + o.Scopes = ActivityScopes.All; + o.IncludeDocument = true; + }) + .AddQueryType() + .ExecuteRequestAsync("query SayHelloOperation { sayHello }"); + + // assert + activities.MatchSnapshot(); + } + } + + [Fact] + public async Task Ensure_That_The_Validation_Activity_Has_An_Error_Status() + { + using (CaptureActivities(out var activities)) + { + // arrange & act + await new ServiceCollection() + .AddGraphQL() + .AddInstrumentation(o => + { + o.Scopes = ActivityScopes.All; + o.IncludeDocument = true; + }) + .AddQueryType() + .ExecuteRequestAsync("query SayHelloOperation { sayHello_ }"); + + // assert + activities.MatchSnapshot(); + } + } + + [Fact] + public async Task Cause_A_Resolver_Error_That_Deletes_The_Whole_Result() + { + using (CaptureActivities(out var activities)) + { + // arrange & act + await new ServiceCollection() + .AddGraphQL() + .AddInstrumentation(o => + { + o.Scopes = ActivityScopes.All; + o.IncludeDocument = true; + }) + .AddQueryType() + .ExecuteRequestAsync("query SayHelloOperation { causeFatalError }"); + + // assert + activities.MatchSnapshot(); + } + } + + [Fact] + public async Task PersistedOperation_LoadsFromStorage_DefaultScopes() + { + using (CaptureActivities(out var activities)) + { + // arrange + var storage = new InMemoryOperationDocumentStorage(); + storage.Add("sayHelloOp", "{ sayHello }"); + + var services = new ServiceCollection() + .AddGraphQL() + .AddInstrumentation() + .AddQueryType() + .UsePersistedOperationPipeline() + .ConfigureSchemaServices( + s => s.AddSingleton(storage)) + .Services + .BuildServiceProvider(); + + var executor = await services.GetRequestExecutorAsync(); + + // act + await executor.ExecuteAsync(OperationRequest.FromId("sayHelloOp")); + + // assert + activities.MatchSnapshot(); + } + } + + [Fact] + public async Task ParsingError_InvalidGraphQLDocument_ReportsErrorStatus() + { + using (CaptureActivities(out var activities)) + { + // arrange & act + await new ServiceCollection() + .AddGraphQL() + .AddInstrumentation(o => o.Scopes = ActivityScopes.All) + .AddQueryType() + .ExecuteRequestAsync("{ sayHello"); + + // assert + activities.MatchSnapshot(); + } + } + + [Fact] + public async Task ValidationError_UnknownField_ReportsErrorStatus() + { + using (CaptureActivities(out var activities)) + { + // arrange & act + await new ServiceCollection() + .AddGraphQL() + .AddInstrumentation(o => o.Scopes = ActivityScopes.All) + .AddQueryType() + .ExecuteRequestAsync("{ unknownField123 }"); + + // assert + activities.MatchSnapshot(); + } + } + + // TODO: Not sure if we want this + [Fact] + public async Task DefaultScopes_ExcludesExecuteRequestAndParseDocumentSpans() + { + using (CaptureActivities(out var activities)) + { + // arrange & act + await new ServiceCollection() + .AddGraphQL() + .AddInstrumentation() + .AddQueryType() + .ExecuteRequestAsync("{ sayHello }"); + + // assert + activities.MatchSnapshot(); + } + } + + [Fact] + public async Task AllScopes_IncludesExecuteRequestAndParseDocumentSpans() + { + using (CaptureActivities(out var activities)) + { + // arrange & act + await new ServiceCollection() + .AddGraphQL() + .AddInstrumentation(o => o.Scopes = ActivityScopes.All) + .AddQueryType() + .ExecuteRequestAsync("{ sayHello }"); + + // assert + activities.MatchSnapshot(); + } + } + + [Fact] + public async Task CustomScopes_OnlyValidateAndCompile_LimitsSpans() + { + using (CaptureActivities(out var activities)) + { + // arrange & act + await new ServiceCollection() + .AddGraphQL() + .AddInstrumentation(o => + o.Scopes = ActivityScopes.ValidateDocument | ActivityScopes.CompileOperation) + .AddQueryType() + .ExecuteRequestAsync("{ sayHello }"); + + // assert + activities.MatchSnapshot(); + } + } + + [Fact] + public async Task ResolverError_AtRootLevel_MarksOperationAsError() + { + using (CaptureActivities(out var activities)) + { + // arrange & act + await new ServiceCollection() + .AddGraphQL() + .AddInstrumentation(o => + { + o.Scopes = ActivityScopes.All; + o.IncludeDocument = true; + }) + .AddQueryType() + .ExecuteRequestAsync("{ causeFatalError }"); + + // assert + activities.MatchSnapshot(); + } + } + + [Fact] + public async Task ResolverError_DeepInTree_MarksNestedFieldAsError() + { + using (CaptureActivities(out var activities)) + { + // arrange & act + await new ServiceCollection() + .AddGraphQL() + .AddInstrumentation(o => + { + o.Scopes = ActivityScopes.All; + o.IncludeDocument = true; + }) + .AddQueryType() + .ExecuteRequestAsync( + """ + { + deep { + deeper { + deeps { + deeper { + causeFatalError + } + } + } + } + } + """); + + // assert + activities.MatchSnapshot(); + } + } + + [Fact] + public async Task ComplexityAnalysis_Enabled_RecordsCostInSpan() + { + using (CaptureActivities(out var activities)) + { + // arrange & act + await new ServiceCollection() + .AddGraphQL() + .AddInstrumentation(o => + o.Scopes = ActivityScopes.All) + .AddCostAnalyzer() + .AddQueryType() + .ExecuteRequestAsync("{ sayHello }"); + + // assert + activities.MatchSnapshot(); + } + } + + [Fact] + public async Task DataLoader_BatchExecution_RecordsBatchSpan() + { + using (CaptureActivities(out var activities)) + { + // arrange & act + await new ServiceCollection() + .AddGraphQL() + .AddInstrumentation(o => o.Scopes = ActivityScopes.All) + .AddQueryType() + .ExecuteRequestAsync("{ dataLoader(key: \"abc\") }"); + + // assert + activities.MatchSnapshot(); + } + } + + [Fact] + public async Task DataLoader_BatchExecution_With_Keys_RecordsBatchSpan() + { + using (CaptureActivities(out var activities)) + { + // arrange & act + await new ServiceCollection() + .AddGraphQL() + .AddInstrumentation(o => + { + o.Scopes = ActivityScopes.All; + o.IncludeDataLoaderKeys = true; + }) + .AddQueryType() + .ExecuteRequestAsync("{ dataLoader(key: \"abc\") }"); + + // assert + activities.MatchSnapshot(); + } + } + + [Fact] + public async Task VariableCoercion_WithAllScopes_RecordsCoercionSpan() + { + using (CaptureActivities(out var activities)) + { + // arrange & act + await new ServiceCollection() + .AddGraphQL() + .AddInstrumentation(o => o.Scopes = ActivityScopes.All) + .AddQueryType() + .ExecuteRequestAsync( + OperationRequestBuilder.New() + .SetDocument("query($name: String!) { greeting(name: $name) }") + .SetVariableValues( + new Dictionary { { "name", "World" } }) + .Build()); + + // assert + activities.MatchSnapshot(); + } + } + + [Fact] + public async Task DocumentCache_SecondExecution_RecordsCacheHitEvent() + { + // arrange + var services = new ServiceCollection() + .AddGraphQL() + .AddInstrumentation(o => o.Scopes = ActivityScopes.All) + .AddQueryType() + .Services + .BuildServiceProvider(); + + var executor = await services.GetRequestExecutorAsync(); + + var request = OperationRequestBuilder.New() + .SetDocument("{ sayHello }") + .SetDocumentHash(new OperationDocumentHash("abc", "sha256", HashFormat.Hex)) + .Build(); + + // act - execute twice so second uses cached document + await executor.ExecuteAsync(request); + + using (CaptureActivities(out var activities)) + { + await executor.ExecuteAsync(request); + + // assert + activities.MatchSnapshot(); + } + } + + [Fact] + public async Task SubscriptionEvent_Records_Subscription_Event_Span() + { + using var cts = new CancellationTokenSource(5000); + + using (CaptureActivities(out var activities)) + { + // arrange + var services = new ServiceCollection() + .AddGraphQL() + .AddInstrumentation(o => o.Scopes = ActivityScopes.All) + .AddQueryType() + .AddSubscriptionType() + .AddInMemorySubscriptions() + .Services + .BuildServiceProvider(); + + var executor = await services.GetRequestExecutorAsync(); + var sender = services.GetRequiredService(); + + await using var result = await executor.ExecuteAsync( + "subscription OnMessageSubscription { onMessage }"); + await using var responseStream = result.ExpectResponseStream(); + + var results = responseStream.ReadResultsAsync().GetAsyncEnumerator(cts.Token); + + try + { + var moveNext = results.MoveNextAsync().AsTask(); + await sender.SendAsync("OnMessage", "hello", cts.Token); + Assert.True(await moveNext); + await sender.CompleteAsync("OnMessage"); + } + finally + { + await results.DisposeAsync(); + } + + // assert + activities.MatchSnapshot(); + } + } + + [Fact] + public async Task SubscriptionEventError_Records_Subscription_Event_Error() + { + using var cts = new CancellationTokenSource(5000); + + using (CaptureActivities(out var activities)) + { + // arrange + var services = new ServiceCollection() + .AddGraphQL() + .AddInstrumentation(o => o.Scopes = ActivityScopes.All) + .AddQueryType() + .AddSubscriptionType() + .AddInMemorySubscriptions() + .Services + .BuildServiceProvider(); + + var executor = await services.GetRequestExecutorAsync(); + var sender = services.GetRequiredService(); + + await using var result = await executor.ExecuteAsync( + "subscription OnFailingMessageSubscription { onFailingMessage }"); + await using var responseStream = result.ExpectResponseStream(); + + var results = responseStream.ReadResultsAsync().GetAsyncEnumerator(cts.Token); + + try + { + var moveNext = results.MoveNextAsync().AsTask(); + await sender.SendAsync("OnFailingMessage", "hello", cts.Token); + Assert.True(await moveNext); + await sender.CompleteAsync("OnFailingMessage"); + } + finally + { + await results.DisposeAsync(); + } + + // assert + activities.MatchSnapshot(); + } + } + + public class SimpleQuery + { + public string SayHello() => "hello"; + + public string Greeting(string name) => $"Hello, {name}!"; + + public string CauseFatalError() => throw new GraphQLException("fail"); + + public Deep Deep() => new(); + + public Task DataLoader(CustomDataLoader dataLoader, string key) + => dataLoader.LoadAsync(key); + } + + public class Deep + { + public Deeper Deeper() => new(); + + public string CauseFatalError() => throw new GraphQLException("fail"); + } + + public class Deeper + { + public Deep[] Deeps() => [new Deep()]; + } + + public class SimpleSubscription + { + [Subscribe] + public string OnMessage([EventMessage] string message) => message; + + [Subscribe] + public string OnFailingMessage([EventMessage] string message) + => throw new InvalidOperationException("Subscription event failed."); + } + + private sealed class InMemoryOperationDocumentStorage : IOperationDocumentStorage + { + private readonly Dictionary _cache = []; + + public void Add(string id, string document) + => _cache[id] = Utf8GraphQLParser.Parse(document); + + public ValueTask TryReadAsync( + OperationDocumentId documentId, + CancellationToken cancellationToken = default) + { + if (_cache.TryGetValue(documentId.Value, out var document)) + { + return new ValueTask(new OperationDocument(document)); + } + + return new ValueTask(default(IOperationDocument)); + } + + public ValueTask SaveAsync( + OperationDocumentId documentId, + IOperationDocument document, + CancellationToken cancellationToken = default) + { + _cache[documentId.Value] = Utf8GraphQLParser.Parse(document.AsSpan()); + return default; + } + } +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/ServerInstrumentationTests.cs b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/ActivityServerDiagnosticListenerTests.cs similarity index 60% rename from src/HotChocolate/Diagnostics/test/Diagnostics.Tests/ServerInstrumentationTests.cs rename to src/HotChocolate/Diagnostics/test/Diagnostics.Tests/ActivityServerDiagnosticListenerTests.cs index bc07c2f38fb..f4da56a8650 100644 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/ServerInstrumentationTests.cs +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/ActivityServerDiagnosticListenerTests.cs @@ -1,17 +1,16 @@ using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using HotChocolate.AspNetCore.Tests.Utilities; +using HotChocolate.Transport.Http; using static HotChocolate.Diagnostics.ActivityTestHelper; +using OperationRequest = HotChocolate.Transport.OperationRequest; namespace HotChocolate.Diagnostics; [Collection("Instrumentation")] -public class ServerInstrumentationTests : ServerTestBase +public class ActivityServerDiagnosticListenerTests(TestServerFactory serverFactory) : ServerTestBase(serverFactory) { - public ServerInstrumentationTests(TestServerFactory serverFactory) - : base(serverFactory) - { - } + private static readonly Uri s_url = new("http://localhost:5000/graphql"); [Fact] public async Task Http_Post_SingleRequest_GetHeroName_Default() @@ -20,17 +19,18 @@ public async Task Http_Post_SingleRequest_GetHeroName_Default() { // arrange using var server = CreateInstrumentedServer(); + using var client = GraphQLHttpClient.Create(server.CreateClient()); // act - await server.PostAsync(new ClientQueryRequest - { - Query = @" + var request = new OperationRequest( + @" { hero { name } - }" - }); + }"); + using var result = await client.PostAsync(request, s_url); + await result.ReadAsResultAsync(); // assert activities.MatchSnapshot(); @@ -45,17 +45,18 @@ public async Task Http_Post_SingleRequest_GetHeroName() // arrange using var server = CreateInstrumentedServer( o => o.Scopes = ActivityScopes.All); + using var client = GraphQLHttpClient.Create(server.CreateClient()); // act - await server.PostAsync(new ClientQueryRequest - { - Query = @" + var request = new OperationRequest( + @" { hero { name } - }" - }); + }"); + using var result = await client.PostAsync(request, s_url); + await result.ReadAsResultAsync(); // assert activities.MatchSnapshot(); @@ -70,17 +71,18 @@ public async Task Http_Get_SingleRequest_GetHeroName() // arrange using var server = CreateInstrumentedServer( o => o.Scopes = ActivityScopes.All); + using var client = GraphQLHttpClient.Create(server.CreateClient()); // act - await server.GetAsync(new ClientQueryRequest - { - Query = @" + var request = new OperationRequest( + @" { hero { name } - }" - }); + }"); + using var result = await client.GetAsync(request, s_url); + await result.ReadAsResultAsync(); // assert activities.MatchSnapshot(); @@ -88,25 +90,26 @@ await server.GetAsync(new ClientQueryRequest } [Fact] - public async Task Http_Post_variables_are_not_automatically_added_to_activities() + public async Task Http_Post_Variables_Are_Not_Automatically_Added_To_Activities() { using (CaptureActivities(out var activities)) { // arrange using var server = CreateInstrumentedServer( o => o.Scopes = ActivityScopes.All); + using var client = GraphQLHttpClient.Create(server.CreateClient()); // act - await server.PostAsync(new ClientQueryRequest - { - Query = @" + var request = new OperationRequest( + query: @" query ($episode: Episode!) { hero(episode: $episode) { name } }", - Variables = new Dictionary { { "episode", "NEW_HOPE" } } - }); + variables: new Dictionary { { "episode", "NEW_HOPE" } }); + using var result = await client.PostAsync(request, s_url); + await result.ReadAsResultAsync(); // assert activities.MatchSnapshot(); @@ -114,7 +117,7 @@ await server.PostAsync(new ClientQueryRequest } [Fact] - public async Task Http_Post_add_variables_to_http_activity() + public async Task Http_Post_Add_Variables_To_Http_Activity() { using (CaptureActivities(out var activities)) { @@ -125,48 +128,19 @@ public async Task Http_Post_add_variables_to_http_activity() o.Scopes = ActivityScopes.All; o.RequestDetails = RequestDetails.Default | RequestDetails.Variables; }); + using var client = GraphQLHttpClient.Create(server.CreateClient()); // act - await server.PostAsync(new ClientQueryRequest - { - Query = @" - query ($episode: Episode!) { - hero(episode: $episode) { - name - } - }", - Variables = new Dictionary { { "episode", "NEW_HOPE" } } - }); - - // assert - activities.MatchSnapshot(); - } - } - - [Fact] - public async Task Http_Post_add_query_to_http_activity() - { - using (CaptureActivities(out var activities)) - { - // arrange - using var server = CreateInstrumentedServer( - o => - { - o.Scopes = ActivityScopes.All; - o.RequestDetails = RequestDetails.Default | RequestDetails.Operation; - }); - - // act - await server.PostAsync(new ClientQueryRequest - { - Query = @" + var request = new OperationRequest( + query: @" query ($episode: Episode!) { hero(episode: $episode) { name } }", - Variables = new Dictionary { { "episode", "NEW_HOPE" } } - }); + variables: new Dictionary { { "episode", "NEW_HOPE" } }); + using var result = await client.PostAsync(request, s_url); + await result.ReadAsResultAsync(); // assert activities.MatchSnapshot(); @@ -174,26 +148,27 @@ await server.PostAsync(new ClientQueryRequest } [Fact] - public async Task Http_Post_with_extensions_map() + public async Task Http_Post_With_Extensions_Map() { using (CaptureActivities(out var activities)) { // arrange using var server = CreateInstrumentedServer( o => o.Scopes = ActivityScopes.All); + using var client = GraphQLHttpClient.Create(server.CreateClient()); // act - await server.PostAsync(new ClientQueryRequest - { - Query = @" + var request = new OperationRequest( + query: @" query ($episode: Episode!) { hero(episode: $episode) { name } }", - Variables = new Dictionary { { "episode", "NEW_HOPE" } }, - Extensions = new Dictionary { { "test", "abc" } } - }); + variables: new Dictionary { { "episode", "NEW_HOPE" } }, + extensions: new Dictionary { { "test", "abc" } }); + using var result = await client.PostAsync(request, s_url); + await result.ReadAsResultAsync(); // assert activities.MatchSnapshot(); @@ -201,7 +176,7 @@ await server.PostAsync(new ClientQueryRequest } [Fact] - public async Task Http_Get_SDL_download() + public async Task Http_Get_SDL_Download() { using (CaptureActivities(out var activities)) { @@ -222,7 +197,7 @@ public async Task Http_Get_SDL_download() } [Fact] - public async Task Http_Post_capture_deferred_response() + public async Task Http_Post_Capture_Deferred_Response() { using (CaptureActivities(out var activities)) { @@ -251,7 +226,7 @@ ... on Droid @defer(label: "my_id") { } [Fact] - public async Task Http_Post_ensure_list_path_is_correctly_built() + public async Task Http_Post_Ensure_List_Path_Is_Correctly_Built() { using (CaptureActivities(out var activities)) { @@ -286,7 +261,7 @@ await server.PostRawAsync(new ClientQueryRequest } [Fact] - public async Task Http_Post_parser_error() + public async Task Http_Post_Parser_Error() { using (CaptureActivities(out var activities)) { @@ -322,7 +297,7 @@ await server.PostRawAsync(new ClientQueryRequest } [Fact] - public async Task Parsing_error_when_rename_root_is_activated() + public async Task RequestDetails_None_ExcludesAllDetails() { using (CaptureActivities(out var activities)) { @@ -331,18 +306,54 @@ public async Task Parsing_error_when_rename_root_is_activated() o => { o.Scopes = ActivityScopes.All; - o.RenameRootActivity = true; + o.RequestDetails = RequestDetails.None; }); + using var client = GraphQLHttpClient.Create(server.CreateClient()); // act - await server.PostRawAsync(new ClientQueryRequest - { - // lang=text - Query = @" + var request = new OperationRequest( + query: @" + query GetHero($episode: Episode!) { + hero(episode: $episode) { + name + } + }", + variables: new Dictionary { { "episode", "NEW_HOPE" } }, + extensions: new Dictionary { { "test", "abc" } }); + using var result = await client.PostAsync(request, s_url); + await result.ReadAsResultAsync(); + + // assert + activities.MatchSnapshot(); + } + } + + [Fact] + public async Task RequestDetails_All_IncludesAllDetails() + { + using (CaptureActivities(out var activities)) + { + // arrange + using var server = CreateInstrumentedServer( + o => { - 1 - }" - }); + o.Scopes = ActivityScopes.All; + o.RequestDetails = RequestDetails.All; + }); + using var client = GraphQLHttpClient.Create(server.CreateClient()); + + // act + var request = new OperationRequest( + query: @" + query GetHero($episode: Episode!) { + hero(episode: $episode) { + name + } + }", + variables: new Dictionary { { "episode", "NEW_HOPE" } }, + extensions: new Dictionary { { "test", "abc" } }); + using var result = await client.PostAsync(request, s_url); + await result.ReadAsResultAsync(); // assert activities.MatchSnapshot(); @@ -350,7 +361,7 @@ await server.PostRawAsync(new ClientQueryRequest } [Fact] - public async Task Validation_error_when_rename_root_is_activated() + public async Task RequestDetails_DocumentOnly_IncludesDocumentTag() { using (CaptureActivities(out var activities)) { @@ -359,17 +370,47 @@ public async Task Validation_error_when_rename_root_is_activated() o => { o.Scopes = ActivityScopes.All; - o.RenameRootActivity = true; + o.RequestDetails = RequestDetails.Document; }); + using var client = GraphQLHttpClient.Create(server.CreateClient()); // act - await server.PostRawAsync(new ClientQueryRequest - { - Query = @" + var request = new OperationRequest( + @" { - abc - }" - }); + hero { + name + } + }"); + using var result = await client.PostAsync(request, s_url); + await result.ReadAsResultAsync(); + + // assert + activities.MatchSnapshot(); + } + } + + [Fact] + public async Task RequestDetails_Default_IncludesIdHashOperationNameExtensions() + { + using (CaptureActivities(out var activities)) + { + // arrange + using var server = CreateInstrumentedServer( + o => o.Scopes = ActivityScopes.All); + using var client = GraphQLHttpClient.Create(server.CreateClient()); + + // act + var request = new OperationRequest( + query: @" + query GetHero { + hero { + name + } + }", + extensions: new Dictionary { { "test", "abc" } }); + using var result = await client.PostAsync(request, s_url); + await result.ReadAsResultAsync(); // assert activities.MatchSnapshot(); diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/ActivityTestHelper.cs b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/ActivityTestHelper.cs index 783ca73a4fc..97bd9804ec2 100644 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/ActivityTestHelper.cs +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/ActivityTestHelper.cs @@ -1,16 +1,23 @@ using System.Diagnostics; +using System.Text.RegularExpressions; using HotChocolate.Utilities; namespace HotChocolate.Diagnostics; -public static class ActivityTestHelper +public static partial class ActivityTestHelper { + [GeneratedRegex(@" in (?.+?):line (?\d+)", RegexOptions.CultureInvariant)] + private static partial Regex StackTracePathRegex(); + [GeneratedRegex(@"lambda_method\d+", RegexOptions.CultureInvariant)] + private static partial Regex LambdaMethodRegex(); + public static IDisposable CaptureActivities(out object activities) { var sync = new object(); var listener = new ActivityListener(); var root = new OrderedDictionary(); var lookup = new Dictionary>(); + var spanLookup = new Dictionary>(); Activity rootActivity = null!; listener.ShouldListenTo = source => source.Name.EqualsOrdinal("HotChocolate.Diagnostics"); @@ -31,6 +38,17 @@ public static IDisposable CaptureActivities(out object activities) { RegisterActivity(a, parentData); lookup[a] = (OrderedDictionary)a.GetCustomProperty("test.data")!; + spanLookup[a.SpanId] = (OrderedDictionary)a.GetCustomProperty("test.data")!; + return; + } + + if (a.Parent is null + && a.ParentSpanId != default + && spanLookup.TryGetValue(a.ParentSpanId, out parentData)) + { + RegisterActivity(a, parentData); + lookup[a] = (OrderedDictionary)a.GetCustomProperty("test.data")!; + spanLookup[a.SpanId] = (OrderedDictionary)a.GetCustomProperty("test.data")!; } } }; @@ -42,6 +60,7 @@ public static IDisposable CaptureActivities(out object activities) rootActivity = HotChocolateActivitySource.Source.StartActivity()!; rootActivity.SetCustomProperty("test.data", root); lookup[rootActivity] = root; + spanLookup[rootActivity.SpanId] = root; activities = root; return new Session(rootActivity, listener); @@ -75,8 +94,44 @@ private static void SerializeActivity(Activity activity) data["OperationName"] = activity.OperationName; data["DisplayName"] = activity.DisplayName; data["Status"] = activity.Status; - data["tags"] = activity.Tags; - data["event"] = activity.Events.Select(t => new { t.Name, t.Tags }); + data["tags"] = activity.TagObjects; + data["event"] = activity.Events.Select(t => new + { + t.Name, + Tags = ScrubEventTags(t.Tags) + }); + } + + private static IEnumerable> ScrubEventTags( + IEnumerable>? tags) + { + if (tags is null) + { + yield break; + } + + foreach (var tag in tags) + { + if (tag.Value is string stackTrace + && (tag.Key.Equals("exception.stacktrace", StringComparison.Ordinal) + || tag.Key.EndsWith(".stacktrace", StringComparison.Ordinal))) + { + var scrubbedStackTrace = StackTracePathRegex().Replace(stackTrace, match => + { + var fileName = System.IO.Path.GetFileName(match.Groups["path"].Value); + var lineNumber = match.Groups["line"].Value; + return $" in {fileName}:line {lineNumber}"; + }); + + yield return new KeyValuePair( + tag.Key, + LambdaMethodRegex().Replace(scrubbedStackTrace, "lambda_method")); + } + else + { + yield return tag; + } + } } private sealed class Session : IDisposable diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/CustomDataLoader.cs b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/CustomDataLoader.cs index 06d8ebdf422..255914f2b0e 100644 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/CustomDataLoader.cs +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/CustomDataLoader.cs @@ -2,7 +2,7 @@ namespace HotChocolate.Diagnostics; -public partial class QueryInstrumentationTests +public partial class ActivityExecutionDiagnosticListenerTests { public class CustomDataLoader(IBatchScheduler batchScheduler, DataLoaderOptions options) : BatchDataLoader(batchScheduler, options) diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/HotChocolate.Diagnostics.Tests.csproj b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/HotChocolate.Diagnostics.Tests.csproj index f3d92a3b4f1..2f7b559fa8a 100644 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/HotChocolate.Diagnostics.Tests.csproj +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/HotChocolate.Diagnostics.Tests.csproj @@ -8,6 +8,7 @@ + diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/QueryInstrumentationTests.cs b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/QueryInstrumentationTests.cs deleted file mode 100644 index f2c4475cb3d..00000000000 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/QueryInstrumentationTests.cs +++ /dev/null @@ -1,364 +0,0 @@ -using System.Diagnostics; -using Microsoft.Extensions.DependencyInjection; -using HotChocolate.Execution; -using static HotChocolate.Diagnostics.ActivityTestHelper; - -namespace HotChocolate.Diagnostics; - -[Collection("Instrumentation")] -public partial class QueryInstrumentationTests -{ - [Fact] - public async Task Track_events_of_a_simple_query_default() - { - using (CaptureActivities(out var activities)) - { - // arrange & act - await new ServiceCollection() - .AddGraphQL() - .AddInstrumentation() - .AddQueryType() - .ExecuteRequestAsync("{ sayHello }"); - - // assert - activities.MatchSnapshot(); - } - } - - [Fact(Skip = "This test is flaky with the new DL batching.")] - public async Task Track_data_loader_events() - { - using (CaptureActivities(out var activities)) - { - // arrange & act - await new ServiceCollection() - .AddGraphQL() - .AddInstrumentation() - .AddQueryType() - .ExecuteRequestAsync("{ dataLoader(key: \"abc\") }"); - - // assert - activities.MatchSnapshot(); - } - } - - [Fact(Skip = "This test is flaky with the new DL batching.")] - public async Task Track_data_loader_events_with_keys() - { - using (CaptureActivities(out var activities)) - { - // arrange & act - await new ServiceCollection() - .AddGraphQL() - .AddInstrumentation(o => o.IncludeDataLoaderKeys = true) - .AddQueryType() - .ExecuteRequestAsync("{ dataLoader(key: \"abc\") }"); - - // assert - activities.MatchSnapshot(); - } - } - - [Fact] - public async Task Track_events_of_a_simple_query_default_rename_root() - { - using (CaptureActivities(out _)) - { - // arrange & act - await new ServiceCollection() - .AddGraphQL() - .AddInstrumentation(o => - { - o.RenameRootActivity = true; - o.Scopes = ActivityScopes.All; - }) - .AddQueryType() - .ExecuteRequestAsync("{ sayHello }"); - - // assert - Assert.Equal("CaptureActivities: query { sayHello }", Activity.Current!.DisplayName); - } - } - - [Fact] - public async Task Parsing_error_when_rename_root_is_activated() - { - using (CaptureActivities(out _)) - { - // arrange & act - await new ServiceCollection() - .AddGraphQL() - .AddInstrumentation(o => - { - o.RenameRootActivity = true; - o.Scopes = ActivityScopes.All; - }) - .AddQueryType() - .ExecuteRequestAsync("{ sayHello"); - - // assert - Assert.Equal("CaptureActivities: Begin Parse Document", Activity.Current!.DisplayName); - } - } - - [Fact] - public async Task Validation_error_when_rename_root_is_activated() - { - using (CaptureActivities(out _)) - { - // arrange & act - await new ServiceCollection() - .AddGraphQL() - .AddInstrumentation(o => - { - o.RenameRootActivity = true; - o.Scopes = ActivityScopes.All; - }) - .AddQueryType() - .ExecuteRequestAsync("{ abc123 }"); - - // assert - Assert.Equal("CaptureActivities: Begin Validate Document", - Activity.Current!.DisplayName); - } - } - - [Fact] - public async Task Create_operation_display_name_with_1_field() - { - using (CaptureActivities(out var activities)) - { - // arrange & act - await new ServiceCollection() - .AddGraphQL() - .AddInstrumentation(o => - { - o.RenameRootActivity = true; - o.Scopes = ActivityScopes.All; - }) - .AddQueryType() - .ExecuteRequestAsync("{ a: sayHello }"); - - // assert - activities.MatchSnapshot(); - } - } - - [Fact] - public async Task Create_operation_display_name_with_1_field_and_op() - { - using (CaptureActivities(out var activities)) - { - // arrange & act - await new ServiceCollection() - .AddGraphQL() - .AddInstrumentation(o => - { - o.RenameRootActivity = true; - o.Scopes = ActivityScopes.All; - }) - .AddQueryType() - .ExecuteRequestAsync("query GetA { a: sayHello }"); - - // assert - activities.MatchSnapshot(); - } - } - - [Fact] - public async Task Create_operation_display_name_with_3_field() - { - using (CaptureActivities(out var activities)) - { - // arrange & act - await new ServiceCollection() - .AddGraphQL() - .AddInstrumentation(o => - { - o.RenameRootActivity = true; - o.Scopes = ActivityScopes.All; - }) - .AddQueryType() - .ExecuteRequestAsync("{ a: sayHello b: sayHello c: sayHello }"); - - // assert - activities.MatchSnapshot(); - } - } - - [Fact] - public async Task Create_operation_display_name_with_4_field() - { - using (CaptureActivities(out var activities)) - { - // arrange & act - await new ServiceCollection() - .AddGraphQL() - .AddInstrumentation(o => - { - o.RenameRootActivity = true; - o.Scopes = ActivityScopes.All; - }) - .AddQueryType() - .ExecuteRequestAsync("{ a: sayHello b: sayHello c: sayHello d: sayHello }"); - - // assert - activities.MatchSnapshot(); - } - } - - [Fact] - public async Task Track_events_of_a_simple_query_detailed() - { - using (CaptureActivities(out var activities)) - { - // arrange & act - await new ServiceCollection() - .AddGraphQL() - .AddInstrumentation(o => o.Scopes = ActivityScopes.All) - .AddQueryType() - .ExecuteRequestAsync("{ sayHello }"); - - // assert - activities.MatchSnapshot(); - } - } - - [Fact] - public async Task Ensure_operation_name_is_used_as_request_name() - { - using (CaptureActivities(out var activities)) - { - // arrange & act - await new ServiceCollection() - .AddGraphQL() - .AddInstrumentation(o => o.Scopes = ActivityScopes.All) - .AddQueryType() - .ExecuteRequestAsync("query SayHelloOperation { sayHello }"); - - // assert - activities.MatchSnapshot(); - } - } - - [Fact] - public async Task Allow_document_to_be_captured() - { - using (CaptureActivities(out var activities)) - { - // arrange & act - await new ServiceCollection() - .AddGraphQL() - .AddInstrumentation(o => - { - o.Scopes = ActivityScopes.All; - o.IncludeDocument = true; - }) - .AddQueryType() - .ExecuteRequestAsync("query SayHelloOperation { sayHello }"); - - // assert - activities.MatchSnapshot(); - } - } - - [Fact] - public async Task Ensure_that_the_validation_activity_has_an_error_status() - { - using (CaptureActivities(out var activities)) - { - // arrange & act - await new ServiceCollection() - .AddGraphQL() - .AddInstrumentation(o => - { - o.Scopes = ActivityScopes.All; - o.IncludeDocument = true; - }) - .AddQueryType() - .ExecuteRequestAsync("query SayHelloOperation { sayHello_ }"); - - // assert - activities.MatchSnapshot(); - } - } - - [Fact] - public async Task Cause_a_resolver_error_that_deletes_the_whole_result() - { - using (CaptureActivities(out var activities)) - { - // arrange & act - await new ServiceCollection() - .AddGraphQL() - .AddInstrumentation(o => - { - o.Scopes = ActivityScopes.All; - o.IncludeDocument = true; - }) - .AddQueryType() - .ExecuteRequestAsync("query SayHelloOperation { causeFatalError }"); - - // assert - activities.MatchSnapshot(); - } - } - - [Fact] - public async Task Cause_a_resolver_error_that_deletes_the_whole_result_deep() - { - using (CaptureActivities(out var activities)) - { - // arrange & act - await new ServiceCollection() - .AddGraphQL() - .AddInstrumentation(o => - { - o.Scopes = ActivityScopes.All; - o.IncludeDocument = true; - }) - .AddQueryType() - .ExecuteRequestAsync( - """ - query SayHelloOperation { - deep { - deeper { - deeps { - deeper { - causeFatalError - } - } - } - } - } - """); - - // assert - activities.MatchSnapshot(); - } - } - - public class SimpleQuery - { - public string SayHello() => "hello"; - - public string CauseFatalError() => throw new GraphQLException("fail"); - - public Deep Deep() => new(); - - public Task DataLoader(CustomDataLoader dataLoader, string key) - => dataLoader.LoadAsync(key); - } - - public class Deep - { - public Deeper Deeper() => new(); - - public string CauseFatalError() => throw new GraphQLException("fail"); - } - - public class Deeper - { - public Deep[] Deeps() => [new Deep()]; - } -} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityDataLoaderDiagnosticListenerTests.Run_Batch_Dispatch_Coordinator_Emits_Activity.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityDataLoaderDiagnosticListenerTests.Run_Batch_Dispatch_Coordinator_Emits_Activity.snap new file mode 100644 index 00000000000..a7bc715d1b7 --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityDataLoaderDiagnosticListenerTests.Run_Batch_Dispatch_Coordinator_Emits_Activity.snap @@ -0,0 +1,16 @@ +{ + "activities": [ + { + "OperationName": "GraphQL DataLoader Dispatch", + "DisplayName": "GraphQL DataLoader Dispatch", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "dataloader_dispatch" + } + ], + "event": [] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityDataLoaderDiagnosticListenerTests.Run_Batch_Dispatch_Coordinator_Tracks_Dispatch_Events.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityDataLoaderDiagnosticListenerTests.Run_Batch_Dispatch_Coordinator_Tracks_Dispatch_Events.snap new file mode 100644 index 00000000000..f835ed1a85a --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityDataLoaderDiagnosticListenerTests.Run_Batch_Dispatch_Coordinator_Tracks_Dispatch_Events.snap @@ -0,0 +1,35 @@ +{ + "activities": [ + { + "OperationName": "GraphQL DataLoader Dispatch", + "DisplayName": "GraphQL DataLoader Dispatch", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "dataloader_dispatch" + } + ], + "event": [ + { + "Name": "BatchEvaluated", + "Tags": [ + { + "Key": "openBatches", + "Value": 2 + } + ] + }, + { + "Name": "BatchDispatched", + "Tags": [ + { + "Key": "dispatchedBatches", + "Value": 1 + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.AllScopes_IncludesExecuteRequestAndParseDocumentSpans.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.AllScopes_IncludesExecuteRequestAndParseDocumentSpans.snap new file mode 100644 index 00000000000..49f7e009662 --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.AllScopes_IncludesExecuteRequestAndParseDocumentSpans.snap @@ -0,0 +1,133 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [ + { + "Name": "AddedOperationToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [], + "activities": [ + { + "OperationName": "SimpleQuery.sayHello", + "DisplayName": "SimpleQuery.sayHello", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "resolve" + }, + { + "Key": "graphql.selection.name", + "Value": "sayHello" + }, + { + "Key": "graphql.selection.path", + "Value": "sayHello" + }, + { + "Key": "graphql.selection.field.name", + "Value": "sayHello" + }, + { + "Key": "graphql.selection.field.coordinate", + "Value": "SimpleQuery.sayHello" + }, + { + "Key": "graphql.selection.field.parent_type", + "Value": "SimpleQuery" + } + ], + "event": [] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.Allow_Document_To_Be_Captured.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.Allow_Document_To_Be_Captured.snap new file mode 100644 index 00000000000..1a00249e432 --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.Allow_Document_To_Be_Captured.snap @@ -0,0 +1,149 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "SayHelloOperation" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:6af18618ae20c266f6ffc352b78cb69b" + }, + { + "Key": "graphql.document.body", + "Value": "query SayHelloOperation {\n sayHello\n}" + } + ], + "event": [ + { + "Name": "AddedOperationToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:6af18618ae20c266f6ffc352b78cb69b" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:6af18618ae20c266f6ffc352b78cb69b" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:6af18618ae20c266f6ffc352b78cb69b" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "SayHelloOperation" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "SayHelloOperation" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:6af18618ae20c266f6ffc352b78cb69b" + } + ], + "event": [], + "activities": [ + { + "OperationName": "SimpleQuery.sayHello", + "DisplayName": "SimpleQuery.sayHello", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "resolve" + }, + { + "Key": "graphql.selection.name", + "Value": "sayHello" + }, + { + "Key": "graphql.selection.path", + "Value": "sayHello" + }, + { + "Key": "graphql.selection.field.name", + "Value": "sayHello" + }, + { + "Key": "graphql.selection.field.coordinate", + "Value": "SimpleQuery.sayHello" + }, + { + "Key": "graphql.selection.field.parent_type", + "Value": "SimpleQuery" + } + ], + "event": [] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.Cause_A_Resolver_Error_That_Deletes_The_Whole_Result.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.Cause_A_Resolver_Error_That_Deletes_The_Whole_Result.snap new file mode 100644 index 00000000000..6b24042cac8 --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.Cause_A_Resolver_Error_That_Deletes_The_Whole_Result.snap @@ -0,0 +1,163 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Error", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "SayHelloOperation" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:851fb754d9ba6b5cc5a55ebcbea2621d" + }, + { + "Key": "graphql.document.body", + "Value": "query SayHelloOperation {\n causeFatalError\n}" + } + ], + "event": [ + { + "Name": "AddedOperationToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:851fb754d9ba6b5cc5a55ebcbea2621d" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:851fb754d9ba6b5cc5a55ebcbea2621d" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:851fb754d9ba6b5cc5a55ebcbea2621d" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "SayHelloOperation" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Error", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "SayHelloOperation" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:851fb754d9ba6b5cc5a55ebcbea2621d" + } + ], + "event": [], + "activities": [ + { + "OperationName": "SimpleQuery.causeFatalError", + "DisplayName": "SimpleQuery.causeFatalError", + "Status": "Error", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "resolve" + }, + { + "Key": "graphql.selection.name", + "Value": "causeFatalError" + }, + { + "Key": "graphql.selection.path", + "Value": "causeFatalError" + }, + { + "Key": "graphql.selection.field.name", + "Value": "causeFatalError" + }, + { + "Key": "graphql.selection.field.coordinate", + "Value": "SimpleQuery.causeFatalError" + }, + { + "Key": "graphql.selection.field.parent_type", + "Value": "SimpleQuery" + } + ], + "event": [ + { + "Name": "exception", + "Tags": [ + { + "Key": "graphql.error.message", + "Value": "fail" + }, + { + "Key": "graphql.error.path", + "Value": "causeFatalError" + } + ] + } + ] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.ComplexityAnalysis_Enabled_RecordsCostInSpan.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.ComplexityAnalysis_Enabled_RecordsCostInSpan.snap new file mode 100644 index 00000000000..18b75c1afa7 --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.ComplexityAnalysis_Enabled_RecordsCostInSpan.snap @@ -0,0 +1,153 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [ + { + "Name": "AddedOperationToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Complexity Analysis", + "DisplayName": "GraphQL Complexity Analysis", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.fieldCost", + "Value": 0.0 + }, + { + "Key": "graphql.operation.typeCost", + "Value": 1.0 + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [], + "activities": [ + { + "OperationName": "SimpleQuery.sayHello", + "DisplayName": "SimpleQuery.sayHello", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "resolve" + }, + { + "Key": "graphql.selection.name", + "Value": "sayHello" + }, + { + "Key": "graphql.selection.path", + "Value": "sayHello" + }, + { + "Key": "graphql.selection.field.name", + "Value": "sayHello" + }, + { + "Key": "graphql.selection.field.coordinate", + "Value": "SimpleQuery.sayHello" + }, + { + "Key": "graphql.selection.field.parent_type", + "Value": "SimpleQuery" + } + ], + "event": [] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.CustomScopes_OnlyValidateAndCompile_LimitsSpans.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.CustomScopes_OnlyValidateAndCompile_LimitsSpans.snap new file mode 100644 index 00000000000..6e5564554ca --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.CustomScopes_OnlyValidateAndCompile_LimitsSpans.snap @@ -0,0 +1,40 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.DataLoader_BatchExecution_RecordsBatchSpan.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.DataLoader_BatchExecution_RecordsBatchSpan.snap new file mode 100644 index 00000000000..3a1922dc668 --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.DataLoader_BatchExecution_RecordsBatchSpan.snap @@ -0,0 +1,186 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:9b20745108c8de5afccc35cd56ead9fc" + } + ], + "event": [ + { + "Name": "AddedOperationToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:9b20745108c8de5afccc35cd56ead9fc" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:9b20745108c8de5afccc35cd56ead9fc" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:9b20745108c8de5afccc35cd56ead9fc" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:9b20745108c8de5afccc35cd56ead9fc" + } + ], + "event": [], + "activities": [ + { + "OperationName": "SimpleQuery.dataLoader", + "DisplayName": "SimpleQuery.dataLoader", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "resolve" + }, + { + "Key": "graphql.selection.name", + "Value": "dataLoader" + }, + { + "Key": "graphql.selection.path", + "Value": "dataLoader" + }, + { + "Key": "graphql.selection.field.name", + "Value": "dataLoader" + }, + { + "Key": "graphql.selection.field.coordinate", + "Value": "SimpleQuery.dataLoader" + }, + { + "Key": "graphql.selection.field.parent_type", + "Value": "SimpleQuery" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL DataLoader Dispatch", + "DisplayName": "GraphQL DataLoader Dispatch", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "dataloader_dispatch" + } + ], + "event": [ + { + "Name": "BatchEvaluated", + "Tags": [ + { + "Key": "openBatches", + "Value": 1 + } + ] + }, + { + "Name": "BatchDispatched", + "Tags": [ + { + "Key": "dispatchedBatches", + "Value": 1 + } + ] + } + ], + "activities": [ + { + "OperationName": "GraphQL DataLoader Batch CustomDataLoader", + "DisplayName": "GraphQL DataLoader Batch CustomDataLoader", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "dataloader_batch" + }, + { + "Key": "graphql.dataloader.name", + "Value": "CustomDataLoader" + }, + { + "Key": "graphql.dataloader.batch.size", + "Value": 1 + } + ], + "event": [] + } + ] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.DataLoader_BatchExecution_With_Keys_RecordsBatchSpan.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.DataLoader_BatchExecution_With_Keys_RecordsBatchSpan.snap new file mode 100644 index 00000000000..f36210d5447 --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.DataLoader_BatchExecution_With_Keys_RecordsBatchSpan.snap @@ -0,0 +1,192 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:9b20745108c8de5afccc35cd56ead9fc" + } + ], + "event": [ + { + "Name": "AddedOperationToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:9b20745108c8de5afccc35cd56ead9fc" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:9b20745108c8de5afccc35cd56ead9fc" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:9b20745108c8de5afccc35cd56ead9fc" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:9b20745108c8de5afccc35cd56ead9fc" + } + ], + "event": [], + "activities": [ + { + "OperationName": "SimpleQuery.dataLoader", + "DisplayName": "SimpleQuery.dataLoader", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "resolve" + }, + { + "Key": "graphql.selection.name", + "Value": "dataLoader" + }, + { + "Key": "graphql.selection.path", + "Value": "dataLoader" + }, + { + "Key": "graphql.selection.field.name", + "Value": "dataLoader" + }, + { + "Key": "graphql.selection.field.coordinate", + "Value": "SimpleQuery.dataLoader" + }, + { + "Key": "graphql.selection.field.parent_type", + "Value": "SimpleQuery" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL DataLoader Dispatch", + "DisplayName": "GraphQL DataLoader Dispatch", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "dataloader_dispatch" + } + ], + "event": [ + { + "Name": "BatchEvaluated", + "Tags": [ + { + "Key": "openBatches", + "Value": 1 + } + ] + }, + { + "Name": "BatchDispatched", + "Tags": [ + { + "Key": "dispatchedBatches", + "Value": 1 + } + ] + } + ], + "activities": [ + { + "OperationName": "GraphQL DataLoader Batch CustomDataLoader", + "DisplayName": "GraphQL DataLoader Batch CustomDataLoader", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "dataloader_batch" + }, + { + "Key": "graphql.dataloader.name", + "Value": "CustomDataLoader" + }, + { + "Key": "graphql.dataloader.batch.size", + "Value": 1 + }, + { + "Key": "graphql.dataloader.batch.keys", + "Value": [ + "abc" + ] + } + ], + "event": [] + } + ] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_events_of_a_simple_query_default.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.DefaultScopes_ExcludesExecuteRequestAndParseDocumentSpans.snap similarity index 50% rename from src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_events_of_a_simple_query_default.snap rename to src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.DefaultScopes_ExcludesExecuteRequestAndParseDocumentSpans.snap index 31c920d0d93..2386d6e2307 100644 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_events_of_a_simple_query_default.snap +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.DefaultScopes_ExcludesExecuteRequestAndParseDocumentSpans.snap @@ -1,57 +1,57 @@ -{ +{ "activities": [ { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", "Status": "Ok", "tags": [ { - "Key": "graphql.document.id", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" + "Key": "graphql.processing.type", + "Value": "validate" }, { "Key": "graphql.document.hash", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "otel.status_code", - "Value": "OK" + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" } ], "event": [] }, { - "OperationName": "CompileOperation", - "DisplayName": "Compile Operation", + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", "Status": "Ok", "tags": [ { - "Key": "otel.status_code", - "Value": "OK" + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.type", + "Value": "query" } ], "event": [] }, { - "OperationName": "ResolveFieldValue", - "DisplayName": "/sayHello", + "OperationName": "SimpleQuery.sayHello", + "DisplayName": "SimpleQuery.sayHello", "Status": "Ok", "tags": [ { - "Key": "graphql.selection.name", - "Value": "sayHello" + "Key": "graphql.processing.type", + "Value": "resolve" }, { - "Key": "graphql.selection.type", - "Value": "String!" + "Key": "graphql.selection.name", + "Value": "sayHello" }, { "Key": "graphql.selection.path", - "Value": "/sayHello" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/sayHello" + "Value": "sayHello" }, { "Key": "graphql.selection.field.name", @@ -62,12 +62,8 @@ "Value": "SimpleQuery.sayHello" }, { - "Key": "graphql.selection.field.declaringType", + "Key": "graphql.selection.field.parent_type", "Value": "SimpleQuery" - }, - { - "Key": "otel.status_code", - "Value": "OK" } ], "event": [] diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.DocumentCache_SecondExecution_RecordsCacheHitEvent.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.DocumentCache_SecondExecution_RecordsCacheHitEvent.snap new file mode 100644 index 00000000000..e6b686d9426 --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.DocumentCache_SecondExecution_RecordsCacheHitEvent.snap @@ -0,0 +1,81 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [ + { + "Name": "RetrievedDocumentFromCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [], + "activities": [ + { + "OperationName": "SimpleQuery.sayHello", + "DisplayName": "SimpleQuery.sayHello", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "resolve" + }, + { + "Key": "graphql.selection.name", + "Value": "sayHello" + }, + { + "Key": "graphql.selection.path", + "Value": "sayHello" + }, + { + "Key": "graphql.selection.field.name", + "Value": "sayHello" + }, + { + "Key": "graphql.selection.field.coordinate", + "Value": "SimpleQuery.sayHello" + }, + { + "Key": "graphql.selection.field.parent_type", + "Value": "SimpleQuery" + } + ], + "event": [] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.Ensure_That_The_Validation_Activity_Has_An_Error_Status.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.Ensure_That_The_Validation_Activity_Has_An_Error_Status.snap new file mode 100644 index 00000000000..16d3fd1d1f8 --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.Ensure_That_The_Validation_Activity_Has_An_Error_Status.snap @@ -0,0 +1,69 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "GraphQL Operation", + "Status": "Error", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:bb1d246465341a97bdc727d6cd8ead5c" + }, + { + "Key": "graphql.document.body", + "Value": "query SayHelloOperation {\n sayHello_\n}" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:bb1d246465341a97bdc727d6cd8ead5c" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Error", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:bb1d246465341a97bdc727d6cd8ead5c" + } + ], + "event": [ + { + "Name": "exception", + "Tags": [ + { + "Key": "graphql.error.message", + "Value": "The field `sayHello_` does not exist on the type `SimpleQuery`." + }, + { + "Key": "graphql.error.locations", + "Value": [ + { + "line": 1, + "column": 27 + } + ] + } + ] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.ParsingError_InvalidGraphQLDocument_ReportsErrorStatus.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.ParsingError_InvalidGraphQLDocument_ReportsErrorStatus.snap new file mode 100644 index 00000000000..aec02c711c2 --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.ParsingError_InvalidGraphQLDocument_ReportsErrorStatus.snap @@ -0,0 +1,48 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "GraphQL Operation", + "Status": "Error", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:bb507ba78696fc3c6ff3a17fb06784d5" + } + ], + "event": [ + { + "Name": "exception", + "Tags": [ + { + "Key": "exception.message", + "Value": "Expected a `RightBrace`-token, but found a `EndOfFile`-token." + }, + { + "Key": "exception.stacktrace", + "Value": "HotChocolate.Language.SyntaxException: Expected a `RightBrace`-token, but found a `EndOfFile`-token.\n at HotChocolate.Language.Utf8GraphQLParser.ParseSelectionSet() in Utf8GraphQLParser.Operations.cs:line 221\n at HotChocolate.Language.Utf8GraphQLParser.ParseShortOperationDefinition() in Utf8GraphQLParser.Operations.cs:line 73\n at HotChocolate.Language.Utf8GraphQLParser.ParseDefinition() in Utf8GraphQLParser.cs:line 215\n at HotChocolate.Language.Utf8GraphQLParser.Parse() in Utf8GraphQLParser.cs:line 98\n at HotChocolate.Language.Utf8GraphQLParser.Parse(String sourceText, ParserOptions options) in Utf8GraphQLParser.cs:line 326\n at HotChocolate.Execution.Pipeline.DocumentParserMiddleware.InvokeAsync(RequestContext context) in DocumentParserMiddleware.cs:line 63" + }, + { + "Key": "exception.type", + "Value": "HotChocolate.Language.SyntaxException" + } + ] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Unset", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:bb507ba78696fc3c6ff3a17fb06784d5" + } + ], + "event": [] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.PersistedOperation_LoadsFromStorage_DefaultScopes.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.PersistedOperation_LoadsFromStorage_DefaultScopes.snap new file mode 100644 index 00000000000..b85b922a120 --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.PersistedOperation_LoadsFromStorage_DefaultScopes.snap @@ -0,0 +1,80 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.document.id", + "Value": "sayHelloOp" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.document.id", + "Value": "sayHelloOp" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "SimpleQuery.sayHello", + "DisplayName": "SimpleQuery.sayHello", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "resolve" + }, + { + "Key": "graphql.selection.name", + "Value": "sayHello" + }, + { + "Key": "graphql.selection.path", + "Value": "sayHello" + }, + { + "Key": "graphql.selection.field.name", + "Value": "sayHello" + }, + { + "Key": "graphql.selection.field.coordinate", + "Value": "SimpleQuery.sayHello" + }, + { + "Key": "graphql.selection.field.parent_type", + "Value": "SimpleQuery" + } + ], + "event": [] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.ResolverError_AtRootLevel_MarksOperationAsError.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.ResolverError_AtRootLevel_MarksOperationAsError.snap new file mode 100644 index 00000000000..6031fca24ee --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.ResolverError_AtRootLevel_MarksOperationAsError.snap @@ -0,0 +1,151 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Error", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:ae3f9af1d2570e25f30a1ad002172b82" + }, + { + "Key": "graphql.document.body", + "Value": "{\n causeFatalError\n}" + } + ], + "event": [ + { + "Name": "AddedOperationToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:ae3f9af1d2570e25f30a1ad002172b82" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:ae3f9af1d2570e25f30a1ad002172b82" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:ae3f9af1d2570e25f30a1ad002172b82" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Error", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:ae3f9af1d2570e25f30a1ad002172b82" + } + ], + "event": [], + "activities": [ + { + "OperationName": "SimpleQuery.causeFatalError", + "DisplayName": "SimpleQuery.causeFatalError", + "Status": "Error", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "resolve" + }, + { + "Key": "graphql.selection.name", + "Value": "causeFatalError" + }, + { + "Key": "graphql.selection.path", + "Value": "causeFatalError" + }, + { + "Key": "graphql.selection.field.name", + "Value": "causeFatalError" + }, + { + "Key": "graphql.selection.field.coordinate", + "Value": "SimpleQuery.causeFatalError" + }, + { + "Key": "graphql.selection.field.parent_type", + "Value": "SimpleQuery" + } + ], + "event": [ + { + "Name": "exception", + "Tags": [ + { + "Key": "graphql.error.message", + "Value": "fail" + }, + { + "Key": "graphql.error.path", + "Value": "causeFatalError" + } + ] + } + ] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.ResolverError_DeepInTree_MarksNestedFieldAsError.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.ResolverError_DeepInTree_MarksNestedFieldAsError.snap new file mode 100644 index 00000000000..dd5f4d13d99 --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.ResolverError_DeepInTree_MarksNestedFieldAsError.snap @@ -0,0 +1,69 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "GraphQL Operation", + "Status": "Error", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:2bf0466e723c1bad3a65089b6fb0dbfc" + }, + { + "Key": "graphql.document.body", + "Value": "{\n deep {\n deeper {\n deeps {\n deeper {\n causeFatalError\n }\n }\n }\n }\n}" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:2bf0466e723c1bad3a65089b6fb0dbfc" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Error", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:2bf0466e723c1bad3a65089b6fb0dbfc" + } + ], + "event": [ + { + "Name": "exception", + "Tags": [ + { + "Key": "graphql.error.message", + "Value": "The field `causeFatalError` does not exist on the type `Deeper`." + }, + { + "Key": "graphql.error.locations", + "Value": [ + { + "line": 6, + "column": 21 + } + ] + } + ] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.SubscriptionEventError_Records_Subscription_Event_Error.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.SubscriptionEventError_Records_Subscription_Event_Error.snap new file mode 100644 index 00000000000..f96698c579d --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.SubscriptionEventError_Records_Subscription_Event_Error.snap @@ -0,0 +1,201 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "subscription", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "subscription" + }, + { + "Key": "graphql.operation.name", + "Value": "OnFailingMessageSubscription" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:1f38a1bfe720ad0cb6083bf8b309d3aa" + } + ], + "event": [ + { + "Name": "AddedOperationToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:1f38a1bfe720ad0cb6083bf8b309d3aa" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:1f38a1bfe720ad0cb6083bf8b309d3aa" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:1f38a1bfe720ad0cb6083bf8b309d3aa" + }, + { + "Key": "graphql.operation.type", + "Value": "subscription" + }, + { + "Key": "graphql.operation.name", + "Value": "OnFailingMessageSubscription" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "subscription" + }, + { + "Key": "graphql.operation.name", + "Value": "OnFailingMessageSubscription" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:1f38a1bfe720ad0cb6083bf8b309d3aa" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Subscription Event", + "DisplayName": "GraphQL Subscription Event", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "subscription" + }, + { + "Key": "graphql.operation.name", + "Value": "OnFailingMessageSubscription" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:1f38a1bfe720ad0cb6083bf8b309d3aa" + }, + { + "Key": "graphql.subscription.id", + "Value": "1" + } + ], + "event": [], + "activities": [ + { + "OperationName": "SimpleSubscription.onFailingMessage", + "DisplayName": "SimpleSubscription.onFailingMessage", + "Status": "Error", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "resolve" + }, + { + "Key": "graphql.selection.name", + "Value": "onFailingMessage" + }, + { + "Key": "graphql.selection.path", + "Value": "onFailingMessage" + }, + { + "Key": "graphql.selection.field.name", + "Value": "onFailingMessage" + }, + { + "Key": "graphql.selection.field.coordinate", + "Value": "SimpleSubscription.onFailingMessage" + }, + { + "Key": "graphql.selection.field.parent_type", + "Value": "SimpleSubscription" + } + ], + "event": [ + { + "Name": "exception", + "Tags": [ + { + "Key": "graphql.error.message", + "Value": "Unexpected Execution Error" + }, + { + "Key": "exception.message", + "Value": "Subscription event failed." + }, + { + "Key": "exception.stacktrace", + "Value": "System.InvalidOperationException: Subscription event failed.\n at HotChocolate.Diagnostics.ActivityExecutionDiagnosticListenerTests.SimpleSubscription.OnFailingMessage(String message) in ActivityExecutionDiagnosticListenerTests.cs:line 493\n at lambda_method(Closure, IResolverContext)\n at HotChocolate.Types.Helpers.FieldMiddlewareCompiler.<>c__DisplayClass9_0.<b__0>d.MoveNext() in FieldMiddlewareCompiler.cs:line 127\n--- End of stack trace from previous location ---\n at HotChocolate.Execution.Processing.Tasks.ResolverTask.ExecuteResolverPipelineAsync(CancellationToken cancellationToken) in ResolverTask.Execute.cs:line 135\n at HotChocolate.Execution.Processing.Tasks.ResolverTask.TryExecuteAsync(CancellationToken cancellationToken) in ResolverTask.Execute.cs:line 81" + }, + { + "Key": "exception.type", + "Value": "System.InvalidOperationException" + }, + { + "Key": "graphql.error.path", + "Value": "onFailingMessage" + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.SubscriptionEvent_Records_Subscription_Event_Span.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.SubscriptionEvent_Records_Subscription_Event_Span.snap new file mode 100644 index 00000000000..dab79a8bcac --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.SubscriptionEvent_Records_Subscription_Event_Span.snap @@ -0,0 +1,175 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "subscription", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "subscription" + }, + { + "Key": "graphql.operation.name", + "Value": "OnMessageSubscription" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2" + } + ], + "event": [ + { + "Name": "AddedOperationToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2" + }, + { + "Key": "graphql.operation.type", + "Value": "subscription" + }, + { + "Key": "graphql.operation.name", + "Value": "OnMessageSubscription" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "subscription" + }, + { + "Key": "graphql.operation.name", + "Value": "OnMessageSubscription" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Subscription Event", + "DisplayName": "GraphQL Subscription Event", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "subscription" + }, + { + "Key": "graphql.operation.name", + "Value": "OnMessageSubscription" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2" + }, + { + "Key": "graphql.subscription.id", + "Value": "1" + } + ], + "event": [], + "activities": [ + { + "OperationName": "SimpleSubscription.onMessage", + "DisplayName": "SimpleSubscription.onMessage", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "resolve" + }, + { + "Key": "graphql.selection.name", + "Value": "onMessage" + }, + { + "Key": "graphql.selection.path", + "Value": "onMessage" + }, + { + "Key": "graphql.selection.field.name", + "Value": "onMessage" + }, + { + "Key": "graphql.selection.field.coordinate", + "Value": "SimpleSubscription.onMessage" + }, + { + "Key": "graphql.selection.field.parent_type", + "Value": "SimpleSubscription" + } + ], + "event": [] + } + ] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.Track_Events_Of_A_Simple_Query_Default.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.Track_Events_Of_A_Simple_Query_Default.snap new file mode 100644 index 00000000000..2386d6e2307 --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.Track_Events_Of_A_Simple_Query_Default.snap @@ -0,0 +1,72 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "SimpleQuery.sayHello", + "DisplayName": "SimpleQuery.sayHello", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "resolve" + }, + { + "Key": "graphql.selection.name", + "Value": "sayHello" + }, + { + "Key": "graphql.selection.path", + "Value": "sayHello" + }, + { + "Key": "graphql.selection.field.name", + "Value": "sayHello" + }, + { + "Key": "graphql.selection.field.coordinate", + "Value": "SimpleQuery.sayHello" + }, + { + "Key": "graphql.selection.field.parent_type", + "Value": "SimpleQuery" + } + ], + "event": [] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.ValidationError_UnknownField_ReportsErrorStatus.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.ValidationError_UnknownField_ReportsErrorStatus.snap new file mode 100644 index 00000000000..c0dc8f03d3c --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.ValidationError_UnknownField_ReportsErrorStatus.snap @@ -0,0 +1,65 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "GraphQL Operation", + "Status": "Error", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:1526f207d516803b71eb8a0db419b57b" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:1526f207d516803b71eb8a0db419b57b" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Error", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:1526f207d516803b71eb8a0db419b57b" + } + ], + "event": [ + { + "Name": "exception", + "Tags": [ + { + "Key": "graphql.error.message", + "Value": "The field `unknownField123` does not exist on the type `SimpleQuery`." + }, + { + "Key": "graphql.error.locations", + "Value": [ + { + "line": 1, + "column": 3 + } + ] + } + ] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.VariableCoercion_WithAllScopes_RecordsCoercionSpan.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.VariableCoercion_WithAllScopes_RecordsCoercionSpan.snap new file mode 100644 index 00000000000..e9d8d435d16 --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityExecutionDiagnosticListenerTests.VariableCoercion_WithAllScopes_RecordsCoercionSpan.snap @@ -0,0 +1,153 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + } + ], + "event": [ + { + "Name": "AddedOperationToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Variable Coercion", + "DisplayName": "GraphQL Variable Coercion", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "variable_coercion" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + } + ], + "event": [], + "activities": [ + { + "OperationName": "SimpleQuery.greeting", + "DisplayName": "SimpleQuery.greeting", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "resolve" + }, + { + "Key": "graphql.selection.name", + "Value": "greeting" + }, + { + "Key": "graphql.selection.path", + "Value": "greeting" + }, + { + "Key": "graphql.selection.field.name", + "Value": "greeting" + }, + { + "Key": "graphql.selection.field.coordinate", + "Value": "SimpleQuery.greeting" + }, + { + "Key": "graphql.selection.field.parent_type", + "Value": "SimpleQuery" + } + ], + "event": [] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Get_SDL_Download.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Get_SDL_Download.snap similarity index 75% rename from src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Get_SDL_Download.snap rename to src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Get_SDL_Download.snap index 1669a6a2090..d77edd7831a 100644 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Get_SDL_Download.snap +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Get_SDL_Download.snap @@ -5,6 +5,10 @@ "DisplayName": "GraphQL HTTP GET SDL", "Status": "Ok", "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpGetSchema" + }, { "Key": "graphql.schema.name", "Value": "_Default" diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_add_variables_to_http_activity.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Get_SingleRequest_GetHeroName.snap similarity index 50% rename from src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_add_variables_to_http_activity.snap rename to src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Get_SingleRequest_GetHeroName.snap index c6b48eaef41..549d391b410 100644 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_add_variables_to_http_activity.snap +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Get_SingleRequest_GetHeroName.snap @@ -2,9 +2,13 @@ "activities": [ { "OperationName": "ExecuteHttpRequest", - "DisplayName": "GraphQL HTTP POST", + "DisplayName": "GraphQL HTTP GET", "Status": "Ok", "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpGet" + }, { "Key": "graphql.schema.name", "Value": "_Default" @@ -14,40 +18,31 @@ "Value": "single" }, { - "Key": "graphql.http.request.variables", - "Value": "{\"episode\":\"NEW_HOPE\"}" + "Key": "graphql.http.request.query.hash", + "Value": "a570a6bff748b5916eadf153261d9c6d" } ], "event": [], "activities": [ { - "OperationName": "ParseHttpRequest", + "OperationName": "Parse HTTP Request", "DisplayName": "Parse HTTP Request", "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], + "tags": [], "event": [] }, { - "OperationName": "ExecuteRequest", - "DisplayName": "query { hero }", - "Status": "Unset", + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", "tags": [ { - "Key": "graphql.document.id", - "Value": "1d4bca4d0dff630390ddf48e9085589d" + "Key": "graphql.operation.type", + "Value": "query" }, { "Key": "graphql.document.hash", - "Value": "cc68dfd8c0c54a586a03c35296c5d1f9" - }, - { - "Key": "graphql.operation.id", - "Value": "_Default-1-1d4bca4d0dff630390ddf48e9085589d" + "Value": "md5:acb8d5d513c260b3cef3e3a12b0e29af" } ], "event": [ @@ -62,93 +57,97 @@ ], "activities": [ { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", "Status": "Ok", "tags": [ { - "Key": "graphql.document.id", - "Value": "1d4bca4d0dff630390ddf48e9085589d" + "Key": "graphql.processing.type", + "Value": "validate" }, { "Key": "graphql.document.hash", - "Value": "cc68dfd8c0c54a586a03c35296c5d1f9" - }, - { - "Key": "otel.status_code", - "Value": "OK" + "Value": "md5:acb8d5d513c260b3cef3e3a12b0e29af" } ], "event": [] }, { - "OperationName": "AnalyzeOperationCost", - "DisplayName": "Analyze Operation Complexity", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.operation.id", - "Value": "1d4bca4d0dff630390ddf48e9085589d" - } - ], - "event": [] - }, - { - "OperationName": "CompileOperation", - "DisplayName": "Compile Operation", + "OperationName": "GraphQL Complexity Analysis", + "DisplayName": "GraphQL Complexity Analysis", "Status": "Ok", "tags": [ { - "Key": "otel.status_code", - "Value": "OK" + "Key": "graphql.document.hash", + "Value": "md5:acb8d5d513c260b3cef3e3a12b0e29af" + }, + { + "Key": "graphql.operation.fieldCost", + "Value": 1.0 + }, + { + "Key": "graphql.operation.typeCost", + "Value": 2.0 } ], "event": [] }, { - "OperationName": "CoerceVariables", - "DisplayName": "Coerce Variable", + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", "Status": "Ok", "tags": [ { - "Key": "otel.status_code", - "Value": "OK" + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:acb8d5d513c260b3cef3e3a12b0e29af" + }, + { + "Key": "graphql.operation.type", + "Value": "query" } ], "event": [] }, { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", "Status": "Ok", "tags": [ { - "Key": "otel.status_code", - "Value": "OK" + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:acb8d5d513c260b3cef3e3a12b0e29af" } ], "event": [], "activities": [ { - "OperationName": "ResolveFieldValue", - "DisplayName": "/hero", + "OperationName": "Query.hero", + "DisplayName": "Query.hero", "Status": "Ok", "tags": [ { - "Key": "graphql.selection.name", - "Value": "hero" + "Key": "graphql.processing.type", + "Value": "resolve" }, { - "Key": "graphql.selection.type", - "Value": "Character" + "Key": "graphql.selection.name", + "Value": "hero" }, { "Key": "graphql.selection.path", - "Value": "/hero" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/hero" + "Value": "hero" }, { "Key": "graphql.selection.field.name", @@ -159,12 +158,8 @@ "Value": "Query.hero" }, { - "Key": "graphql.selection.field.declaringType", + "Key": "graphql.selection.field.parent_type", "Value": "Query" - }, - { - "Key": "otel.status_code", - "Value": "OK" } ], "event": [] @@ -174,7 +169,7 @@ ] }, { - "OperationName": "FormatHttpResponse", + "OperationName": "Format HTTP Response", "DisplayName": "Format HTTP Response", "Status": "Ok", "tags": [], diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_Add_Variables_To_Http_Activity.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_Add_Variables_To_Http_Activity.snap new file mode 100644 index 00000000000..f0a0844464d --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_Add_Variables_To_Http_Activity.snap @@ -0,0 +1,209 @@ +{ + "activities": [ + { + "OperationName": "ExecuteHttpRequest", + "DisplayName": "GraphQL HTTP POST", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, + { + "Key": "graphql.schema.name", + "Value": "_Default" + }, + { + "Key": "graphql.http.request.type", + "Value": "single" + }, + { + "Key": "graphql.http.request.query.id", + "Value": "1d4bca4d0dff630390ddf48e9085589d" + }, + { + "Key": "graphql.http.request.query.hash", + "Value": "1d4bca4d0dff630390ddf48e9085589d" + }, + { + "Key": "graphql.http.request.variables", + "Value": "{\"episode\":\"NEW_HOPE\"}" + } + ], + "event": [], + "activities": [ + { + "OperationName": "Parse HTTP Request", + "DisplayName": "Parse HTTP Request", + "Status": "Ok", + "tags": [], + "event": [] + }, + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:cc68dfd8c0c54a586a03c35296c5d1f9" + } + ], + "event": [ + { + "Name": "AddedOperationToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:cc68dfd8c0c54a586a03c35296c5d1f9" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Complexity Analysis", + "DisplayName": "GraphQL Complexity Analysis", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:cc68dfd8c0c54a586a03c35296c5d1f9" + }, + { + "Key": "graphql.operation.fieldCost", + "Value": 1.0 + }, + { + "Key": "graphql.operation.typeCost", + "Value": 2.0 + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:cc68dfd8c0c54a586a03c35296c5d1f9" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Variable Coercion", + "DisplayName": "GraphQL Variable Coercion", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "variable_coercion" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:cc68dfd8c0c54a586a03c35296c5d1f9" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:cc68dfd8c0c54a586a03c35296c5d1f9" + } + ], + "event": [], + "activities": [ + { + "OperationName": "Query.hero", + "DisplayName": "Query.hero", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "resolve" + }, + { + "Key": "graphql.selection.name", + "Value": "hero" + }, + { + "Key": "graphql.selection.path", + "Value": "hero" + }, + { + "Key": "graphql.selection.field.name", + "Value": "hero" + }, + { + "Key": "graphql.selection.field.coordinate", + "Value": "Query.hero" + }, + { + "Key": "graphql.selection.field.parent_type", + "Value": "Query" + } + ], + "event": [] + } + ] + } + ] + }, + { + "OperationName": "Format HTTP Response", + "DisplayName": "Format HTTP Response", + "Status": "Ok", + "tags": [], + "event": [] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_capture_deferred_response.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_Capture_Deferred_Response.snap similarity index 53% rename from src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_capture_deferred_response.snap rename to src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_Capture_Deferred_Response.snap index 88a26adcf95..16356a5b239 100644 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_capture_deferred_response.snap +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_Capture_Deferred_Response.snap @@ -5,6 +5,10 @@ "DisplayName": "GraphQL HTTP POST", "Status": "Ok", "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, { "Key": "graphql.schema.name", "Value": "_Default" @@ -12,38 +16,37 @@ { "Key": "graphql.http.request.type", "Value": "single" + }, + { + "Key": "graphql.http.request.query.id", + "Value": "dd31323224a6428d4dc301134352aeab" + }, + { + "Key": "graphql.http.request.query.hash", + "Value": "dd31323224a6428d4dc301134352aeab" } ], "event": [], "activities": [ { - "OperationName": "ParseHttpRequest", + "OperationName": "Parse HTTP Request", "DisplayName": "Parse HTTP Request", "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], + "tags": [], "event": [] }, { - "OperationName": "ExecuteRequest", - "DisplayName": "query { hero }", - "Status": "Unset", + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", "tags": [ { - "Key": "graphql.document.id", - "Value": "dd31323224a6428d4dc301134352aeab" + "Key": "graphql.operation.type", + "Value": "query" }, { "Key": "graphql.document.hash", - "Value": "3beaca4ee1714ac9c9dfec8e445529df" - }, - { - "Key": "graphql.operation.id", - "Value": "_Default-1-dd31323224a6428d4dc301134352aeab" + "Value": "md5:3beaca4ee1714ac9c9dfec8e445529df" } ], "event": [ @@ -58,81 +61,97 @@ ], "activities": [ { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", "Status": "Ok", "tags": [ { - "Key": "graphql.document.id", - "Value": "dd31323224a6428d4dc301134352aeab" + "Key": "graphql.processing.type", + "Value": "validate" }, { "Key": "graphql.document.hash", - "Value": "3beaca4ee1714ac9c9dfec8e445529df" - }, - { - "Key": "otel.status_code", - "Value": "OK" + "Value": "md5:3beaca4ee1714ac9c9dfec8e445529df" } ], "event": [] }, { - "OperationName": "AnalyzeOperationCost", - "DisplayName": "Analyze Operation Complexity", - "Status": "Unset", + "OperationName": "GraphQL Complexity Analysis", + "DisplayName": "GraphQL Complexity Analysis", + "Status": "Ok", "tags": [ { - "Key": "graphql.operation.id", - "Value": "dd31323224a6428d4dc301134352aeab" + "Key": "graphql.document.hash", + "Value": "md5:3beaca4ee1714ac9c9dfec8e445529df" + }, + { + "Key": "graphql.operation.fieldCost", + "Value": 1.0 + }, + { + "Key": "graphql.operation.typeCost", + "Value": 2.0 } ], "event": [] }, { - "OperationName": "CompileOperation", - "DisplayName": "Compile Operation", + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", "Status": "Ok", "tags": [ { - "Key": "otel.status_code", - "Value": "OK" + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:3beaca4ee1714ac9c9dfec8e445529df" + }, + { + "Key": "graphql.operation.type", + "Value": "query" } ], "event": [] }, { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", "Status": "Ok", "tags": [ { - "Key": "otel.status_code", - "Value": "OK" + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:3beaca4ee1714ac9c9dfec8e445529df" } ], "event": [], "activities": [ { - "OperationName": "ResolveFieldValue", - "DisplayName": "/hero", + "OperationName": "Query.hero", + "DisplayName": "Query.hero", "Status": "Ok", "tags": [ { - "Key": "graphql.selection.name", - "Value": "hero" + "Key": "graphql.processing.type", + "Value": "resolve" }, { - "Key": "graphql.selection.type", - "Value": "Character" + "Key": "graphql.selection.name", + "Value": "hero" }, { "Key": "graphql.selection.path", - "Value": "/hero" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/hero" + "Value": "hero" }, { "Key": "graphql.selection.field.name", @@ -143,36 +162,28 @@ "Value": "Query.hero" }, { - "Key": "graphql.selection.field.declaringType", + "Key": "graphql.selection.field.parent_type", "Value": "Query" - }, - { - "Key": "otel.status_code", - "Value": "OK" } ], "event": [] }, { - "OperationName": "ResolveFieldValue", - "DisplayName": "/hero/id", + "OperationName": "Droid.id", + "DisplayName": "Droid.id", "Status": "Ok", "tags": [ { - "Key": "graphql.selection.name", - "Value": "id" + "Key": "graphql.processing.type", + "Value": "resolve" }, { - "Key": "graphql.selection.type", - "Value": "ID!" + "Key": "graphql.selection.name", + "Value": "id" }, { "Key": "graphql.selection.path", - "Value": "/hero/id" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/hero/id" + "Value": "hero.id" }, { "Key": "graphql.selection.field.name", @@ -183,12 +194,8 @@ "Value": "Droid.id" }, { - "Key": "graphql.selection.field.declaringType", + "Key": "graphql.selection.field.parent_type", "Value": "Droid" - }, - { - "Key": "otel.status_code", - "Value": "OK" } ], "event": [] diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_ensure_list_path_is_correctly_built.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_Ensure_List_Path_Is_Correctly_Built.snap similarity index 55% rename from src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_ensure_list_path_is_correctly_built.snap rename to src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_Ensure_List_Path_Is_Correctly_Built.snap index 8a5b3455d73..a4c809a7deb 100644 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_ensure_list_path_is_correctly_built.snap +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_Ensure_List_Path_Is_Correctly_Built.snap @@ -5,6 +5,10 @@ "DisplayName": "GraphQL HTTP POST", "Status": "Ok", "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, { "Key": "graphql.schema.name", "Value": "_Default" @@ -12,38 +16,37 @@ { "Key": "graphql.http.request.type", "Value": "single" + }, + { + "Key": "graphql.http.request.query.id", + "Value": "c0513b4b6f0cf7430f64de4aa3dcd7c6" + }, + { + "Key": "graphql.http.request.query.hash", + "Value": "c0513b4b6f0cf7430f64de4aa3dcd7c6" } ], "event": [], "activities": [ { - "OperationName": "ParseHttpRequest", + "OperationName": "Parse HTTP Request", "DisplayName": "Parse HTTP Request", "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], + "tags": [], "event": [] }, { - "OperationName": "ExecuteRequest", - "DisplayName": "query { hero }", - "Status": "Unset", + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", "tags": [ { - "Key": "graphql.document.id", - "Value": "c0513b4b6f0cf7430f64de4aa3dcd7c6" + "Key": "graphql.operation.type", + "Value": "query" }, { "Key": "graphql.document.hash", - "Value": "668e9631148921208d08dbb69513fa8e" - }, - { - "Key": "graphql.operation.id", - "Value": "_Default-1-c0513b4b6f0cf7430f64de4aa3dcd7c6" + "Value": "md5:668e9631148921208d08dbb69513fa8e" } ], "event": [ @@ -58,81 +61,97 @@ ], "activities": [ { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", "Status": "Ok", "tags": [ { - "Key": "graphql.document.id", - "Value": "c0513b4b6f0cf7430f64de4aa3dcd7c6" + "Key": "graphql.processing.type", + "Value": "validate" }, { "Key": "graphql.document.hash", - "Value": "668e9631148921208d08dbb69513fa8e" - }, - { - "Key": "otel.status_code", - "Value": "OK" + "Value": "md5:668e9631148921208d08dbb69513fa8e" } ], "event": [] }, { - "OperationName": "AnalyzeOperationCost", - "DisplayName": "Analyze Operation Complexity", - "Status": "Unset", + "OperationName": "GraphQL Complexity Analysis", + "DisplayName": "GraphQL Complexity Analysis", + "Status": "Ok", "tags": [ { - "Key": "graphql.operation.id", - "Value": "c0513b4b6f0cf7430f64de4aa3dcd7c6" + "Key": "graphql.document.hash", + "Value": "md5:668e9631148921208d08dbb69513fa8e" + }, + { + "Key": "graphql.operation.fieldCost", + "Value": 1.0 + }, + { + "Key": "graphql.operation.typeCost", + "Value": 2.0 } ], "event": [] }, { - "OperationName": "CompileOperation", - "DisplayName": "Compile Operation", + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", "Status": "Ok", "tags": [ { - "Key": "otel.status_code", - "Value": "OK" + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:668e9631148921208d08dbb69513fa8e" + }, + { + "Key": "graphql.operation.type", + "Value": "query" } ], "event": [] }, { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", "Status": "Ok", "tags": [ { - "Key": "otel.status_code", - "Value": "OK" + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:668e9631148921208d08dbb69513fa8e" } ], "event": [], "activities": [ { - "OperationName": "ResolveFieldValue", - "DisplayName": "/hero", + "OperationName": "Query.hero", + "DisplayName": "Query.hero", "Status": "Ok", "tags": [ { - "Key": "graphql.selection.name", - "Value": "hero" + "Key": "graphql.processing.type", + "Value": "resolve" }, { - "Key": "graphql.selection.type", - "Value": "Character" + "Key": "graphql.selection.name", + "Value": "hero" }, { "Key": "graphql.selection.path", - "Value": "/hero" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/hero" + "Value": "hero" }, { "Key": "graphql.selection.field.name", @@ -143,36 +162,28 @@ "Value": "Query.hero" }, { - "Key": "graphql.selection.field.declaringType", + "Key": "graphql.selection.field.parent_type", "Value": "Query" - }, - { - "Key": "otel.status_code", - "Value": "OK" } ], "event": [] }, { - "OperationName": "ResolveFieldValue", - "DisplayName": "/hero/friends", + "OperationName": "Droid.friends", + "DisplayName": "Droid.friends", "Status": "Ok", "tags": [ { - "Key": "graphql.selection.name", - "Value": "friends" + "Key": "graphql.processing.type", + "Value": "resolve" }, { - "Key": "graphql.selection.type", - "Value": "FriendsConnection" + "Key": "graphql.selection.name", + "Value": "friends" }, { "Key": "graphql.selection.path", - "Value": "/hero/friends" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/hero/friends" + "Value": "hero.friends" }, { "Key": "graphql.selection.field.name", @@ -183,36 +194,28 @@ "Value": "Droid.friends" }, { - "Key": "graphql.selection.field.declaringType", + "Key": "graphql.selection.field.parent_type", "Value": "Droid" - }, - { - "Key": "otel.status_code", - "Value": "OK" } ], "event": [] }, { - "OperationName": "ResolveFieldValue", - "DisplayName": "/hero/friends/nodes[0]/friends", + "OperationName": "Human.friends", + "DisplayName": "Human.friends", "Status": "Ok", "tags": [ { - "Key": "graphql.selection.name", - "Value": "friends" + "Key": "graphql.processing.type", + "Value": "resolve" }, { - "Key": "graphql.selection.type", - "Value": "FriendsConnection" + "Key": "graphql.selection.name", + "Value": "friends" }, { "Key": "graphql.selection.path", - "Value": "/hero/friends/nodes[0]/friends" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/hero/friends/nodes/friends" + "Value": "hero.friends.nodes[0].friends" }, { "Key": "graphql.selection.field.name", @@ -223,36 +226,28 @@ "Value": "Human.friends" }, { - "Key": "graphql.selection.field.declaringType", + "Key": "graphql.selection.field.parent_type", "Value": "Human" - }, - { - "Key": "otel.status_code", - "Value": "OK" } ], "event": [] }, { - "OperationName": "ResolveFieldValue", - "DisplayName": "/hero/friends/nodes[1]/friends", + "OperationName": "Human.friends", + "DisplayName": "Human.friends", "Status": "Ok", "tags": [ { - "Key": "graphql.selection.name", - "Value": "friends" + "Key": "graphql.processing.type", + "Value": "resolve" }, { - "Key": "graphql.selection.type", - "Value": "FriendsConnection" + "Key": "graphql.selection.name", + "Value": "friends" }, { "Key": "graphql.selection.path", - "Value": "/hero/friends/nodes[1]/friends" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/hero/friends/nodes/friends" + "Value": "hero.friends.nodes[1].friends" }, { "Key": "graphql.selection.field.name", @@ -263,36 +258,28 @@ "Value": "Human.friends" }, { - "Key": "graphql.selection.field.declaringType", + "Key": "graphql.selection.field.parent_type", "Value": "Human" - }, - { - "Key": "otel.status_code", - "Value": "OK" } ], "event": [] }, { - "OperationName": "ResolveFieldValue", - "DisplayName": "/hero/friends/nodes[2]/friends", + "OperationName": "Human.friends", + "DisplayName": "Human.friends", "Status": "Ok", "tags": [ { - "Key": "graphql.selection.name", - "Value": "friends" + "Key": "graphql.processing.type", + "Value": "resolve" }, { - "Key": "graphql.selection.type", - "Value": "FriendsConnection" + "Key": "graphql.selection.name", + "Value": "friends" }, { "Key": "graphql.selection.path", - "Value": "/hero/friends/nodes[2]/friends" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/hero/friends/nodes/friends" + "Value": "hero.friends.nodes[2].friends" }, { "Key": "graphql.selection.field.name", @@ -303,12 +290,8 @@ "Value": "Human.friends" }, { - "Key": "graphql.selection.field.declaringType", + "Key": "graphql.selection.field.parent_type", "Value": "Human" - }, - { - "Key": "otel.status_code", - "Value": "OK" } ], "event": [] @@ -318,7 +301,7 @@ ] }, { - "OperationName": "FormatHttpResponse", + "OperationName": "Format HTTP Response", "DisplayName": "Format HTTP Response", "Status": "Ok", "tags": [], diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_parser_error.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_Parser_Error.snap similarity index 63% rename from src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_parser_error.snap rename to src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_Parser_Error.snap index e33490a9a8c..774711bad83 100644 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_parser_error.snap +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_Parser_Error.snap @@ -1,10 +1,14 @@ -{ +{ "activities": [ { "OperationName": "ExecuteHttpRequest", "DisplayName": "GraphQL HTTP POST", "Status": "Ok", "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, { "Key": "graphql.schema.name", "Value": "_Default" @@ -13,41 +17,37 @@ "event": [], "activities": [ { - "OperationName": "ParseHttpRequest", + "OperationName": "Parse HTTP Request", "DisplayName": "Parse HTTP Request", "Status": "Error", - "tags": [ - { - "Key": "otel.status_code", - "Value": "ERROR" - } - ], + "tags": [], "event": [ { "Name": "exception", "Tags": [ { - "Key": "exception.message", + "Key": "graphql.error.message", "Value": "Found a NameStart character `n` (110) following a number, which is disallowed." }, { - "Key": "exception.type", + "Key": "graphql.error.code", "Value": "HC0011" }, { - "Key": "graphql.error.location.column", - "Value": 37 - }, - { - "Key": "graphql.error.location.line", - "Value": 10 + "Key": "graphql.error.locations", + "Value": [ + { + "line": 10, + "column": 37 + } + ] } ] } ] }, { - "OperationName": "FormatHttpResponse", + "OperationName": "Format HTTP Response", "DisplayName": "Format HTTP Response", "Status": "Ok", "tags": [], diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_add_query_to_http_activity.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_SingleRequest_GetHeroName.snap similarity index 50% rename from src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_add_query_to_http_activity.snap rename to src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_SingleRequest_GetHeroName.snap index ed74db6956e..5735177229f 100644 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_add_query_to_http_activity.snap +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_SingleRequest_GetHeroName.snap @@ -5,6 +5,10 @@ "DisplayName": "GraphQL HTTP POST", "Status": "Ok", "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, { "Key": "graphql.schema.name", "Value": "_Default" @@ -12,38 +16,37 @@ { "Key": "graphql.http.request.type", "Value": "single" + }, + { + "Key": "graphql.http.request.query.id", + "Value": "a570a6bff748b5916eadf153261d9c6d" + }, + { + "Key": "graphql.http.request.query.hash", + "Value": "a570a6bff748b5916eadf153261d9c6d" } ], "event": [], "activities": [ { - "OperationName": "ParseHttpRequest", + "OperationName": "Parse HTTP Request", "DisplayName": "Parse HTTP Request", "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], + "tags": [], "event": [] }, { - "OperationName": "ExecuteRequest", - "DisplayName": "query { hero }", - "Status": "Unset", + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", "tags": [ { - "Key": "graphql.document.id", - "Value": "1d4bca4d0dff630390ddf48e9085589d" + "Key": "graphql.operation.type", + "Value": "query" }, { "Key": "graphql.document.hash", - "Value": "cc68dfd8c0c54a586a03c35296c5d1f9" - }, - { - "Key": "graphql.operation.id", - "Value": "_Default-1-1d4bca4d0dff630390ddf48e9085589d" + "Value": "md5:acb8d5d513c260b3cef3e3a12b0e29af" } ], "event": [ @@ -58,93 +61,97 @@ ], "activities": [ { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", "Status": "Ok", "tags": [ { - "Key": "graphql.document.id", - "Value": "1d4bca4d0dff630390ddf48e9085589d" + "Key": "graphql.processing.type", + "Value": "validate" }, { "Key": "graphql.document.hash", - "Value": "cc68dfd8c0c54a586a03c35296c5d1f9" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "AnalyzeOperationCost", - "DisplayName": "Analyze Operation Complexity", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.operation.id", - "Value": "1d4bca4d0dff630390ddf48e9085589d" + "Value": "md5:acb8d5d513c260b3cef3e3a12b0e29af" } ], "event": [] }, { - "OperationName": "CompileOperation", - "DisplayName": "Compile Operation", + "OperationName": "GraphQL Complexity Analysis", + "DisplayName": "GraphQL Complexity Analysis", "Status": "Ok", "tags": [ { - "Key": "otel.status_code", - "Value": "OK" + "Key": "graphql.document.hash", + "Value": "md5:acb8d5d513c260b3cef3e3a12b0e29af" + }, + { + "Key": "graphql.operation.fieldCost", + "Value": 1.0 + }, + { + "Key": "graphql.operation.typeCost", + "Value": 2.0 } ], "event": [] }, { - "OperationName": "CoerceVariables", - "DisplayName": "Coerce Variable", + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", "Status": "Ok", "tags": [ { - "Key": "otel.status_code", - "Value": "OK" + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:acb8d5d513c260b3cef3e3a12b0e29af" + }, + { + "Key": "graphql.operation.type", + "Value": "query" } ], "event": [] }, { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", "Status": "Ok", "tags": [ { - "Key": "otel.status_code", - "Value": "OK" + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:acb8d5d513c260b3cef3e3a12b0e29af" } ], "event": [], "activities": [ { - "OperationName": "ResolveFieldValue", - "DisplayName": "/hero", + "OperationName": "Query.hero", + "DisplayName": "Query.hero", "Status": "Ok", "tags": [ { - "Key": "graphql.selection.name", - "Value": "hero" + "Key": "graphql.processing.type", + "Value": "resolve" }, { - "Key": "graphql.selection.type", - "Value": "Character" + "Key": "graphql.selection.name", + "Value": "hero" }, { "Key": "graphql.selection.path", - "Value": "/hero" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/hero" + "Value": "hero" }, { "Key": "graphql.selection.field.name", @@ -155,12 +162,8 @@ "Value": "Query.hero" }, { - "Key": "graphql.selection.field.declaringType", + "Key": "graphql.selection.field.parent_type", "Value": "Query" - }, - { - "Key": "otel.status_code", - "Value": "OK" } ], "event": [] @@ -170,7 +173,7 @@ ] }, { - "OperationName": "FormatHttpResponse", + "OperationName": "Format HTTP Response", "DisplayName": "Format HTTP Response", "Status": "Ok", "tags": [], diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_SingleRequest_GetHeroName_Default.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_SingleRequest_GetHeroName_Default.snap similarity index 57% rename from src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_SingleRequest_GetHeroName_Default.snap rename to src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_SingleRequest_GetHeroName_Default.snap index f5cc8ff4b80..007fefef81a 100644 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_SingleRequest_GetHeroName_Default.snap +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_SingleRequest_GetHeroName_Default.snap @@ -2,9 +2,13 @@ "activities": [ { "OperationName": "ExecuteHttpRequest", - "DisplayName": "query { hero }", + "DisplayName": "query", "Status": "Ok", "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, { "Key": "graphql.schema.name", "Value": "_Default" @@ -14,16 +18,20 @@ "Value": "single" }, { - "Key": "graphql.document.id", + "Key": "graphql.http.request.query.id", "Value": "a570a6bff748b5916eadf153261d9c6d" }, { - "Key": "graphql.document.hash", - "Value": "acb8d5d513c260b3cef3e3a12b0e29af" + "Key": "graphql.http.request.query.hash", + "Value": "a570a6bff748b5916eadf153261d9c6d" }, { - "Key": "graphql.operation.id", - "Value": "_Default-1-a570a6bff748b5916eadf153261d9c6d" + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:acb8d5d513c260b3cef3e3a12b0e29af" } ], "event": [ @@ -38,69 +46,64 @@ ], "activities": [ { - "OperationName": "ParseHttpRequest", + "OperationName": "Parse HTTP Request", "DisplayName": "Parse HTTP Request", "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], + "tags": [], "event": [] }, { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", "Status": "Ok", "tags": [ { - "Key": "graphql.document.id", - "Value": "a570a6bff748b5916eadf153261d9c6d" + "Key": "graphql.processing.type", + "Value": "validate" }, { "Key": "graphql.document.hash", - "Value": "acb8d5d513c260b3cef3e3a12b0e29af" - }, - { - "Key": "otel.status_code", - "Value": "OK" + "Value": "md5:acb8d5d513c260b3cef3e3a12b0e29af" } ], "event": [] }, { - "OperationName": "CompileOperation", - "DisplayName": "Compile Operation", + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", "Status": "Ok", "tags": [ { - "Key": "otel.status_code", - "Value": "OK" + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:acb8d5d513c260b3cef3e3a12b0e29af" + }, + { + "Key": "graphql.operation.type", + "Value": "query" } ], "event": [] }, { - "OperationName": "ResolveFieldValue", - "DisplayName": "/hero", + "OperationName": "Query.hero", + "DisplayName": "Query.hero", "Status": "Ok", "tags": [ { - "Key": "graphql.selection.name", - "Value": "hero" + "Key": "graphql.processing.type", + "Value": "resolve" }, { - "Key": "graphql.selection.type", - "Value": "Character" + "Key": "graphql.selection.name", + "Value": "hero" }, { "Key": "graphql.selection.path", - "Value": "/hero" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/hero" + "Value": "hero" }, { "Key": "graphql.selection.field.name", @@ -111,18 +114,14 @@ "Value": "Query.hero" }, { - "Key": "graphql.selection.field.declaringType", + "Key": "graphql.selection.field.parent_type", "Value": "Query" - }, - { - "Key": "otel.status_code", - "Value": "OK" } ], "event": [] }, { - "OperationName": "FormatHttpResponse", + "OperationName": "Format HTTP Response", "DisplayName": "Format HTTP Response", "Status": "Ok", "tags": [], diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_Variables_Are_Not_Automatically_Added_To_Activities.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_Variables_Are_Not_Automatically_Added_To_Activities.snap new file mode 100644 index 00000000000..357e78284ce --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_Variables_Are_Not_Automatically_Added_To_Activities.snap @@ -0,0 +1,205 @@ +{ + "activities": [ + { + "OperationName": "ExecuteHttpRequest", + "DisplayName": "GraphQL HTTP POST", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, + { + "Key": "graphql.schema.name", + "Value": "_Default" + }, + { + "Key": "graphql.http.request.type", + "Value": "single" + }, + { + "Key": "graphql.http.request.query.id", + "Value": "1d4bca4d0dff630390ddf48e9085589d" + }, + { + "Key": "graphql.http.request.query.hash", + "Value": "1d4bca4d0dff630390ddf48e9085589d" + } + ], + "event": [], + "activities": [ + { + "OperationName": "Parse HTTP Request", + "DisplayName": "Parse HTTP Request", + "Status": "Ok", + "tags": [], + "event": [] + }, + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:cc68dfd8c0c54a586a03c35296c5d1f9" + } + ], + "event": [ + { + "Name": "AddedOperationToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:cc68dfd8c0c54a586a03c35296c5d1f9" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Complexity Analysis", + "DisplayName": "GraphQL Complexity Analysis", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:cc68dfd8c0c54a586a03c35296c5d1f9" + }, + { + "Key": "graphql.operation.fieldCost", + "Value": 1.0 + }, + { + "Key": "graphql.operation.typeCost", + "Value": 2.0 + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:cc68dfd8c0c54a586a03c35296c5d1f9" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Variable Coercion", + "DisplayName": "GraphQL Variable Coercion", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "variable_coercion" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:cc68dfd8c0c54a586a03c35296c5d1f9" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:cc68dfd8c0c54a586a03c35296c5d1f9" + } + ], + "event": [], + "activities": [ + { + "OperationName": "Query.hero", + "DisplayName": "Query.hero", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "resolve" + }, + { + "Key": "graphql.selection.name", + "Value": "hero" + }, + { + "Key": "graphql.selection.path", + "Value": "hero" + }, + { + "Key": "graphql.selection.field.name", + "Value": "hero" + }, + { + "Key": "graphql.selection.field.coordinate", + "Value": "Query.hero" + }, + { + "Key": "graphql.selection.field.parent_type", + "Value": "Query" + } + ], + "event": [] + } + ] + } + ] + }, + { + "OperationName": "Format HTTP Response", + "DisplayName": "Format HTTP Response", + "Status": "Ok", + "tags": [], + "event": [] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_With_Extensions_Map.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_With_Extensions_Map.snap new file mode 100644 index 00000000000..66aac757f7e --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.Http_Post_With_Extensions_Map.snap @@ -0,0 +1,209 @@ +{ + "activities": [ + { + "OperationName": "ExecuteHttpRequest", + "DisplayName": "GraphQL HTTP POST", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, + { + "Key": "graphql.schema.name", + "Value": "_Default" + }, + { + "Key": "graphql.http.request.type", + "Value": "single" + }, + { + "Key": "graphql.http.request.query.id", + "Value": "1d4bca4d0dff630390ddf48e9085589d" + }, + { + "Key": "graphql.http.request.query.hash", + "Value": "1d4bca4d0dff630390ddf48e9085589d" + }, + { + "Key": "graphql.http.request.extensions", + "Value": "{\"test\":\"abc\"}" + } + ], + "event": [], + "activities": [ + { + "OperationName": "Parse HTTP Request", + "DisplayName": "Parse HTTP Request", + "Status": "Ok", + "tags": [], + "event": [] + }, + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:cc68dfd8c0c54a586a03c35296c5d1f9" + } + ], + "event": [ + { + "Name": "AddedOperationToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:cc68dfd8c0c54a586a03c35296c5d1f9" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Complexity Analysis", + "DisplayName": "GraphQL Complexity Analysis", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:cc68dfd8c0c54a586a03c35296c5d1f9" + }, + { + "Key": "graphql.operation.fieldCost", + "Value": 1.0 + }, + { + "Key": "graphql.operation.typeCost", + "Value": 2.0 + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:cc68dfd8c0c54a586a03c35296c5d1f9" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Variable Coercion", + "DisplayName": "GraphQL Variable Coercion", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "variable_coercion" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:cc68dfd8c0c54a586a03c35296c5d1f9" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:cc68dfd8c0c54a586a03c35296c5d1f9" + } + ], + "event": [], + "activities": [ + { + "OperationName": "Query.hero", + "DisplayName": "Query.hero", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "resolve" + }, + { + "Key": "graphql.selection.name", + "Value": "hero" + }, + { + "Key": "graphql.selection.path", + "Value": "hero" + }, + { + "Key": "graphql.selection.field.name", + "Value": "hero" + }, + { + "Key": "graphql.selection.field.coordinate", + "Value": "Query.hero" + }, + { + "Key": "graphql.selection.field.parent_type", + "Value": "Query" + } + ], + "event": [] + } + ] + } + ] + }, + { + "OperationName": "Format HTTP Response", + "DisplayName": "Format HTTP Response", + "Status": "Ok", + "tags": [], + "event": [] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.RequestDetails_All_IncludesAllDetails.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.RequestDetails_All_IncludesAllDetails.snap new file mode 100644 index 00000000000..3ae9d85ab7e --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.RequestDetails_All_IncludesAllDetails.snap @@ -0,0 +1,233 @@ +{ + "activities": [ + { + "OperationName": "ExecuteHttpRequest", + "DisplayName": "GraphQL HTTP POST", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, + { + "Key": "graphql.schema.name", + "Value": "_Default" + }, + { + "Key": "graphql.http.request.type", + "Value": "single" + }, + { + "Key": "graphql.http.request.query.id", + "Value": "b45805c675e9fff20b0367e5ff31735f" + }, + { + "Key": "graphql.http.request.query.hash", + "Value": "b45805c675e9fff20b0367e5ff31735f" + }, + { + "Key": "graphql.http.request.query.body", + "Value": "query GetHero(\n $episode: Episode!\n) {\n hero(episode: $episode) {\n name\n }\n}" + }, + { + "Key": "graphql.http.request.variables", + "Value": "{\"episode\":\"NEW_HOPE\"}" + }, + { + "Key": "graphql.http.request.extensions", + "Value": "{\"test\":\"abc\"}" + } + ], + "event": [], + "activities": [ + { + "OperationName": "Parse HTTP Request", + "DisplayName": "Parse HTTP Request", + "Status": "Ok", + "tags": [], + "event": [] + }, + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetHero" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:9335388c19fd2e230b6294a596d13a93" + } + ], + "event": [ + { + "Name": "AddedOperationToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:9335388c19fd2e230b6294a596d13a93" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Complexity Analysis", + "DisplayName": "GraphQL Complexity Analysis", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:9335388c19fd2e230b6294a596d13a93" + }, + { + "Key": "graphql.operation.fieldCost", + "Value": 1.0 + }, + { + "Key": "graphql.operation.typeCost", + "Value": 2.0 + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:9335388c19fd2e230b6294a596d13a93" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetHero" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Variable Coercion", + "DisplayName": "GraphQL Variable Coercion", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "variable_coercion" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetHero" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:9335388c19fd2e230b6294a596d13a93" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetHero" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:9335388c19fd2e230b6294a596d13a93" + } + ], + "event": [], + "activities": [ + { + "OperationName": "Query.hero", + "DisplayName": "Query.hero", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "resolve" + }, + { + "Key": "graphql.selection.name", + "Value": "hero" + }, + { + "Key": "graphql.selection.path", + "Value": "hero" + }, + { + "Key": "graphql.selection.field.name", + "Value": "hero" + }, + { + "Key": "graphql.selection.field.coordinate", + "Value": "Query.hero" + }, + { + "Key": "graphql.selection.field.parent_type", + "Value": "Query" + } + ], + "event": [] + } + ] + } + ] + }, + { + "OperationName": "Format HTTP Response", + "DisplayName": "Format HTTP Response", + "Status": "Ok", + "tags": [], + "event": [] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.RequestDetails_Default_IncludesIdHashOperationNameExtensions.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.RequestDetails_Default_IncludesIdHashOperationNameExtensions.snap new file mode 100644 index 00000000000..62db38168d3 --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.RequestDetails_Default_IncludesIdHashOperationNameExtensions.snap @@ -0,0 +1,201 @@ +{ + "activities": [ + { + "OperationName": "ExecuteHttpRequest", + "DisplayName": "GraphQL HTTP POST", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, + { + "Key": "graphql.schema.name", + "Value": "_Default" + }, + { + "Key": "graphql.http.request.type", + "Value": "single" + }, + { + "Key": "graphql.http.request.query.id", + "Value": "029e67e1b2d8493b3dd23fdaf6545b03" + }, + { + "Key": "graphql.http.request.query.hash", + "Value": "029e67e1b2d8493b3dd23fdaf6545b03" + }, + { + "Key": "graphql.http.request.extensions", + "Value": "{\"test\":\"abc\"}" + } + ], + "event": [], + "activities": [ + { + "OperationName": "Parse HTTP Request", + "DisplayName": "Parse HTTP Request", + "Status": "Ok", + "tags": [], + "event": [] + }, + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetHero" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:06731ff10fea3ec8e9b2aabceee6ddd9" + } + ], + "event": [ + { + "Name": "AddedOperationToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:06731ff10fea3ec8e9b2aabceee6ddd9" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Complexity Analysis", + "DisplayName": "GraphQL Complexity Analysis", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:06731ff10fea3ec8e9b2aabceee6ddd9" + }, + { + "Key": "graphql.operation.fieldCost", + "Value": 1.0 + }, + { + "Key": "graphql.operation.typeCost", + "Value": 2.0 + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:06731ff10fea3ec8e9b2aabceee6ddd9" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetHero" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetHero" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:06731ff10fea3ec8e9b2aabceee6ddd9" + } + ], + "event": [], + "activities": [ + { + "OperationName": "Query.hero", + "DisplayName": "Query.hero", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "resolve" + }, + { + "Key": "graphql.selection.name", + "Value": "hero" + }, + { + "Key": "graphql.selection.path", + "Value": "hero" + }, + { + "Key": "graphql.selection.field.name", + "Value": "hero" + }, + { + "Key": "graphql.selection.field.coordinate", + "Value": "Query.hero" + }, + { + "Key": "graphql.selection.field.parent_type", + "Value": "Query" + } + ], + "event": [] + } + ] + } + ] + }, + { + "OperationName": "Format HTTP Response", + "DisplayName": "Format HTTP Response", + "Status": "Ok", + "tags": [], + "event": [] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_with_extensions_map.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.RequestDetails_DocumentOnly_IncludesDocumentTag.snap similarity index 51% rename from src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_with_extensions_map.snap rename to src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.RequestDetails_DocumentOnly_IncludesDocumentTag.snap index 3b5136f879a..a8ba3e91332 100644 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_with_extensions_map.snap +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.RequestDetails_DocumentOnly_IncludesDocumentTag.snap @@ -5,6 +5,10 @@ "DisplayName": "GraphQL HTTP POST", "Status": "Ok", "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, { "Key": "graphql.schema.name", "Value": "_Default" @@ -14,40 +18,31 @@ "Value": "single" }, { - "Key": "graphql.http.request.extensions", - "Value": "{\"test\":\"abc\"}" + "Key": "graphql.http.request.query.body", + "Value": "{\n hero {\n name\n }\n}" } ], "event": [], "activities": [ { - "OperationName": "ParseHttpRequest", + "OperationName": "Parse HTTP Request", "DisplayName": "Parse HTTP Request", "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], + "tags": [], "event": [] }, { - "OperationName": "ExecuteRequest", - "DisplayName": "query { hero }", - "Status": "Unset", + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", "tags": [ { - "Key": "graphql.document.id", - "Value": "1d4bca4d0dff630390ddf48e9085589d" + "Key": "graphql.operation.type", + "Value": "query" }, { "Key": "graphql.document.hash", - "Value": "cc68dfd8c0c54a586a03c35296c5d1f9" - }, - { - "Key": "graphql.operation.id", - "Value": "_Default-1-1d4bca4d0dff630390ddf48e9085589d" + "Value": "md5:acb8d5d513c260b3cef3e3a12b0e29af" } ], "event": [ @@ -62,93 +57,97 @@ ], "activities": [ { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", "Status": "Ok", "tags": [ { - "Key": "graphql.document.id", - "Value": "1d4bca4d0dff630390ddf48e9085589d" + "Key": "graphql.processing.type", + "Value": "validate" }, { "Key": "graphql.document.hash", - "Value": "cc68dfd8c0c54a586a03c35296c5d1f9" - }, - { - "Key": "otel.status_code", - "Value": "OK" + "Value": "md5:acb8d5d513c260b3cef3e3a12b0e29af" } ], "event": [] }, { - "OperationName": "AnalyzeOperationCost", - "DisplayName": "Analyze Operation Complexity", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.operation.id", - "Value": "1d4bca4d0dff630390ddf48e9085589d" - } - ], - "event": [] - }, - { - "OperationName": "CompileOperation", - "DisplayName": "Compile Operation", + "OperationName": "GraphQL Complexity Analysis", + "DisplayName": "GraphQL Complexity Analysis", "Status": "Ok", "tags": [ { - "Key": "otel.status_code", - "Value": "OK" + "Key": "graphql.document.hash", + "Value": "md5:acb8d5d513c260b3cef3e3a12b0e29af" + }, + { + "Key": "graphql.operation.fieldCost", + "Value": 1.0 + }, + { + "Key": "graphql.operation.typeCost", + "Value": 2.0 } ], "event": [] }, { - "OperationName": "CoerceVariables", - "DisplayName": "Coerce Variable", + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", "Status": "Ok", "tags": [ { - "Key": "otel.status_code", - "Value": "OK" + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:acb8d5d513c260b3cef3e3a12b0e29af" + }, + { + "Key": "graphql.operation.type", + "Value": "query" } ], "event": [] }, { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", "Status": "Ok", "tags": [ { - "Key": "otel.status_code", - "Value": "OK" + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:acb8d5d513c260b3cef3e3a12b0e29af" } ], "event": [], "activities": [ { - "OperationName": "ResolveFieldValue", - "DisplayName": "/hero", + "OperationName": "Query.hero", + "DisplayName": "Query.hero", "Status": "Ok", "tags": [ { - "Key": "graphql.selection.name", - "Value": "hero" + "Key": "graphql.processing.type", + "Value": "resolve" }, { - "Key": "graphql.selection.type", - "Value": "Character" + "Key": "graphql.selection.name", + "Value": "hero" }, { "Key": "graphql.selection.path", - "Value": "/hero" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/hero" + "Value": "hero" }, { "Key": "graphql.selection.field.name", @@ -159,12 +158,8 @@ "Value": "Query.hero" }, { - "Key": "graphql.selection.field.declaringType", + "Key": "graphql.selection.field.parent_type", "Value": "Query" - }, - { - "Key": "otel.status_code", - "Value": "OK" } ], "event": [] @@ -174,7 +169,7 @@ ] }, { - "OperationName": "FormatHttpResponse", + "OperationName": "Format HTTP Response", "DisplayName": "Format HTTP Response", "Status": "Ok", "tags": [], diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.RequestDetails_None_ExcludesAllDetails.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.RequestDetails_None_ExcludesAllDetails.snap new file mode 100644 index 00000000000..d79cffdd6df --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.RequestDetails_None_ExcludesAllDetails.snap @@ -0,0 +1,209 @@ +{ + "activities": [ + { + "OperationName": "ExecuteHttpRequest", + "DisplayName": "GraphQL HTTP POST", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, + { + "Key": "graphql.schema.name", + "Value": "_Default" + } + ], + "event": [], + "activities": [ + { + "OperationName": "Parse HTTP Request", + "DisplayName": "Parse HTTP Request", + "Status": "Ok", + "tags": [], + "event": [] + }, + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetHero" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:9335388c19fd2e230b6294a596d13a93" + } + ], + "event": [ + { + "Name": "AddedOperationToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:9335388c19fd2e230b6294a596d13a93" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Complexity Analysis", + "DisplayName": "GraphQL Complexity Analysis", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:9335388c19fd2e230b6294a596d13a93" + }, + { + "Key": "graphql.operation.fieldCost", + "Value": 1.0 + }, + { + "Key": "graphql.operation.typeCost", + "Value": 2.0 + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:9335388c19fd2e230b6294a596d13a93" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetHero" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Variable Coercion", + "DisplayName": "GraphQL Variable Coercion", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "variable_coercion" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetHero" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:9335388c19fd2e230b6294a596d13a93" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetHero" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:9335388c19fd2e230b6294a596d13a93" + } + ], + "event": [], + "activities": [ + { + "OperationName": "Query.hero", + "DisplayName": "Query.hero", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "resolve" + }, + { + "Key": "graphql.selection.name", + "Value": "hero" + }, + { + "Key": "graphql.selection.path", + "Value": "hero" + }, + { + "Key": "graphql.selection.field.name", + "Value": "hero" + }, + { + "Key": "graphql.selection.field.coordinate", + "Value": "Query.hero" + }, + { + "Key": "graphql.selection.field.parent_type", + "Value": "Query" + } + ], + "event": [] + } + ] + } + ] + }, + { + "OperationName": "Format HTTP Response", + "DisplayName": "Format HTTP Response", + "Status": "Ok", + "tags": [], + "event": [] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Allow_document_to_be_captured.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Allow_document_to_be_captured.snap deleted file mode 100644 index 523e91ae3ae..00000000000 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Allow_document_to_be_captured.snap +++ /dev/null @@ -1,132 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "query SayHelloOperation { sayHello }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "6af18618ae20c266f6ffc352b78cb69b" - }, - { - "Key": "graphql.document.hash", - "Value": "6af18618ae20c266f6ffc352b78cb69b" - }, - { - "Key": "graphql.operation.id", - "Value": "_Default-1-6af18618ae20c266f6ffc352b78cb69b" - }, - { - "Key": "graphql.operation.name", - "Value": "SayHelloOperation" - }, - { - "Key": "graphql.document.body", - "Value": "query SayHelloOperation {\n sayHello\n}" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "6af18618ae20c266f6ffc352b78cb69b" - }, - { - "Key": "graphql.document.hash", - "Value": "6af18618ae20c266f6ffc352b78cb69b" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "CompileOperation", - "DisplayName": "Compile Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation SayHelloOperation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ResolveFieldValue", - "DisplayName": "/sayHello", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.selection.name", - "Value": "sayHello" - }, - { - "Key": "graphql.selection.type", - "Value": "String!" - }, - { - "Key": "graphql.selection.path", - "Value": "/sayHello" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/sayHello" - }, - { - "Key": "graphql.selection.field.name", - "Value": "sayHello" - }, - { - "Key": "graphql.selection.field.coordinate", - "Value": "SimpleQuery.sayHello" - }, - { - "Key": "graphql.selection.field.declaringType", - "Value": "SimpleQuery" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Cause_a_resolver_error_that_deletes_the_whole_result.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Cause_a_resolver_error_that_deletes_the_whole_result.snap deleted file mode 100644 index 121d1b09cd0..00000000000 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Cause_a_resolver_error_that_deletes_the_whole_result.snap +++ /dev/null @@ -1,154 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "query SayHelloOperation { causeFatalError }", - "Status": "Error", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "851fb754d9ba6b5cc5a55ebcbea2621d" - }, - { - "Key": "graphql.document.hash", - "Value": "851fb754d9ba6b5cc5a55ebcbea2621d" - }, - { - "Key": "graphql.operation.id", - "Value": "_Default-1-851fb754d9ba6b5cc5a55ebcbea2621d" - }, - { - "Key": "graphql.operation.name", - "Value": "SayHelloOperation" - }, - { - "Key": "graphql.document.body", - "Value": "query SayHelloOperation {\n causeFatalError\n}" - }, - { - "Key": "otel.status_code", - "Value": "ERROR" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "851fb754d9ba6b5cc5a55ebcbea2621d" - }, - { - "Key": "graphql.document.hash", - "Value": "851fb754d9ba6b5cc5a55ebcbea2621d" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "CompileOperation", - "DisplayName": "Compile Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation SayHelloOperation", - "Status": "Error", - "tags": [ - { - "Key": "otel.status_code", - "Value": "ERROR" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ResolveFieldValue", - "DisplayName": "/causeFatalError", - "Status": "Error", - "tags": [ - { - "Key": "graphql.selection.name", - "Value": "causeFatalError" - }, - { - "Key": "graphql.selection.type", - "Value": "String!" - }, - { - "Key": "graphql.selection.path", - "Value": "/causeFatalError" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/causeFatalError" - }, - { - "Key": "graphql.selection.field.name", - "Value": "causeFatalError" - }, - { - "Key": "graphql.selection.field.coordinate", - "Value": "SimpleQuery.causeFatalError" - }, - { - "Key": "graphql.selection.field.declaringType", - "Value": "SimpleQuery" - }, - { - "Key": "otel.status_code", - "Value": "ERROR" - } - ], - "event": [ - { - "Name": "exception", - "Tags": [ - { - "Key": "exception.message", - "Value": "fail" - }, - { - "Key": "exception.type", - "Value": "GRAPHQL_ERROR" - }, - { - "Key": "graphql.error.path", - "Value": "/causeFatalError" - } - ] - } - ] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Cause_a_resolver_error_that_deletes_the_whole_result_deep.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Cause_a_resolver_error_that_deletes_the_whole_result_deep.snap deleted file mode 100644 index 158121c23ce..00000000000 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Cause_a_resolver_error_that_deletes_the_whole_result_deep.snap +++ /dev/null @@ -1,84 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "Execute Request", - "Status": "Error", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "803df9346db185e9dc0b22dd3909aa70" - }, - { - "Key": "graphql.document.hash", - "Value": "803df9346db185e9dc0b22dd3909aa70" - }, - { - "Key": "graphql.document.body", - "Value": "query SayHelloOperation {\n deep {\n deeper {\n deeps {\n deeper {\n causeFatalError\n }\n }\n }\n }\n}" - }, - { - "Key": "otel.status_code", - "Value": "ERROR" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Error", - "tags": [ - { - "Key": "otel.status_code", - "Value": "ERROR" - }, - { - "Key": "graphql.document.id", - "Value": "803df9346db185e9dc0b22dd3909aa70" - }, - { - "Key": "graphql.document.hash", - "Value": "803df9346db185e9dc0b22dd3909aa70" - } - ], - "event": [ - { - "Name": "exception", - "Tags": [ - { - "Key": "exception.message", - "Value": "The field `causeFatalError` does not exist on the type `Deeper`." - }, - { - "Key": "exception.type", - "Value": "GRAPHQL_ERROR" - }, - { - "Key": "graphql.error.location.column", - "Value": 21 - }, - { - "Key": "graphql.error.location.line", - "Value": 6 - } - ] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_operation_display_name_with_1_field.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_operation_display_name_with_1_field.snap deleted file mode 100644 index 4f621b6ca2d..00000000000 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_operation_display_name_with_1_field.snap +++ /dev/null @@ -1,124 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "query { a }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "452ea802c4d1bf2a81a7411b0b361d9f" - }, - { - "Key": "graphql.document.hash", - "Value": "452ea802c4d1bf2a81a7411b0b361d9f" - }, - { - "Key": "graphql.operation.id", - "Value": "_Default-1-452ea802c4d1bf2a81a7411b0b361d9f" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "452ea802c4d1bf2a81a7411b0b361d9f" - }, - { - "Key": "graphql.document.hash", - "Value": "452ea802c4d1bf2a81a7411b0b361d9f" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "CompileOperation", - "DisplayName": "Compile Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ResolveFieldValue", - "DisplayName": "/a", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.selection.name", - "Value": "a" - }, - { - "Key": "graphql.selection.type", - "Value": "String!" - }, - { - "Key": "graphql.selection.path", - "Value": "/a" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/a" - }, - { - "Key": "graphql.selection.field.name", - "Value": "sayHello" - }, - { - "Key": "graphql.selection.field.coordinate", - "Value": "SimpleQuery.sayHello" - }, - { - "Key": "graphql.selection.field.declaringType", - "Value": "SimpleQuery" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_operation_display_name_with_1_field_and_op.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_operation_display_name_with_1_field_and_op.snap deleted file mode 100644 index 6adee85c424..00000000000 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_operation_display_name_with_1_field_and_op.snap +++ /dev/null @@ -1,128 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "query GetA { a }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "cee0e2939ece72d650cb0331f4be4669" - }, - { - "Key": "graphql.document.hash", - "Value": "cee0e2939ece72d650cb0331f4be4669" - }, - { - "Key": "graphql.operation.id", - "Value": "_Default-1-cee0e2939ece72d650cb0331f4be4669" - }, - { - "Key": "graphql.operation.name", - "Value": "GetA" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "cee0e2939ece72d650cb0331f4be4669" - }, - { - "Key": "graphql.document.hash", - "Value": "cee0e2939ece72d650cb0331f4be4669" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "CompileOperation", - "DisplayName": "Compile Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation GetA", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ResolveFieldValue", - "DisplayName": "/a", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.selection.name", - "Value": "a" - }, - { - "Key": "graphql.selection.type", - "Value": "String!" - }, - { - "Key": "graphql.selection.path", - "Value": "/a" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/a" - }, - { - "Key": "graphql.selection.field.name", - "Value": "sayHello" - }, - { - "Key": "graphql.selection.field.coordinate", - "Value": "SimpleQuery.sayHello" - }, - { - "Key": "graphql.selection.field.declaringType", - "Value": "SimpleQuery" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_operation_display_name_with_3_field.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_operation_display_name_with_3_field.snap deleted file mode 100644 index 321f860a750..00000000000 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_operation_display_name_with_3_field.snap +++ /dev/null @@ -1,204 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "query { a b c }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "2e55fbe10a9e3ddf26935a8f8d15ec89" - }, - { - "Key": "graphql.document.hash", - "Value": "2e55fbe10a9e3ddf26935a8f8d15ec89" - }, - { - "Key": "graphql.operation.id", - "Value": "_Default-1-2e55fbe10a9e3ddf26935a8f8d15ec89" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "2e55fbe10a9e3ddf26935a8f8d15ec89" - }, - { - "Key": "graphql.document.hash", - "Value": "2e55fbe10a9e3ddf26935a8f8d15ec89" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "CompileOperation", - "DisplayName": "Compile Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ResolveFieldValue", - "DisplayName": "/a", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.selection.name", - "Value": "a" - }, - { - "Key": "graphql.selection.type", - "Value": "String!" - }, - { - "Key": "graphql.selection.path", - "Value": "/a" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/a" - }, - { - "Key": "graphql.selection.field.name", - "Value": "sayHello" - }, - { - "Key": "graphql.selection.field.coordinate", - "Value": "SimpleQuery.sayHello" - }, - { - "Key": "graphql.selection.field.declaringType", - "Value": "SimpleQuery" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ResolveFieldValue", - "DisplayName": "/b", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.selection.name", - "Value": "b" - }, - { - "Key": "graphql.selection.type", - "Value": "String!" - }, - { - "Key": "graphql.selection.path", - "Value": "/b" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/b" - }, - { - "Key": "graphql.selection.field.name", - "Value": "sayHello" - }, - { - "Key": "graphql.selection.field.coordinate", - "Value": "SimpleQuery.sayHello" - }, - { - "Key": "graphql.selection.field.declaringType", - "Value": "SimpleQuery" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ResolveFieldValue", - "DisplayName": "/c", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.selection.name", - "Value": "c" - }, - { - "Key": "graphql.selection.type", - "Value": "String!" - }, - { - "Key": "graphql.selection.path", - "Value": "/c" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/c" - }, - { - "Key": "graphql.selection.field.name", - "Value": "sayHello" - }, - { - "Key": "graphql.selection.field.coordinate", - "Value": "SimpleQuery.sayHello" - }, - { - "Key": "graphql.selection.field.declaringType", - "Value": "SimpleQuery" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_operation_display_name_with_4_field.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_operation_display_name_with_4_field.snap deleted file mode 100644 index 677ff97b674..00000000000 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_operation_display_name_with_4_field.snap +++ /dev/null @@ -1,244 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "query { a b c ... }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "a5f924bb2f5f8651014e92e1cc2428c7" - }, - { - "Key": "graphql.document.hash", - "Value": "a5f924bb2f5f8651014e92e1cc2428c7" - }, - { - "Key": "graphql.operation.id", - "Value": "_Default-1-a5f924bb2f5f8651014e92e1cc2428c7" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "a5f924bb2f5f8651014e92e1cc2428c7" - }, - { - "Key": "graphql.document.hash", - "Value": "a5f924bb2f5f8651014e92e1cc2428c7" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "CompileOperation", - "DisplayName": "Compile Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ResolveFieldValue", - "DisplayName": "/a", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.selection.name", - "Value": "a" - }, - { - "Key": "graphql.selection.type", - "Value": "String!" - }, - { - "Key": "graphql.selection.path", - "Value": "/a" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/a" - }, - { - "Key": "graphql.selection.field.name", - "Value": "sayHello" - }, - { - "Key": "graphql.selection.field.coordinate", - "Value": "SimpleQuery.sayHello" - }, - { - "Key": "graphql.selection.field.declaringType", - "Value": "SimpleQuery" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ResolveFieldValue", - "DisplayName": "/b", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.selection.name", - "Value": "b" - }, - { - "Key": "graphql.selection.type", - "Value": "String!" - }, - { - "Key": "graphql.selection.path", - "Value": "/b" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/b" - }, - { - "Key": "graphql.selection.field.name", - "Value": "sayHello" - }, - { - "Key": "graphql.selection.field.coordinate", - "Value": "SimpleQuery.sayHello" - }, - { - "Key": "graphql.selection.field.declaringType", - "Value": "SimpleQuery" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ResolveFieldValue", - "DisplayName": "/c", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.selection.name", - "Value": "c" - }, - { - "Key": "graphql.selection.type", - "Value": "String!" - }, - { - "Key": "graphql.selection.path", - "Value": "/c" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/c" - }, - { - "Key": "graphql.selection.field.name", - "Value": "sayHello" - }, - { - "Key": "graphql.selection.field.coordinate", - "Value": "SimpleQuery.sayHello" - }, - { - "Key": "graphql.selection.field.declaringType", - "Value": "SimpleQuery" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ResolveFieldValue", - "DisplayName": "/d", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.selection.name", - "Value": "d" - }, - { - "Key": "graphql.selection.type", - "Value": "String!" - }, - { - "Key": "graphql.selection.path", - "Value": "/d" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/d" - }, - { - "Key": "graphql.selection.field.name", - "Value": "sayHello" - }, - { - "Key": "graphql.selection.field.coordinate", - "Value": "SimpleQuery.sayHello" - }, - { - "Key": "graphql.selection.field.declaringType", - "Value": "SimpleQuery" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Ensure_operation_name_is_used_as_request_name.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Ensure_operation_name_is_used_as_request_name.snap deleted file mode 100644 index b0ea2c00d2c..00000000000 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Ensure_operation_name_is_used_as_request_name.snap +++ /dev/null @@ -1,128 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "query SayHelloOperation { sayHello }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "6af18618ae20c266f6ffc352b78cb69b" - }, - { - "Key": "graphql.document.hash", - "Value": "6af18618ae20c266f6ffc352b78cb69b" - }, - { - "Key": "graphql.operation.id", - "Value": "_Default-1-6af18618ae20c266f6ffc352b78cb69b" - }, - { - "Key": "graphql.operation.name", - "Value": "SayHelloOperation" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "6af18618ae20c266f6ffc352b78cb69b" - }, - { - "Key": "graphql.document.hash", - "Value": "6af18618ae20c266f6ffc352b78cb69b" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "CompileOperation", - "DisplayName": "Compile Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation SayHelloOperation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ResolveFieldValue", - "DisplayName": "/sayHello", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.selection.name", - "Value": "sayHello" - }, - { - "Key": "graphql.selection.type", - "Value": "String!" - }, - { - "Key": "graphql.selection.path", - "Value": "/sayHello" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/sayHello" - }, - { - "Key": "graphql.selection.field.name", - "Value": "sayHello" - }, - { - "Key": "graphql.selection.field.coordinate", - "Value": "SimpleQuery.sayHello" - }, - { - "Key": "graphql.selection.field.declaringType", - "Value": "SimpleQuery" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Ensure_that_the_validation_activity_has_an_error_status.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Ensure_that_the_validation_activity_has_an_error_status.snap deleted file mode 100644 index c13dbaa8b92..00000000000 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Ensure_that_the_validation_activity_has_an_error_status.snap +++ /dev/null @@ -1,84 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "Execute Request", - "Status": "Error", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "bb1d246465341a97bdc727d6cd8ead5c" - }, - { - "Key": "graphql.document.hash", - "Value": "bb1d246465341a97bdc727d6cd8ead5c" - }, - { - "Key": "graphql.document.body", - "Value": "query SayHelloOperation {\n sayHello_\n}" - }, - { - "Key": "otel.status_code", - "Value": "ERROR" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Error", - "tags": [ - { - "Key": "otel.status_code", - "Value": "ERROR" - }, - { - "Key": "graphql.document.id", - "Value": "bb1d246465341a97bdc727d6cd8ead5c" - }, - { - "Key": "graphql.document.hash", - "Value": "bb1d246465341a97bdc727d6cd8ead5c" - } - ], - "event": [ - { - "Name": "exception", - "Tags": [ - { - "Key": "exception.message", - "Value": "The field `sayHello_` does not exist on the type `SimpleQuery`." - }, - { - "Key": "exception.type", - "Value": "GRAPHQL_ERROR" - }, - { - "Key": "graphql.error.location.column", - "Value": 27 - }, - { - "Key": "graphql.error.location.line", - "Value": 1 - } - ] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_data_loader_events.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_data_loader_events.snap deleted file mode 100644 index 04ebef7195e..00000000000 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_data_loader_events.snap +++ /dev/null @@ -1,111 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "9b20745108c8de5afccc35cd56ead9fc" - }, - { - "Key": "graphql.document.hash", - "Value": "9b20745108c8de5afccc35cd56ead9fc" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "CompileOperation", - "DisplayName": "Compile Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ResolveFieldValue", - "DisplayName": "/dataLoader", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.selection.name", - "Value": "dataLoader" - }, - { - "Key": "graphql.selection.type", - "Value": "String" - }, - { - "Key": "graphql.selection.path", - "Value": "/dataLoader" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/dataLoader" - }, - { - "Key": "graphql.selection.field.name", - "Value": "dataLoader" - }, - { - "Key": "graphql.selection.field.coordinate", - "Value": "SimpleQuery.dataLoader" - }, - { - "Key": "graphql.selection.field.declaringType", - "Value": "SimpleQuery" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "BatchCoordinator", - "DisplayName": "Coordinate DataLoader Batches", - "Status": "Unset", - "tags": [], - "event": [ - { - "Name": "BatchEvaluated", - "Tags": [ - { - "Key": "openBatches", - "Value": 1 - } - ] - }, - { - "Name": "BatchDispatched", - "Tags": [ - { - "Key": "dispatchedBatches", - "Value": 1 - } - ] - } - ], - "activities": [ - { - "OperationName": "ExecuteBatch", - "DisplayName": "Execute CustomDataLoader Batch", - "Status": "Unset", - "tags": [], - "event": [] - } - ] - } - ] -} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_data_loader_events_with_keys.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_data_loader_events_with_keys.snap deleted file mode 100644 index 04ebef7195e..00000000000 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_data_loader_events_with_keys.snap +++ /dev/null @@ -1,111 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "9b20745108c8de5afccc35cd56ead9fc" - }, - { - "Key": "graphql.document.hash", - "Value": "9b20745108c8de5afccc35cd56ead9fc" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "CompileOperation", - "DisplayName": "Compile Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ResolveFieldValue", - "DisplayName": "/dataLoader", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.selection.name", - "Value": "dataLoader" - }, - { - "Key": "graphql.selection.type", - "Value": "String" - }, - { - "Key": "graphql.selection.path", - "Value": "/dataLoader" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/dataLoader" - }, - { - "Key": "graphql.selection.field.name", - "Value": "dataLoader" - }, - { - "Key": "graphql.selection.field.coordinate", - "Value": "SimpleQuery.dataLoader" - }, - { - "Key": "graphql.selection.field.declaringType", - "Value": "SimpleQuery" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "BatchCoordinator", - "DisplayName": "Coordinate DataLoader Batches", - "Status": "Unset", - "tags": [], - "event": [ - { - "Name": "BatchEvaluated", - "Tags": [ - { - "Key": "openBatches", - "Value": 1 - } - ] - }, - { - "Name": "BatchDispatched", - "Tags": [ - { - "Key": "dispatchedBatches", - "Value": 1 - } - ] - } - ], - "activities": [ - { - "OperationName": "ExecuteBatch", - "DisplayName": "Execute CustomDataLoader Batch", - "Status": "Unset", - "tags": [], - "event": [] - } - ] - } - ] -} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_events_of_a_simple_query_detailed.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_events_of_a_simple_query_detailed.snap deleted file mode 100644 index d528c17e108..00000000000 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_events_of_a_simple_query_detailed.snap +++ /dev/null @@ -1,124 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "query { sayHello }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.document.hash", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.operation.id", - "Value": "_Default-1-f7e9989fbb67af7fa747a9983313c9e5" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.document.hash", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "CompileOperation", - "DisplayName": "Compile Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ResolveFieldValue", - "DisplayName": "/sayHello", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.selection.name", - "Value": "sayHello" - }, - { - "Key": "graphql.selection.type", - "Value": "String!" - }, - { - "Key": "graphql.selection.path", - "Value": "/sayHello" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/sayHello" - }, - { - "Key": "graphql.selection.field.name", - "Value": "sayHello" - }, - { - "Key": "graphql.selection.field.coordinate", - "Value": "SimpleQuery.sayHello" - }, - { - "Key": "graphql.selection.field.declaringType", - "Value": "SimpleQuery" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Get_SingleRequest_GetHeroName.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Get_SingleRequest_GetHeroName.snap deleted file mode 100644 index 8f7af550618..00000000000 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Get_SingleRequest_GetHeroName.snap +++ /dev/null @@ -1,170 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteHttpRequest", - "DisplayName": "GraphQL HTTP GET", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.schema.name", - "Value": "_Default" - }, - { - "Key": "graphql.http.request.type", - "Value": "single" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseHttpRequest", - "DisplayName": "Parse HTTP Request", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteRequest", - "DisplayName": "query { hero }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "530cb46cabc38757c74c05cc7a96b636" - }, - { - "Key": "graphql.document.hash", - "Value": "acb8d5d513c260b3cef3e3a12b0e29af" - }, - { - "Key": "graphql.operation.id", - "Value": "_Default-1-530cb46cabc38757c74c05cc7a96b636" - } - ], - "event": [ - { - "Name": "AddedOperationToCache", - "Tags": [] - }, - { - "Name": "AddedDocumentToCache", - "Tags": [] - } - ], - "activities": [ - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "530cb46cabc38757c74c05cc7a96b636" - }, - { - "Key": "graphql.document.hash", - "Value": "acb8d5d513c260b3cef3e3a12b0e29af" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "AnalyzeOperationCost", - "DisplayName": "Analyze Operation Complexity", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.operation.id", - "Value": "530cb46cabc38757c74c05cc7a96b636" - } - ], - "event": [] - }, - { - "OperationName": "CompileOperation", - "DisplayName": "Compile Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ResolveFieldValue", - "DisplayName": "/hero", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.selection.name", - "Value": "hero" - }, - { - "Key": "graphql.selection.type", - "Value": "Character" - }, - { - "Key": "graphql.selection.path", - "Value": "/hero" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/hero" - }, - { - "Key": "graphql.selection.field.name", - "Value": "hero" - }, - { - "Key": "graphql.selection.field.coordinate", - "Value": "Query.hero" - }, - { - "Key": "graphql.selection.field.declaringType", - "Value": "Query" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - }, - { - "OperationName": "FormatHttpResponse", - "DisplayName": "Format HTTP Response", - "Status": "Ok", - "tags": [], - "event": [] - } - ] - } - ] -} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_SingleRequest_GetHeroName.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_SingleRequest_GetHeroName.snap deleted file mode 100644 index ea5e73977d3..00000000000 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_SingleRequest_GetHeroName.snap +++ /dev/null @@ -1,170 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteHttpRequest", - "DisplayName": "GraphQL HTTP POST", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.schema.name", - "Value": "_Default" - }, - { - "Key": "graphql.http.request.type", - "Value": "single" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseHttpRequest", - "DisplayName": "Parse HTTP Request", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteRequest", - "DisplayName": "query { hero }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "a570a6bff748b5916eadf153261d9c6d" - }, - { - "Key": "graphql.document.hash", - "Value": "acb8d5d513c260b3cef3e3a12b0e29af" - }, - { - "Key": "graphql.operation.id", - "Value": "_Default-1-a570a6bff748b5916eadf153261d9c6d" - } - ], - "event": [ - { - "Name": "AddedOperationToCache", - "Tags": [] - }, - { - "Name": "AddedDocumentToCache", - "Tags": [] - } - ], - "activities": [ - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "a570a6bff748b5916eadf153261d9c6d" - }, - { - "Key": "graphql.document.hash", - "Value": "acb8d5d513c260b3cef3e3a12b0e29af" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "AnalyzeOperationCost", - "DisplayName": "Analyze Operation Complexity", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.operation.id", - "Value": "a570a6bff748b5916eadf153261d9c6d" - } - ], - "event": [] - }, - { - "OperationName": "CompileOperation", - "DisplayName": "Compile Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ResolveFieldValue", - "DisplayName": "/hero", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.selection.name", - "Value": "hero" - }, - { - "Key": "graphql.selection.type", - "Value": "Character" - }, - { - "Key": "graphql.selection.path", - "Value": "/hero" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/hero" - }, - { - "Key": "graphql.selection.field.name", - "Value": "hero" - }, - { - "Key": "graphql.selection.field.coordinate", - "Value": "Query.hero" - }, - { - "Key": "graphql.selection.field.declaringType", - "Value": "Query" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - }, - { - "OperationName": "FormatHttpResponse", - "DisplayName": "Format HTTP Response", - "Status": "Ok", - "tags": [], - "event": [] - } - ] - } - ] -} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_variables_are_not_automatically_added_to_activities.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_variables_are_not_automatically_added_to_activities.snap deleted file mode 100644 index ed74db6956e..00000000000 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_variables_are_not_automatically_added_to_activities.snap +++ /dev/null @@ -1,182 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteHttpRequest", - "DisplayName": "GraphQL HTTP POST", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.schema.name", - "Value": "_Default" - }, - { - "Key": "graphql.http.request.type", - "Value": "single" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseHttpRequest", - "DisplayName": "Parse HTTP Request", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteRequest", - "DisplayName": "query { hero }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "1d4bca4d0dff630390ddf48e9085589d" - }, - { - "Key": "graphql.document.hash", - "Value": "cc68dfd8c0c54a586a03c35296c5d1f9" - }, - { - "Key": "graphql.operation.id", - "Value": "_Default-1-1d4bca4d0dff630390ddf48e9085589d" - } - ], - "event": [ - { - "Name": "AddedOperationToCache", - "Tags": [] - }, - { - "Name": "AddedDocumentToCache", - "Tags": [] - } - ], - "activities": [ - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "1d4bca4d0dff630390ddf48e9085589d" - }, - { - "Key": "graphql.document.hash", - "Value": "cc68dfd8c0c54a586a03c35296c5d1f9" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "AnalyzeOperationCost", - "DisplayName": "Analyze Operation Complexity", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.operation.id", - "Value": "1d4bca4d0dff630390ddf48e9085589d" - } - ], - "event": [] - }, - { - "OperationName": "CompileOperation", - "DisplayName": "Compile Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "CoerceVariables", - "DisplayName": "Coerce Variable", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ResolveFieldValue", - "DisplayName": "/hero", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.selection.name", - "Value": "hero" - }, - { - "Key": "graphql.selection.type", - "Value": "Character" - }, - { - "Key": "graphql.selection.path", - "Value": "/hero" - }, - { - "Key": "graphql.selection.hierarchy", - "Value": "/hero" - }, - { - "Key": "graphql.selection.field.name", - "Value": "hero" - }, - { - "Key": "graphql.selection.field.coordinate", - "Value": "Query.hero" - }, - { - "Key": "graphql.selection.field.declaringType", - "Value": "Query" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - }, - { - "OperationName": "FormatHttpResponse", - "DisplayName": "Format HTTP Response", - "Status": "Ok", - "tags": [], - "event": [] - } - ] - } - ] -} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Parsing_error_when_rename_root_is_activated.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Parsing_error_when_rename_root_is_activated.snap deleted file mode 100644 index b0834861a59..00000000000 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Parsing_error_when_rename_root_is_activated.snap +++ /dev/null @@ -1,59 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteHttpRequest", - "DisplayName": "GraphQL HTTP POST: Begin Parse HTTP Request", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.schema.name", - "Value": "_Default" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseHttpRequest", - "DisplayName": "Parse HTTP Request", - "Status": "Error", - "tags": [ - { - "Key": "otel.status_code", - "Value": "ERROR" - } - ], - "event": [ - { - "Name": "exception", - "Tags": [ - { - "Key": "exception.message", - "Value": "Expected a `Name`-token, but found a `Integer`-token." - }, - { - "Key": "exception.type", - "Value": "HC0011" - }, - { - "Key": "graphql.error.location.column", - "Value": 21 - }, - { - "Key": "graphql.error.location.line", - "Value": 3 - } - ] - } - ] - }, - { - "OperationName": "FormatHttpResponse", - "DisplayName": "Format HTTP Response", - "Status": "Ok", - "tags": [], - "event": [] - } - ] - } - ] -} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Validation_error_when_rename_root_is_activated.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Validation_error_when_rename_root_is_activated.snap deleted file mode 100644 index 97a0396c0dc..00000000000 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Validation_error_when_rename_root_is_activated.snap +++ /dev/null @@ -1,105 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteHttpRequest", - "DisplayName": "GraphQL HTTP POST: Begin Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.schema.name", - "Value": "_Default" - }, - { - "Key": "graphql.http.request.type", - "Value": "single" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseHttpRequest", - "DisplayName": "Parse HTTP Request", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteRequest", - "DisplayName": "Execute Request", - "Status": "Error", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "e2b13c5332af8a70da160fcb96894e5c" - }, - { - "Key": "graphql.document.hash", - "Value": "346f68539881f0624dca2927281d1a2f" - }, - { - "Key": "otel.status_code", - "Value": "ERROR" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Error", - "tags": [ - { - "Key": "otel.status_code", - "Value": "ERROR" - }, - { - "Key": "graphql.document.id", - "Value": "e2b13c5332af8a70da160fcb96894e5c" - }, - { - "Key": "graphql.document.hash", - "Value": "346f68539881f0624dca2927281d1a2f" - } - ], - "event": [ - { - "Name": "exception", - "Tags": [ - { - "Key": "exception.message", - "Value": "The field `abc` does not exist on the type `Query`." - }, - { - "Key": "exception.type", - "Value": "GRAPHQL_ERROR" - }, - { - "Key": "graphql.error.location.column", - "Value": 21 - }, - { - "Key": "graphql.error.location.line", - "Value": 3 - } - ] - } - ] - } - ] - }, - { - "OperationName": "FormatHttpResponse", - "DisplayName": "Format HTTP Response", - "Status": "Ok", - "tags": [], - "event": [] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/src/Fusion.Aspire/SchemaComposition.cs b/src/HotChocolate/Fusion/src/Fusion.Aspire/SchemaComposition.cs index e490475a394..8fc4e228186 100644 --- a/src/HotChocolate/Fusion/src/Fusion.Aspire/SchemaComposition.cs +++ b/src/HotChocolate/Fusion/src/Fusion.Aspire/SchemaComposition.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using IOPath = System.IO.Path; namespace HotChocolate.Fusion.Aspire; @@ -94,7 +95,7 @@ private async Task ComposeSchemaAsync( try { var gatewayDirectory = GetProjectPath(compositionResource)!; - var archivePath = Path.Combine(Path.GetDirectoryName(gatewayDirectory)!, settings.OutputFileName); + var archivePath = IOPath.Combine(IOPath.GetDirectoryName(gatewayDirectory)!, settings.OutputFileName); return await ComposeSchemaAsync(archivePath, sourceSchemas, settings, cancellationToken); } finally @@ -277,7 +278,7 @@ private List GetReferencedResources( // For file schemas, settings file is named after the schema file // e.g., "foo.graphql" -> "foo-settings.json" var schemaFileName = annotation.SchemaPath ?? "schema.graphql"; - var settingsFileName = $"{Path.GetFileNameWithoutExtension(schemaFileName)}-settings.json"; + var settingsFileName = $"{IOPath.GetFileNameWithoutExtension(schemaFileName)}-settings.json"; var schemaSettings = await GetSourceSchemaSettingsAsync(resource, settingsFileName, cancellationToken); if (schemaSettings == null) @@ -309,8 +310,8 @@ private List GetReferencedResources( return null; } - var projectDirectory = Path.GetDirectoryName(projectPath); - var settingsFile = Path.Combine(projectDirectory!, settingsFileName); + var projectDirectory = IOPath.GetDirectoryName(projectPath); + var settingsFile = IOPath.Combine(projectDirectory!, settingsFileName); if (!File.Exists(settingsFile)) { @@ -373,8 +374,8 @@ private List GetReferencedResources( return null; } - var projectDirectory = Path.GetDirectoryName(projectPath); - var schemaFile = Path.Combine(projectDirectory!, fileName ?? "schema.graphql"); + var projectDirectory = IOPath.GetDirectoryName(projectPath); + var schemaFile = IOPath.Combine(projectDirectory!, fileName ?? "schema.graphql"); if (!File.Exists(schemaFile)) { @@ -487,7 +488,7 @@ private async Task ComposeSchemaAsync( GraphQLSchemaCompositionAnnotation settings, CancellationToken cancellationToken) { - var tempArchivePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + var tempArchivePath = IOPath.Combine(IOPath.GetTempPath(), IOPath.GetRandomFileName()); try { diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/ContextKeys.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/ContextKeys.cs deleted file mode 100644 index f35bd4946f0..00000000000 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/ContextKeys.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace HotChocolate.Fusion.Diagnostics; - -internal static class ContextKeys -{ - public const string HttpRequestActivity = "HotChocolate.Fusion.Diagnostics.HttpRequest"; - public const string ParseHttpRequestActivity = "HotChocolate.Fusion.Diagnostics.ParseHttpRequest"; - public const string FormatHttpResponseActivity = "HotChocolate.Fusion.Diagnostics.FormatHttpResponse"; - public const string RequestActivity = "HotChocolate.Fusion.Diagnostics.Request"; - public const string ValidateActivity = "HotChocolate.Fusion.Diagnostics.Validate"; -} diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Extensions/DiagnosticsFusionGatewayBuilderExtensions.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Extensions/DiagnosticsFusionGatewayBuilderExtensions.cs index 689f319827a..d475f4fd2a6 100644 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Extensions/DiagnosticsFusionGatewayBuilderExtensions.cs +++ b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Extensions/DiagnosticsFusionGatewayBuilderExtensions.cs @@ -35,13 +35,13 @@ public static IFusionGatewayBuilder AddInstrumentation( builder.AddApplicationService(); builder.AddDiagnosticEventListener( - sp => new ActivityFusionExecutionDiagnosticEventListener( + sp => new FusionActivityExecutionDiagnosticEventListener( sp.GetService() ?? sp.GetRequiredService(), sp.GetRequiredService())); builder.AddDiagnosticEventListener( - sp => new ActivityServerDiagnosticListener( + sp => new FusionActivityServerDiagnosticListener( sp.GetService() ?? sp.GetRequiredService(), sp.GetRequiredService())); @@ -49,8 +49,5 @@ public static IFusionGatewayBuilder AddInstrumentation( return builder; } - private sealed class InternalActivityEnricher( - ObjectPool stringBuilderPool, - InstrumentationOptions options) - : FusionActivityEnricher(stringBuilderPool, options); + private sealed class InternalActivityEnricher(InstrumentationOptions options) : FusionActivityEnricher(options); } diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/FusionActivityEnricher.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/FusionActivityEnricher.cs index e5afc4e0c28..8a430c4b744 100644 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/FusionActivityEnricher.cs +++ b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/FusionActivityEnricher.cs @@ -1,592 +1,78 @@ using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Text; -using System.Text.Json; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.ObjectPool; -using HotChocolate.AspNetCore.Instrumentation; +using HotChocolate.Diagnostics; using HotChocolate.Execution; using HotChocolate.Fusion.Execution; using HotChocolate.Fusion.Execution.Nodes; -using HotChocolate.Language; -using HotChocolate.Language.Utilities; -using OpenTelemetry.Trace; -using static HotChocolate.Fusion.Diagnostics.SemanticConventions; -using static HotChocolate.WellKnownContextData; namespace HotChocolate.Fusion.Diagnostics; /// -/// The activity enricher is used to add information to the activity spans. -/// You can inherit from this class and override the enricher methods to provide more or -/// less information. +/// The activity enricher allows adding additional information to the activity spans +/// created by the Fusion diagnostics system. +/// You can inherit from this class and override the enricher methods to add +/// additional information to the spans. /// -public class FusionActivityEnricher +public class FusionActivityEnricher(InstrumentationOptions options) : ActivityEnricherBase { - private readonly InstrumentationOptions _options; - private readonly ConditionalWeakTable _queryCache = []; + protected InstrumentationOptions Options { get; } = options; - /// - /// Initializes a new instance of . - /// - /// - /// - protected FusionActivityEnricher( - ObjectPool stringBuilderPool, - InstrumentationOptions options) - { - StringBuilderPool = stringBuilderPool; - _options = options; - } - - /// - /// Gets the pool used by this enricher. - /// - protected ObjectPool StringBuilderPool { get; } - - public virtual void EnrichExecuteHttpRequest( - HttpContext context, - HttpRequestKind kind, - Activity activity) - { - switch (kind) - { - case HttpRequestKind.HttpPost: - activity.DisplayName = "GraphQL HTTP POST"; - break; - case HttpRequestKind.HttpMultiPart: - activity.DisplayName = "GraphQL HTTP POST MultiPart"; - break; - case HttpRequestKind.HttpGet: - activity.DisplayName = "GraphQL HTTP GET"; - break; - case HttpRequestKind.HttpGetSchema: - activity.DisplayName = "GraphQL HTTP GET SDL"; - break; - } - - if (_options.RenameRootActivity) - { - UpdateRootActivityName(activity, $"Begin {activity.DisplayName}"); - } - - activity.SetTag("graphql.http.kind", kind); - - var isDefault = false; - if (!(context.Items.TryGetValue(SchemaName, out var value) - && value is string schemaName)) - { - schemaName = ISchemaDefinition.DefaultName; - isDefault = true; - } - - activity.SetTag("graphql.schema.name", schemaName); - activity.SetTag("graphql.schema.isDefault", isDefault); - } - - public virtual void EnrichSingleRequest( - HttpContext context, - GraphQLRequest request, - Activity activity) - { - activity.SetTag("graphql.http.request.type", "single"); - - if (request.DocumentId is not null - && (_options.RequestDetails & RequestDetails.Id) == RequestDetails.Id) - { - activity.SetTag("graphql.http.request.query.id", request.DocumentId.Value); - } - - if (request.DocumentHash is not null - && (_options.RequestDetails & RequestDetails.Hash) == RequestDetails.Hash) - { - activity.SetTag("graphql.http.request.query.hash", request.DocumentHash.Value); - } - - if (request.Document is not null - && (_options.RequestDetails & RequestDetails.Query) == RequestDetails.Query) - { - if (!_queryCache.TryGetValue(request.Document, out var query)) - { - query = request.Document.Print(); - _queryCache.Add(request.Document, query); - } - - activity.SetTag("graphql.http.request.query.body", query); - } - - if (request.OperationName is not null - && (_options.RequestDetails & RequestDetails.Operation) == RequestDetails.Operation) - { - activity.SetTag("graphql.http.request.operation", request.OperationName); - } - - if (request.Variables is not null - && (_options.RequestDetails & RequestDetails.Variables) == RequestDetails.Variables) - { - EnrichRequestVariables(context, request, request.Variables, activity); - } - - if (request.Extensions is not null - && (_options.RequestDetails & RequestDetails.Extensions) == RequestDetails.Extensions) - { - EnrichRequestExtensions(context, request, request.Extensions, activity); - } - } - - public virtual void EnrichBatchRequest( - HttpContext context, - IReadOnlyList batch, - Activity activity) - { - activity.SetTag("graphql.http.request.type", "batch"); - - for (var i = 0; i < batch.Count; i++) - { - var request = batch[i]; - - if (request.DocumentId is not null - && (_options.RequestDetails & RequestDetails.Id) == RequestDetails.Id) - { - activity.SetTag($"graphql.http.request[{i}].query.id", request.DocumentId.Value); - } - - if (request.DocumentHash is not null - && (_options.RequestDetails & RequestDetails.Hash) == RequestDetails.Hash) - { - activity.SetTag($"graphql.http.request[{i}].query.hash", request.DocumentHash.Value); - } - - if (request.Document is not null - && (_options.RequestDetails & RequestDetails.Query) == RequestDetails.Query) - { - activity.SetTag($"graphql.http.request[{i}].query.body", request.Document.Print()); - } - - if (request.OperationName is not null - && (_options.RequestDetails & RequestDetails.Operation) == RequestDetails.Operation) - { - activity.SetTag($"graphql.http.request[{i}].operation", request.OperationName); - } - - if (request.Variables is not null - && (_options.RequestDetails & RequestDetails.Variables) == RequestDetails.Variables) - { - EnrichBatchVariables(context, request, request.Variables, i, activity); - } - - if (request.Extensions is not null - && (_options.RequestDetails & RequestDetails.Extensions) == RequestDetails.Extensions) - { - EnrichBatchExtensions(context, request, request.Extensions, i, activity); - } - } - } - - public virtual void EnrichOperationBatchRequest( - HttpContext context, - GraphQLRequest request, - IReadOnlyList operations, - Activity activity) - { - activity.SetTag("graphql.http.request.type", "operationBatch"); - - if (request.DocumentId is not null - && (_options.RequestDetails & RequestDetails.Id) == RequestDetails.Id) - { - activity.SetTag("graphql.http.request.query.id", request.DocumentId.Value); - } - - if (request.DocumentHash is not null - && (_options.RequestDetails & RequestDetails.Hash) == RequestDetails.Hash) - { - activity.SetTag("graphql.http.request.query.hash", request.DocumentHash.Value); - } - - if (request.Document is not null - && (_options.RequestDetails & RequestDetails.Query) == RequestDetails.Query) - { - activity.SetTag("graphql.http.request.query.body", request.Document.Print()); - } - - if (request.OperationName is not null - && (_options.RequestDetails & RequestDetails.Operation) == RequestDetails.Operation) - { - activity.SetTag("graphql.http.request.operations", string.Join(" -> ", operations)); - } - - if (request.Variables is not null - && (_options.RequestDetails & RequestDetails.Variables) == RequestDetails.Variables) - { - EnrichRequestVariables(context, request, request.Variables, activity); - } - - if (request.Extensions is not null - && (_options.RequestDetails & RequestDetails.Extensions) == RequestDetails.Extensions) - { - EnrichRequestExtensions(context, request, request.Extensions, activity); - } - } - - protected virtual void EnrichRequestVariables( - HttpContext context, - GraphQLRequest request, - JsonDocument variables, - Activity activity) - { - activity.SetTag("graphql.http.request.variables", variables.RootElement.ToString()); - } - - protected virtual void EnrichBatchVariables( - HttpContext context, - GraphQLRequest request, - JsonDocument variables, - int index, - Activity activity) - { - activity.SetTag($"graphql.http.request[{index}].variables", variables.RootElement.ToString()); - } - - protected virtual void EnrichRequestExtensions( - HttpContext context, - GraphQLRequest request, - JsonDocument extensions, + public virtual void EnrichPlanOperation( + RequestContext context, + string operationPlanId, Activity activity) { - try - { - activity.SetTag( - "graphql.http.request.extensions", - extensions.RootElement.ToString()); - } - catch - { - // Ignore any errors - } } - protected virtual void EnrichBatchExtensions( - HttpContext context, - GraphQLRequest request, - JsonDocument extensions, - int index, + public virtual void EnrichExecutePlanNode( + OperationPlanContext context, + ExecutionNode node, + string? schemaName, Activity activity) { - try - { - activity.SetTag( - $"graphql.http.request[{index}].extensions", - extensions.RootElement.ToString()); - } - catch - { - // Ignore any errors - } } - public virtual void EnrichHttpRequestError( - HttpContext context, - IError error, - Activity activity) - => EnrichError(error, activity); - - public virtual void EnrichHttpRequestError( - HttpContext context, + public virtual void EnrichExecutionNodeError( + OperationPlanContext context, + ExecutionNode node, Exception exception, Activity activity) { } - public virtual void EnrichParseHttpRequest(HttpContext context, Activity activity) - { - activity.DisplayName = "Parse HTTP Request"; - - if (_options.RenameRootActivity) - { - UpdateRootActivityName(activity, $"Begin {activity.DisplayName}"); - } - } - - public virtual void EnrichParserErrors(HttpContext context, IError error, Activity activity) - => EnrichError(error, activity); - - public virtual void EnrichFormatHttpResponse(HttpContext context, Activity activity) - { - activity.DisplayName = "Format HTTP Response"; - } - - public virtual void EnrichExecuteRequest(RequestContext context, Activity activity) - { - var plan = context.GetOperationPlan(); - var documentInfo = context.OperationDocumentInfo; - var operationDisplayName = CreateOperationDisplayName(context, plan); - - if (_options.RenameRootActivity && operationDisplayName is not null) - { - UpdateRootActivityName(activity, operationDisplayName); - } - - activity.DisplayName = operationDisplayName ?? "Execute Request"; - activity.SetTag("graphql.document.id", documentInfo.Id.Value); - activity.SetTag("graphql.document.hash", documentInfo.Hash.Value); - activity.SetTag("graphql.document.valid", documentInfo.IsValidated); - activity.SetTag("graphql.operation.id", plan?.Id); - activity.SetTag("graphql.operation.kind", plan?.Operation.Definition.Operation); - activity.SetTag("graphql.operation.name", plan?.OperationName); - - if (_options.IncludeDocument && documentInfo.Document is not null) - { - activity.SetTag("graphql.document.body", documentInfo.Document.Print()); - } - - if (context.Result is OperationResult { Errors: [_, ..] errors }) - { - activity.SetTag("graphql.errors.count", errors.Count); - } - } - - protected virtual string? CreateOperationDisplayName(RequestContext context, OperationPlan? plan) - { - if (plan is null) - { - return null; - } - - var displayName = StringBuilderPool.Get(); - - try - { - var rootSelectionSet = plan.Operation.RootSelectionSet; - var selectionCount = rootSelectionSet.Selections.Length; - - displayName.Append('{'); - displayName.Append(' '); - - foreach (var selection in rootSelectionSet.Selections[..Math.Min(3, selectionCount)]) - { - if (displayName.Length > 2) - { - displayName.Append(' '); - } - - displayName.Append(selection.ResponseName); - } - - if (rootSelectionSet.Selections.Length > 3) - { - displayName.Append(' '); - displayName.Append('.'); - displayName.Append('.'); - displayName.Append('.'); - } - - displayName.Append(' '); - displayName.Append('}'); - - if (plan.OperationName is { } name) - { - displayName.Insert(0, ' '); - displayName.Insert(0, name); - } - - displayName.Insert(0, ' '); - displayName.Insert(0, plan.Operation.Definition.Operation.ToString().ToLowerInvariant()); - - return displayName.ToString(); - } - finally - { - StringBuilderPool.Return(displayName); - } - } - - private void UpdateRootActivityName(Activity activity, string displayName) - { - var current = activity; - - while (current.Parent is not null) - { - current = current.Parent; - } - - if (current != activity) - { - current.DisplayName = CreateRootActivityName(activity, current, displayName); - } - } - - protected virtual string CreateRootActivityName( - Activity activity, - Activity root, - string displayName) - { - const string key = "originalDisplayName"; - - if (root.GetCustomProperty(key) is not string rootDisplayName) - { - rootDisplayName = root.DisplayName; - root.SetCustomProperty(key, rootDisplayName); - } - - return $"{rootDisplayName}: {displayName}"; - } - - public virtual void EnrichParseDocument(RequestContext context, Activity activity) - { - activity.DisplayName = "Parse Document"; - - if (_options.RenameRootActivity) - { - UpdateRootActivityName(activity, $"Begin {activity.DisplayName}"); - } - } - - public virtual void EnrichRequestError( - RequestContext context, - Activity activity, - Exception error) - => EnrichError(ErrorBuilder.FromException(error).Build(), activity); - - public virtual void EnrichRequestError( - RequestContext context, - Activity activity, - IError error) - => EnrichError(error, activity); - - public virtual void EnrichValidateDocument(RequestContext context, Activity activity) - { - activity.DisplayName = "Validate Document"; - - if (_options.RenameRootActivity) - { - UpdateRootActivityName(activity, $"Begin {activity.DisplayName}"); - } - - var documentInfo = context.OperationDocumentInfo; - activity.SetTag("graphql.document.id", documentInfo.Id.Value); - activity.SetTag("graphql.document.hash", documentInfo.Hash.Value); - } - - public virtual void EnrichValidationError( - RequestContext context, - Activity activity, - IError error) - => EnrichError(error, activity); - - public virtual void EnrichAnalyzeOperationComplexity(RequestContext context, Activity activity) - { - activity.DisplayName = "Analyze Operation Complexity"; - } - - public virtual void EnrichCoerceVariables(RequestContext context, Activity activity) - { - activity.DisplayName = "Coerce Variable"; - } - - public virtual void EnrichPlanOperationScope(RequestContext context, Activity activity) - { - activity.DisplayName = "Plan Operation"; - } - - public virtual void EnrichExecuteOperation(RequestContext context, Activity activity) - { - var plan = context.GetOperationPlan(); - activity.DisplayName = - plan?.OperationName is { } op - ? $"Execute Operation {op}" - : "Execute Operation"; - } - - public virtual void EnrichExecuteOperationNode( + public virtual void EnrichSourceSchemaTransportError( OperationPlanContext context, - OperationExecutionNode node, + ExecutionNode node, string schemaName, + Exception exception, Activity activity) { - activity.DisplayName = $"Execute Operation Node ({schemaName})"; - activity.SetTag("graphql.fusion.node.id", node.Id); - activity.SetTag("graphql.fusion.node.type", node.Type.ToString()); - activity.SetTag("graphql.fusion.node.schema", schemaName); } - public virtual void EnrichExecuteOperationBatchNode( + public virtual void EnrichSourceSchemaStoreError( OperationPlanContext context, ExecutionNode node, string schemaName, + Exception exception, Activity activity) { - activity.DisplayName = $"Execute Operation Batch Node ({schemaName})"; - activity.SetTag("graphql.fusion.node.id", node.Id); - activity.SetTag("graphql.fusion.node.type", node.Type.ToString()); - activity.SetTag("graphql.fusion.node.schema", schemaName); - } - - public virtual void EnrichExecuteNodeFieldNode( - OperationPlanContext context, - NodeFieldExecutionNode node, - Activity activity) - { - activity.DisplayName = "Execute Node Field Node"; - activity.SetTag("graphql.fusion.node.id", node.Id); - activity.SetTag("graphql.fusion.node.type", node.Type.ToString()); } - public virtual void EnrichExecuteIntrospectionNode( + public virtual void EnrichOnSubscriptionEvent( OperationPlanContext context, - IntrospectionExecutionNode node, + ExecutionNode node, + string schemaName, + ulong subscriptionId, Activity activity) { - activity.DisplayName = "Execute Introspection Node"; - activity.SetTag("graphql.fusion.node.id", node.Id); - activity.SetTag("graphql.fusion.node.type", node.Type.ToString()); } - public virtual void EnrichExecutionNodeError( - OperationPlanContext context, - ExecutionNode node, - Exception error, - Activity activity) - => activity.RecordException(error); - - public virtual void EnrichSourceSchemaError( + public virtual void EnrichSubscriptionEventError( OperationPlanContext context, ExecutionNode node, string schemaName, - Exception error, - Activity activity) - => activity.RecordException(error); - - protected virtual void EnrichError(IError error, Activity activity) + ulong subscriptionId, + Exception exception, + Activity featureActivity) { - if (error.Exception is { } exception) - { - activity.RecordException(exception); - } - - var tags = new ActivityTagsCollection - { - new(AttributeExceptionMessage, error.Message), - new(AttributeExceptionType, error.Code ?? "GRAPHQL_ERROR") - }; - - if (error.Path is not null) - { - tags["graphql.error.path"] = error.Path.ToString(); - } - - if (error.Locations is { Count: > 0 }) - { - tags["graphql.error.location.column"] = error.Locations[0].Column; - tags["graphql.error.location.line"] = error.Locations[0].Line; - } - - activity.AddEvent(new ActivityEvent(AttributeExceptionEventName, default, tags)); } } - -file static class SemanticConventions -{ - public const string AttributeExceptionEventName = "exception"; - public const string AttributeExceptionType = "exception.type"; - public const string AttributeExceptionMessage = "exception.message"; -} diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/FusionActivityScopes.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/FusionActivityScopes.cs index f0ef6bd29e2..e2208344cd3 100644 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/FusionActivityScopes.cs +++ b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/FusionActivityScopes.cs @@ -14,13 +14,13 @@ public enum FusionActivityScopes CoerceVariables = 128, PlanOperation = 256, ExecuteOperation = 512, - ExecuteNodes = 1024, + ExecutePlanNodes = 1024, Default = ExecuteHttpRequest | ParseHttpRequest | ValidateDocument | PlanOperation - | ExecuteNodes + | ExecutePlanNodes | FormatHttpResponse, All = ExecuteHttpRequest @@ -33,5 +33,5 @@ public enum FusionActivityScopes | CoerceVariables | PlanOperation | ExecuteOperation - | ExecuteNodes + | ExecutePlanNodes } diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/HotChocolate.Fusion.Diagnostics.csproj b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/HotChocolate.Fusion.Diagnostics.csproj index 28e5ea8fabe..c5ca6371cb5 100644 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/HotChocolate.Fusion.Diagnostics.csproj +++ b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/HotChocolate.Fusion.Diagnostics.csproj @@ -16,6 +16,7 @@ + diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/HotChocolateFusionActivitySource.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/HotChocolateFusionActivitySource.cs index dda5993f72b..dd6db410510 100644 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/HotChocolateFusionActivitySource.cs +++ b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/HotChocolateFusionActivitySource.cs @@ -8,8 +8,8 @@ internal static class HotChocolateFusionActivitySource public static ActivitySource Source { get; } = new(GetName(), GetVersion()); public static string GetName() - => typeof(ActivityFusionExecutionDiagnosticEventListener).Assembly.GetName().Name!; + => typeof(FusionActivityExecutionDiagnosticEventListener).Assembly.GetName().Name!; private static string GetVersion() - => typeof(ActivityFusionExecutionDiagnosticEventListener).Assembly.GetName().Version!.ToString(); + => typeof(FusionActivityExecutionDiagnosticEventListener).Assembly.GetName().Version!.ToString(); } diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/InstrumentationOptions.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/InstrumentationOptions.cs index 74ae97005eb..9237f91b67a 100644 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/InstrumentationOptions.cs +++ b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/InstrumentationOptions.cs @@ -1,3 +1,4 @@ +using HotChocolate.Diagnostics; using static HotChocolate.Fusion.Diagnostics.FusionActivityScopes; namespace HotChocolate.Fusion.Diagnostics; @@ -5,30 +6,13 @@ namespace HotChocolate.Fusion.Diagnostics; /// /// The Hot Chocolate Fusion instrumentation options. /// -public sealed class InstrumentationOptions +public sealed class InstrumentationOptions : InstrumentationOptionsBase { - /// - /// Specifies the request detail that shall be included into the tracing activities. - /// - public RequestDetails RequestDetails { get; set; } = RequestDetails.Default; - /// /// Specifies the activity scopes that shall be instrumented. /// public FusionActivityScopes Scopes { get; set; } = Default; - /// - /// Specifies if the parsed document shall be included into the tracing data. - /// - public bool IncludeDocument { get; set; } - - /// - /// Defines if the operation display name shall be included in the root activity. - /// - public bool RenameRootActivity { get; set; } - - internal bool IncludeRequestDetails => RequestDetails is not RequestDetails.None; - internal bool SkipExecuteHttpRequest => (Scopes & ExecuteHttpRequest) != ExecuteHttpRequest; internal bool SkipParseHttpRequest => (Scopes & ParseHttpRequest) != ParseHttpRequest; @@ -41,11 +25,13 @@ public sealed class InstrumentationOptions internal bool SkipValidateDocument => (Scopes & ValidateDocument) != ValidateDocument; + internal bool SkipAnalyzeComplexity => (Scopes & AnalyzeComplexity) != AnalyzeComplexity; + internal bool SkipCoerceVariables => (Scopes & CoerceVariables) != CoerceVariables; internal bool SkipPlanOperation => (Scopes & PlanOperation) != PlanOperation; internal bool SkipExecuteOperation => (Scopes & ExecuteOperation) != ExecuteOperation; - internal bool SkipExecuteNodes => (Scopes & ExecuteNodes) != ExecuteNodes; + internal bool SkipExecutePlanNodes => (Scopes & ExecutePlanNodes) != ExecutePlanNodes; } diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Listeners/ActivityFusionExecutionDiagnosticEventListener.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Listeners/ActivityFusionExecutionDiagnosticEventListener.cs deleted file mode 100644 index 985b74a2334..00000000000 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Listeners/ActivityFusionExecutionDiagnosticEventListener.cs +++ /dev/null @@ -1,358 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Fusion.Diagnostics.Scopes; -using HotChocolate.Execution; -using HotChocolate.Fusion.Execution; -using HotChocolate.Fusion.Execution.Nodes; -using Microsoft.AspNetCore.Http; -using OpenTelemetry.Trace; -using static HotChocolate.Fusion.Diagnostics.ContextKeys; -using static HotChocolate.Fusion.Diagnostics.HotChocolateFusionActivitySource; - -namespace HotChocolate.Fusion.Diagnostics.Listeners; - -internal sealed class ActivityFusionExecutionDiagnosticEventListener : FusionExecutionDiagnosticEventListener -{ - private readonly InstrumentationOptions _options; - private readonly FusionActivityEnricher _enricher; - - public ActivityFusionExecutionDiagnosticEventListener( - FusionActivityEnricher enricher, - InstrumentationOptions options) - { - ArgumentNullException.ThrowIfNull(enricher); - ArgumentNullException.ThrowIfNull(options); - - _enricher = enricher; - _options = options; - } - - public override IDisposable ExecuteRequest(RequestContext context) - { - Activity? activity = null; - - if (_options.SkipExecuteRequest) - { - if (!_options.SkipExecuteHttpRequest - && context.ContextData.TryGetValue(nameof(HttpContext), out var value) - && value is HttpContext httpContext - && httpContext.Items.TryGetValue(HttpRequestActivity, out value) - && value is not null) - { - activity = (Activity)value; - } - else - { - return EmptyScope; - } - } - - activity ??= Source.StartActivity(); - - if (activity is null) - { - return EmptyScope; - } - - context.ContextData[RequestActivity] = activity; - - return new ExecuteRequestScope(_enricher, context, activity); - } - - public override void RetrievedDocumentFromCache(RequestContext context) - { - if (context.ContextData.TryGetValue(RequestActivity, out var activity)) - { - Debug.Assert(activity is not null, "The activity mustn't be null!"); - ((Activity)activity).AddEvent(new(nameof(RetrievedDocumentFromCache))); - } - } - - public override void RetrievedDocumentFromStorage(RequestContext context) - { - if (context.ContextData.TryGetValue(RequestActivity, out var activity)) - { - Debug.Assert(activity is not null, "The activity mustn't be null!"); - ((Activity)activity).AddEvent(new(nameof(RetrievedDocumentFromStorage))); - } - } - - public override void AddedDocumentToCache(RequestContext context) - { - if (context.ContextData.TryGetValue(RequestActivity, out var activity)) - { - Debug.Assert(activity is not null, "The activity mustn't be null!"); - ((Activity)activity).AddEvent(new(nameof(AddedDocumentToCache))); - } - } - - public override void AddedOperationPlanToCache(RequestContext context, string operationPlanId) - { - if (context.ContextData.TryGetValue(RequestActivity, out var activity)) - { - Debug.Assert(activity is not null, "The activity mustn't be null!"); - ((Activity)activity).AddEvent(new(nameof(AddedOperationPlanToCache))); - } - } - - public override IDisposable ParseDocument(RequestContext context) - { - if (_options.SkipParseDocument) - { - return EmptyScope; - } - - var activity = Source.StartActivity(); - - if (activity is null) - { - return EmptyScope; - } - - context.ContextData[RequestActivity] = activity; - - return new ParseDocumentScope(_enricher, context, activity); - } - - public override void RequestError(RequestContext context, Exception error) - { - if (context.ContextData.TryGetValue(RequestActivity, out var value)) - { - Debug.Assert(value is not null, "The activity mustn't be null!"); - - var activity = (Activity)value; - _enricher.EnrichRequestError(context, activity, error); - activity.SetStatus(Status.Error); - activity.SetStatus(ActivityStatusCode.Error); - } - } - - public override void RequestError(RequestContext context, IError error) - { - if (context.ContextData.TryGetValue(RequestActivity, out var value)) - { - Debug.Assert(value is not null, "The activity mustn't be null!"); - - var activity = (Activity)value; - _enricher.EnrichRequestError(context, activity, error); - activity.SetStatus(Status.Error); - activity.SetStatus(ActivityStatusCode.Error); - } - } - - public override void ValidationErrors(RequestContext context, IReadOnlyList errors) - { - if (context.ContextData.TryGetValue(ValidateActivity, out var value)) - { - Debug.Assert(value is not null, "The activity mustn't be null!"); - - var activity = (Activity)value; - - foreach (var error in errors) - { - _enricher.EnrichValidationError(context, activity, error); - } - - activity.SetStatus(Status.Error); - activity.SetStatus(ActivityStatusCode.Error); - } - } - - public override IDisposable ValidateDocument(RequestContext context) - { - if (_options.SkipValidateDocument) - { - return EmptyScope; - } - - var activity = Source.StartActivity(); - - if (activity is null) - { - return EmptyScope; - } - - context.ContextData[ValidateActivity] = activity; - - return new ValidateDocumentScope(_enricher, context, activity); - } - - public override IDisposable CoerceVariables(RequestContext context) - { - if (_options.SkipCoerceVariables) - { - return EmptyScope; - } - - var activity = Source.StartActivity(); - - if (activity is null) - { - return EmptyScope; - } - - return new CoerceVariablesScope(_enricher, context, activity); - } - - public override IDisposable PlanOperation(RequestContext context, string operationPlanId) - { - if (_options.SkipPlanOperation) - { - return EmptyScope; - } - - var activity = Source.StartActivity(); - - if (activity is null) - { - return EmptyScope; - } - - return new PlanOperationScope(_enricher, context, activity); - } - - public override IDisposable ExecuteOperation(RequestContext context) - { - if (_options.SkipExecuteOperation) - { - return EmptyScope; - } - - var activity = Source.StartActivity(); - - if (activity is null) - { - return EmptyScope; - } - - return new ExecuteOperationScope(_enricher, context, activity); - } - - public override IDisposable ExecuteOperationNode( - OperationPlanContext context, - OperationExecutionNode node, - string schemaName) - { - if (_options.SkipExecuteNodes) - { - return EmptyScope; - } - - var activity = Source.StartActivity(); - - if (activity is null) - { - return EmptyScope; - } - - return new ExecuteOperationNodeScope(_enricher, context, node, schemaName, activity); - } - - public override IDisposable ExecuteOperationBatchNode( - OperationPlanContext context, - ExecutionNode node, - string schemaName) - { - if (_options.SkipExecuteNodes) - { - return EmptyScope; - } - - var activity = Source.StartActivity(); - - if (activity is null) - { - return EmptyScope; - } - - return new ExecuteOperationBatchNodeScope(_enricher, context, node, schemaName, activity); - } - - public override IDisposable ExecuteNodeFieldNode( - OperationPlanContext context, - NodeFieldExecutionNode node) - { - if (_options.SkipExecuteNodes) - { - return EmptyScope; - } - - var activity = Source.StartActivity(); - - if (activity is null) - { - return EmptyScope; - } - - return new ExecuteNodeFieldNodeScope(_enricher, context, node, activity); - } - - public override IDisposable ExecuteIntrospectionNode( - OperationPlanContext context, - IntrospectionExecutionNode node) - { - if (_options.SkipExecuteNodes) - { - return EmptyScope; - } - - var activity = Source.StartActivity(); - - if (activity is null) - { - return EmptyScope; - } - - return new ExecuteIntrospectionNodeScope(_enricher, context, node, activity); - } - - public override void ExecutionNodeError( - OperationPlanContext context, - ExecutionNode node, - Exception error) - { - if (Activity.Current is { } activity) - { - _enricher.EnrichExecutionNodeError(context, node, error, activity); - } - } - - public override void SourceSchemaTransportError( - OperationPlanContext context, - ExecutionNode node, - string schemaName, - Exception error) - { - if (Activity.Current is { } activity) - { - _enricher.EnrichSourceSchemaError(context, node, schemaName, error, activity); - } - } - - public override void SourceSchemaStoreError( - OperationPlanContext context, - ExecutionNode node, - string schemaName, - Exception error) - { - if (Activity.Current is { } activity) - { - _enricher.EnrichSourceSchemaError(context, node, schemaName, error, activity); - } - } - - public override IDisposable OnSubscriptionEvent( - OperationPlanContext context, - ExecutionNode node, - string schemaName, - ulong subscriptionId) - { - var activity = Source.StartActivity(); - - if (activity is null) - { - return EmptyScope; - } - - return activity; - } -} diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Listeners/ActivityServerDiagnosticListener.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Listeners/ActivityServerDiagnosticListener.cs deleted file mode 100644 index 1eb078b2f3f..00000000000 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Listeners/ActivityServerDiagnosticListener.cs +++ /dev/null @@ -1,152 +0,0 @@ -using System.Diagnostics; -using Microsoft.AspNetCore.Http; -using HotChocolate.AspNetCore.Instrumentation; -using HotChocolate.Execution; -using HotChocolate.Language; -using OpenTelemetry.Trace; -using static HotChocolate.Fusion.Diagnostics.ContextKeys; - -namespace HotChocolate.Fusion.Diagnostics.Listeners; - -internal sealed class ActivityServerDiagnosticListener( - FusionActivityEnricher enricher, - InstrumentationOptions options) - : ServerDiagnosticEventListener -{ - private readonly InstrumentationOptions _options = options ?? throw new ArgumentNullException(nameof(options)); - private readonly FusionActivityEnricher _enricher = enricher ?? throw new ArgumentNullException(nameof(enricher)); - - public override IDisposable ExecuteHttpRequest(HttpContext context, HttpRequestKind kind) - { - if (_options.SkipExecuteHttpRequest) - { - return EmptyScope; - } - - var activity = HotChocolateFusionActivitySource.Source.StartActivity(); - - if (activity is null) - { - return EmptyScope; - } - - _enricher.EnrichExecuteHttpRequest(context, kind, activity); - activity.SetStatus(ActivityStatusCode.Ok); - context.Items[HttpRequestActivity] = activity; - - return activity; - } - - public override void StartSingleRequest(HttpContext context, GraphQLRequest request) - { - if (_options.IncludeRequestDetails - && context.Items.TryGetValue(HttpRequestActivity, out var activity)) - { - _enricher.EnrichSingleRequest(context, request, (Activity)activity!); - } - } - - public override void StartBatchRequest(HttpContext context, IReadOnlyList batch) - { - if (_options.IncludeRequestDetails - && context.Items.TryGetValue(HttpRequestActivity, out var activity)) - { - _enricher.EnrichBatchRequest(context, batch, (Activity)activity!); - } - } - - public override void StartOperationBatchRequest( - HttpContext context, - GraphQLRequest request, - IReadOnlyList operations) - { - if (_options.IncludeRequestDetails - && context.Items.TryGetValue(HttpRequestActivity, out var activity)) - { - _enricher.EnrichOperationBatchRequest( - context, - request, - operations, - (Activity)activity!); - } - } - - public override void HttpRequestError(HttpContext context, IError error) - { - if (context.Items.TryGetValue(HttpRequestActivity, out var value)) - { - var activity = (Activity)value!; - _enricher.EnrichHttpRequestError(context, error, activity); - activity.SetStatus(Status.Error); - } - } - - public override void HttpRequestError(HttpContext context, Exception exception) - { - if (context.Items.TryGetValue(HttpRequestActivity, out var value)) - { - var activity = (Activity)value!; - _enricher.EnrichHttpRequestError(context, exception, activity); - activity.SetStatus(Status.Error); - } - } - - public override IDisposable ParseHttpRequest(HttpContext context) - { - if (_options.SkipParseHttpRequest) - { - return EmptyScope; - } - - var activity = HotChocolateFusionActivitySource.Source.StartActivity(); - - if (activity is null) - { - return EmptyScope; - } - - _enricher.EnrichParseHttpRequest(context, activity); - activity.SetStatus(Status.Ok); - activity.SetStatus(ActivityStatusCode.Ok); - context.Items[ParseHttpRequestActivity] = activity; - - return activity; - } - - public override void ParserErrors(HttpContext context, IReadOnlyList errors) - { - if (context.Items.TryGetValue(ParseHttpRequestActivity, out var value)) - { - var activity = (Activity)value!; - - foreach (var error in errors) - { - _enricher.EnrichParserErrors(context, error, activity); - } - - activity.SetStatus(Status.Error); - activity.SetStatus(ActivityStatusCode.Error); - } - } - - public override IDisposable FormatHttpResponse(HttpContext context, OperationResult result) - { - if (_options.SkipFormatHttpResponse) - { - return EmptyScope; - } - - var activity = HotChocolateFusionActivitySource.Source.StartActivity(); - - if (activity is null) - { - return EmptyScope; - } - - _enricher.EnrichFormatHttpResponse(context, activity); - activity.SetStatus(ActivityStatusCode.Ok); - context.Items[FormatHttpResponseActivity] = activity; - - return activity; - } -} diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Listeners/FusionActivityExecutionDiagnosticEventListener.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Listeners/FusionActivityExecutionDiagnosticEventListener.cs new file mode 100644 index 00000000000..816544718d6 --- /dev/null +++ b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Listeners/FusionActivityExecutionDiagnosticEventListener.cs @@ -0,0 +1,362 @@ +using System.Diagnostics; +using HotChocolate.Diagnostics; +using HotChocolate.Execution; +using HotChocolate.Fusion.Execution; +using HotChocolate.Fusion.Execution.Nodes; +using Microsoft.AspNetCore.Http; +using static HotChocolate.Fusion.Diagnostics.HotChocolateFusionActivitySource; + +namespace HotChocolate.Fusion.Diagnostics.Listeners; + +internal sealed class FusionActivityExecutionDiagnosticEventListener( + FusionActivityEnricher enricher, + InstrumentationOptions options) : FusionExecutionDiagnosticEventListener +{ + public override IDisposable ExecuteRequest(RequestContext context) + { + Activity? httpContextActivity = null; + + if (options.SkipExecuteRequest) + { + if (!options.SkipExecuteHttpRequest + && context.Features.TryGet(out var httpContext) + && httpContext.Features.Get() is { } httpRequestSpan) + { + httpContextActivity = httpRequestSpan.Activity; + } + else + { + return EmptyScope; + } + } + + var span = httpContextActivity is not null + ? new ExecuteRequestSpan(httpContextActivity, context, options, enricher, false) + : ExecuteRequestSpan.Start(Source, context, options, enricher); + + if (span is null) + { + return EmptyScope; + } + + context.Features.Set(span); + + return span; + } + + public override void RequestError(RequestContext context, Exception error) + { + if (context.Features.TryGet(out var span)) + { + var activity = span.Activity; + + activity.SetStatus(ActivityStatusCode.Error); + activity.AddException(error); + + enricher.EnrichRequestError(context, error, activity); + } + } + + public override void RequestError(RequestContext context, IError error) + { + if (context.Features.TryGet(out var span)) + { + var activity = span.Activity; + + activity.SetStatus(ActivityStatusCode.Error); + activity.AddGraphQLError(error); + + enricher.EnrichRequestError(context, error, activity); + } + } + + public override IDisposable ParseDocument(RequestContext context) + { + if (options.SkipParseDocument) + { + return EmptyScope; + } + + var span = ParsingSpan.Start(Source, context, enricher); + + return span ?? EmptyScope; + } + + public override IDisposable ValidateDocument(RequestContext context) + { + if (options.SkipValidateDocument) + { + return EmptyScope; + } + + var span = ValidationSpan.Start(Source, context, enricher); + + if (span is null) + { + return EmptyScope; + } + + context.Features.Set(span); + + return span; + } + + public override void ValidationErrors(RequestContext context, IReadOnlyList errors) + { + if (!context.Features.TryGet(out var span)) + { + return; + } + + var activity = span.Activity; + + activity.SetStatus(ActivityStatusCode.Error); + + foreach (var error in errors) + { + activity.AddGraphQLError(error); + } + + enricher.EnrichValidationErrors(context, errors, activity); + } + + public override IDisposable PlanOperation(RequestContext context, string operationPlanId) + { + if (options.SkipPlanOperation) + { + return EmptyScope; + } + + var span = PlanOperationSpan.Start(Source, context, enricher, operationPlanId); + + return span ?? EmptyScope; + } + + public override IDisposable CoerceVariables(RequestContext context) + { + if (options.SkipCoerceVariables) + { + return EmptyScope; + } + + if (context.GetOperationPlan() is not { } plan) + { + return EmptyScope; + } + + var span = VariableCoercionSpan.Start( + Source, + context, + plan.Operation.Definition.Operation, + plan.OperationName, + enricher); + + return span ?? EmptyScope; + } + + public override IDisposable ExecuteOperation(RequestContext context) + { + if (options.SkipExecuteOperation) + { + return EmptyScope; + } + + if (context.GetOperationPlan() is not { } plan) + { + return EmptyScope; + } + + var span = ExecuteOperationSpan.Start( + Source, + context, + plan.Operation.Definition.Operation, + plan.OperationName, + enricher); + + return span ?? EmptyScope; + } + + public override IDisposable ExecuteOperationNode( + OperationPlanContext context, + OperationExecutionNode node, + string schemaName) + => ExecuteNode(context, node, schemaName); + + public override IDisposable ExecuteOperationBatchNode( + OperationPlanContext context, + OperationBatchExecutionNode node, + string schemaName) + => ExecuteNode(context, node, schemaName); + + public override IDisposable ExecuteSubscriptionNode( + OperationPlanContext context, + ExecutionNode node, + string schemaName, + ulong subscriptionId) + { + var nodeScope = ExecuteNode(context, node, schemaName); + context.RequestContext.Features.Set( + new SubscriptionContextFeature + { + SubscriptionContext = Activity.Current?.Context + }); + + return nodeScope; + } + + public override IDisposable ExecuteNodeFieldNode( + OperationPlanContext context, + NodeFieldExecutionNode node) + => ExecuteNode(context, node, null); + + public override IDisposable ExecuteIntrospectionNode( + OperationPlanContext context, + IntrospectionExecutionNode node) + => ExecuteNode(context, node, null); + + public override void ExecutionNodeError( + OperationPlanContext context, + ExecutionNode node, + Exception error) + { + if (Activity.Current is { } activity) + { + activity.SetStatus(ActivityStatusCode.Error); + activity.AddException(error); + + enricher.EnrichExecutionNodeError(context, node, error, activity); + } + } + + public override void SourceSchemaTransportError( + OperationPlanContext context, + ExecutionNode node, + string schemaName, + Exception error) + { + if (Activity.Current is { } activity) + { + activity.SetStatus(ActivityStatusCode.Error); + activity.AddException(error); + + enricher.EnrichSourceSchemaTransportError(context, node, schemaName, error, activity); + } + } + + public override void SourceSchemaStoreError( + OperationPlanContext context, + ExecutionNode node, + string schemaName, + Exception error) + { + if (Activity.Current is { } activity) + { + activity.SetStatus(ActivityStatusCode.Error); + activity.AddException(error); + + enricher.EnrichSourceSchemaStoreError(context, node, schemaName, error, activity); + } + } + + public override IDisposable OnSubscriptionEvent( + OperationPlanContext context, + ExecutionNode node, + string schemaName, + ulong subscriptionId) + { + ActivityContext? subscriptionContext = null; + + if (context.RequestContext.Features.TryGet(out var feature) + && feature.SubscriptionContext is { } storedSubscriptionContext) + { + subscriptionContext = storedSubscriptionContext; + } + + var span = SubscriptionEventSpan.Start( + Source, + context.RequestContext, + context.OperationPlan.Operation.Name, + subscriptionId, + subscriptionContext); + + if (span is null) + { + return EmptyScope; + } + + enricher.EnrichOnSubscriptionEvent(context, node, schemaName, subscriptionId, span.Activity); + + return span; + } + + public override void SubscriptionEventError( + OperationPlanContext context, + ExecutionNode node, + string schemaName, + ulong subscriptionId, + Exception exception) + { + if (Activity.Current is { } activity) + { + activity.SetStatus(ActivityStatusCode.Error); + activity.AddException(exception); + + enricher.EnrichSubscriptionEventError( + context, + node, + schemaName, + subscriptionId, + exception, + activity); + } + } + + public override void RetrievedDocumentFromCache(RequestContext context) + { + if (context.Features.TryGet(out var span)) + { + span.Activity.AddEvent(new(nameof(RetrievedDocumentFromCache))); + } + } + + public override void RetrievedDocumentFromStorage(RequestContext context) + { + if (context.Features.TryGet(out var span)) + { + span.Activity.AddEvent(new(nameof(RetrievedDocumentFromStorage))); + } + } + + public override void AddedDocumentToCache(RequestContext context) + { + if (context.Features.TryGet(out var span)) + { + span.Activity.AddEvent(new(nameof(AddedDocumentToCache))); + } + } + + public override void AddedOperationPlanToCache(RequestContext context, string operationPlanId) + { + if (context.Features.TryGet(out var span)) + { + span.Activity.AddEvent(new(nameof(AddedOperationPlanToCache))); + } + } + + private sealed class SubscriptionContextFeature + { + public ActivityContext? SubscriptionContext { get; set; } + } + + private IDisposable ExecuteNode(OperationPlanContext context, ExecutionNode node, string? schemaName) + { + if (options.SkipExecutePlanNodes) + { + return EmptyScope; + } + + var span = ExecutePlanNodeSpan.Start(Source, context, node, schemaName, enricher); + + return span ?? EmptyScope; + } +} diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Listeners/FusionActivityServerDiagnosticListener.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Listeners/FusionActivityServerDiagnosticListener.cs new file mode 100644 index 00000000000..2bfb286ed56 --- /dev/null +++ b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Listeners/FusionActivityServerDiagnosticListener.cs @@ -0,0 +1,118 @@ +using Microsoft.AspNetCore.Http; +using HotChocolate.AspNetCore.Instrumentation; +using HotChocolate.Diagnostics; +using HotChocolate.Execution; +using HotChocolate.Language; +using static HotChocolate.Fusion.Diagnostics.HotChocolateFusionActivitySource; + +namespace HotChocolate.Fusion.Diagnostics.Listeners; + +internal sealed class FusionActivityServerDiagnosticListener( + FusionActivityEnricher enricher, + InstrumentationOptions options) + : ServerDiagnosticEventListener +{ + public override IDisposable ExecuteHttpRequest(HttpContext context, HttpRequestKind kind) + { + if (options.SkipExecuteHttpRequest) + { + return EmptyScope; + } + + var span = ExecuteHttpRequestSpan.Start(Source, context, kind, enricher, options); + + if (span is null) + { + return EmptyScope; + } + + context.Features.Set(span); + + return span; + } + + public override void StartSingleRequest(HttpContext context, GraphQLRequest request) + { + if (options.IncludeRequestDetails + && context.Features.Get() is { } span) + { + span.SetSingleRequestDetails(request); + } + } + + public override void StartBatchRequest(HttpContext context, IReadOnlyList batch) + { + if (options.IncludeRequestDetails + && context.Features.Get() is { } span) + { + span.SetBatchRequestDetails(batch); + } + } + + public override void StartOperationBatchRequest( + HttpContext context, + GraphQLRequest request, + IReadOnlyList operations) + { + if (options.IncludeRequestDetails + && context.Features.Get() is { } span) + { + span.SetOperationBatchRequestDetails(request, operations); + } + } + + public override void HttpRequestError(HttpContext context, IError error) + { + if (context.Features.Get() is { } span) + { + span.RecordError(error); + } + } + + public override void HttpRequestError(HttpContext context, Exception exception) + { + if (context.Features.Get() is { } span) + { + span.RecordError(exception); + } + } + + public override IDisposable ParseHttpRequest(HttpContext context) + { + if (options.SkipParseHttpRequest) + { + return EmptyScope; + } + + var span = ParseHttpRequestSpan.Start(Source, context, enricher); + + if (span is null) + { + return EmptyScope; + } + + context.Features.Set(span); + + return span; + } + + public override void ParserErrors(HttpContext context, IReadOnlyList errors) + { + if (context.Features.Get() is { } span) + { + span.RecordErrors(errors); + } + } + + public override IDisposable FormatHttpResponse(HttpContext context, OperationResult result) + { + if (options.SkipFormatHttpResponse) + { + return EmptyScope; + } + + var span = FormatHttpResponseSpan.Start(Source, context, enricher); + + return span ?? EmptyScope; + } +} diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/RequestDetails.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/RequestDetails.cs deleted file mode 100644 index dd05a951ba2..00000000000 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/RequestDetails.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace HotChocolate.Fusion.Diagnostics; - -[Flags] -public enum RequestDetails -{ - None = 0, - Id = 1, - Hash = 2, - Operation = 4, - Variables = 8, - Extensions = 16, - Query = 32, - Default = Id | Hash | Operation | Extensions, - All = Id | Hash | Operation | Variables | Extensions | Query -} diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/CoerceVariablesScope.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/CoerceVariablesScope.cs deleted file mode 100644 index 8361d9d044d..00000000000 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/CoerceVariablesScope.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Execution; -using OpenTelemetry.Trace; - -namespace HotChocolate.Fusion.Diagnostics.Scopes; - -internal sealed class CoerceVariablesScope( - FusionActivityEnricher enricher, - RequestContext context, - Activity activity) - : RequestScopeBase(enricher, context, activity) -{ - protected override void EnrichActivity() - => Enricher.EnrichCoerceVariables(Context, Activity); - - protected override void SetStatus() - { - if (Context.VariableValues.Length > 0) - { - Activity.SetStatus(Status.Ok); - Activity.SetStatus(ActivityStatusCode.Ok); - } - } -} diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ExecuteIntrospectionNodeScope.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ExecuteIntrospectionNodeScope.cs deleted file mode 100644 index 622397a1e7d..00000000000 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ExecuteIntrospectionNodeScope.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Fusion.Execution; -using HotChocolate.Fusion.Execution.Nodes; -using OpenTelemetry.Trace; - -namespace HotChocolate.Fusion.Diagnostics.Scopes; - -internal sealed class ExecuteIntrospectionNodeScope( - FusionActivityEnricher enricher, - OperationPlanContext context, - IntrospectionExecutionNode node, - Activity activity) - : NodeScopeBase(enricher, context, activity) -{ - protected override void EnrichActivity() - => Enricher.EnrichExecuteIntrospectionNode(Context, node, Activity); - - protected override void SetStatus() - { - Activity.SetStatus(Status.Ok); - Activity.SetStatus(ActivityStatusCode.Ok); - } -} diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ExecuteNodeFieldNodeScope.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ExecuteNodeFieldNodeScope.cs deleted file mode 100644 index 867b33786e5..00000000000 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ExecuteNodeFieldNodeScope.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Fusion.Execution; -using HotChocolate.Fusion.Execution.Nodes; -using OpenTelemetry.Trace; - -namespace HotChocolate.Fusion.Diagnostics.Scopes; - -internal sealed class ExecuteNodeFieldNodeScope( - FusionActivityEnricher enricher, - OperationPlanContext context, - NodeFieldExecutionNode node, - Activity activity) - : NodeScopeBase(enricher, context, activity) -{ - protected override void EnrichActivity() - => Enricher.EnrichExecuteNodeFieldNode(Context, node, Activity); - - protected override void SetStatus() - { - Activity.SetStatus(Status.Ok); - Activity.SetStatus(ActivityStatusCode.Ok); - } -} diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ExecuteOperationBatchNodeScope.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ExecuteOperationBatchNodeScope.cs deleted file mode 100644 index 1363edffea5..00000000000 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ExecuteOperationBatchNodeScope.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Fusion.Execution; -using HotChocolate.Fusion.Execution.Nodes; -using OpenTelemetry.Trace; - -namespace HotChocolate.Fusion.Diagnostics.Scopes; - -internal sealed class ExecuteOperationBatchNodeScope( - FusionActivityEnricher enricher, - OperationPlanContext context, - ExecutionNode node, - string schemaName, - Activity activity) - : NodeScopeBase(enricher, context, activity) -{ - protected override void EnrichActivity() - => Enricher.EnrichExecuteOperationBatchNode(Context, node, schemaName, Activity); - - protected override void SetStatus() - { - Activity.SetStatus(Status.Ok); - Activity.SetStatus(ActivityStatusCode.Ok); - } -} diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ExecuteOperationNodeScope.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ExecuteOperationNodeScope.cs deleted file mode 100644 index 58e19e11185..00000000000 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ExecuteOperationNodeScope.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Fusion.Execution; -using HotChocolate.Fusion.Execution.Nodes; -using OpenTelemetry.Trace; - -namespace HotChocolate.Fusion.Diagnostics.Scopes; - -internal sealed class ExecuteOperationNodeScope( - FusionActivityEnricher enricher, - OperationPlanContext context, - OperationExecutionNode node, - string schemaName, - Activity activity) - : NodeScopeBase(enricher, context, activity) -{ - protected override void EnrichActivity() - => Enricher.EnrichExecuteOperationNode(Context, node, schemaName, Activity); - - protected override void SetStatus() - { - Activity.SetStatus(Status.Ok); - Activity.SetStatus(ActivityStatusCode.Ok); - } -} diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ExecuteOperationScope.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ExecuteOperationScope.cs deleted file mode 100644 index 1f69711d4e6..00000000000 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ExecuteOperationScope.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Execution; -using OpenTelemetry.Trace; - -namespace HotChocolate.Fusion.Diagnostics.Scopes; - -internal sealed class ExecuteOperationScope( - FusionActivityEnricher enricher, - RequestContext context, - Activity activity) - : RequestScopeBase(enricher, context, activity) -{ - protected override void EnrichActivity() - => Enricher.EnrichExecuteOperation(Context, Activity); - - protected override void SetStatus() - { - if (Context.Result is null or OperationResult { Errors: [_, ..] }) - { - Activity.SetStatus(Status.Error); - Activity.SetStatus(ActivityStatusCode.Error); - } - else - { - Activity.SetStatus(Status.Ok); - Activity.SetStatus(ActivityStatusCode.Ok); - } - } -} diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ExecuteRequestScope.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ExecuteRequestScope.cs deleted file mode 100644 index 6ecca09cc07..00000000000 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ExecuteRequestScope.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Execution; -using OpenTelemetry.Trace; - -namespace HotChocolate.Fusion.Diagnostics.Scopes; - -internal sealed class ExecuteRequestScope( - FusionActivityEnricher enricher, - RequestContext context, - Activity activity) - : RequestScopeBase(enricher, context, activity) -{ - protected override void EnrichActivity() - => Enricher.EnrichExecuteRequest(Context, Activity); - - protected override void SetStatus() - { - if (Context.Result is null or OperationResult { Errors: [_, ..] }) - { - Activity.SetStatus(Status.Error); - Activity.SetStatus(ActivityStatusCode.Error); - } - } -} diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/NodeScopeBase.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/NodeScopeBase.cs deleted file mode 100644 index e0c04291939..00000000000 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/NodeScopeBase.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Fusion.Execution; - -namespace HotChocolate.Fusion.Diagnostics.Scopes; - -internal class NodeScopeBase : IDisposable -{ - private bool _disposed; - - protected NodeScopeBase( - FusionActivityEnricher enricher, - OperationPlanContext context, - Activity activity) - { - Enricher = enricher ?? throw new ArgumentNullException(nameof(enricher)); - Context = context ?? throw new ArgumentNullException(nameof(context)); - Activity = activity ?? throw new ArgumentNullException(nameof(activity)); - } - - protected FusionActivityEnricher Enricher { get; } - - protected OperationPlanContext Context { get; } - - protected Activity Activity { get; } - - protected virtual void EnrichActivity() { } - - protected virtual void SetStatus() { } - - public void Dispose() - { - if (!_disposed) - { - EnrichActivity(); - SetStatus(); - Activity.Dispose(); - _disposed = true; - } - } -} diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ParseDocumentScope.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ParseDocumentScope.cs deleted file mode 100644 index 048af8072b1..00000000000 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ParseDocumentScope.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Execution; -using OpenTelemetry.Trace; - -namespace HotChocolate.Fusion.Diagnostics.Scopes; - -internal sealed class ParseDocumentScope( - FusionActivityEnricher enricher, - RequestContext context, - Activity activity) - : RequestScopeBase(enricher, context, activity) -{ - protected override void EnrichActivity() - => Enricher.EnrichParseDocument(Context, Activity); - - protected override void SetStatus() - { - if (Context.TryGetOperationDocument(out _, out _)) - { - Activity.SetStatus(Status.Ok); - Activity.SetStatus(ActivityStatusCode.Ok); - } - } -} diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/PlanOperationScope.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/PlanOperationScope.cs deleted file mode 100644 index aad6df000df..00000000000 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/PlanOperationScope.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Execution; -using OpenTelemetry.Trace; - -namespace HotChocolate.Fusion.Diagnostics.Scopes; - -internal sealed class PlanOperationScope( - FusionActivityEnricher enricher, - RequestContext context, - Activity activity) - : RequestScopeBase(enricher, context, activity) -{ - protected override void EnrichActivity() - => Enricher.EnrichPlanOperationScope(Context, Activity); - - protected override void SetStatus() - { - if (Context.GetOperationPlan() is not null) - { - Activity.SetStatus(Status.Ok); - Activity.SetStatus(ActivityStatusCode.Ok); - } - } -} diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/RequestScopeBase.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/RequestScopeBase.cs deleted file mode 100644 index 69403a62676..00000000000 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/RequestScopeBase.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Execution; - -namespace HotChocolate.Fusion.Diagnostics.Scopes; - -internal class RequestScopeBase : IDisposable -{ - private bool _disposed; - - protected RequestScopeBase( - FusionActivityEnricher enricher, - RequestContext context, - Activity activity) - { - Enricher = enricher ?? throw new ArgumentNullException(nameof(enricher)); - Context = context ?? throw new ArgumentNullException(nameof(context)); - Activity = activity ?? throw new ArgumentNullException(nameof(activity)); - } - - protected FusionActivityEnricher Enricher { get; } - - protected RequestContext Context { get; } - - protected Activity Activity { get; } - - protected virtual void EnrichActivity() { } - - protected virtual void SetStatus() { } - - public void Dispose() - { - if (!_disposed) - { - EnrichActivity(); - SetStatus(); - Activity.Dispose(); - _disposed = true; - } - } -} diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ValidateDocumentScope.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ValidateDocumentScope.cs deleted file mode 100644 index 2f4b9dc58cf..00000000000 --- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Scopes/ValidateDocumentScope.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Execution; -using OpenTelemetry.Trace; - -namespace HotChocolate.Fusion.Diagnostics.Scopes; - -internal sealed class ValidateDocumentScope( - FusionActivityEnricher enricher, - RequestContext context, - Activity activity) - : RequestScopeBase(enricher, context, activity) -{ - protected override void EnrichActivity() - => Enricher.EnrichValidateDocument(Context, Activity); - - protected override void SetStatus() - { - if (Context.IsOperationDocumentValid()) - { - Activity.SetStatus(Status.Ok); - Activity.SetStatus(ActivityStatusCode.Ok); - } - } -} diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Spans/ExecutePlanNodeSpan.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Spans/ExecutePlanNodeSpan.cs new file mode 100644 index 00000000000..c3f7b26f4fe --- /dev/null +++ b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Spans/ExecutePlanNodeSpan.cs @@ -0,0 +1,84 @@ +using System.Collections.Frozen; +using System.Diagnostics; +using System.Globalization; +using HotChocolate.Fusion.Diagnostics; +using HotChocolate.Fusion.Execution; +using HotChocolate.Fusion.Execution.Nodes; +using static HotChocolate.Diagnostics.SemanticConventions; + +namespace HotChocolate.Diagnostics; + +internal sealed class ExecutePlanNodeSpan( + Activity activity, + OperationPlanContext context, + ExecutionNode node, + string? schemaName, + FusionActivityEnricher enricher) : SpanBase(activity) +{ + private static FrozenDictionary KindValues { get; } = + new Dictionary + { + [ExecutionNodeType.Operation] = GraphQL.Operation.Step.KindValues.Operation, + [ExecutionNodeType.OperationBatch] = GraphQL.Operation.Step.KindValues.OperationBatch, + [ExecutionNodeType.Introspection] = GraphQL.Operation.Step.KindValues.Introspection, + [ExecutionNodeType.Node] = GraphQL.Operation.Step.KindValues.Node + }.ToFrozenDictionary(); + + public static ExecutePlanNodeSpan? Start( + ActivitySource source, + OperationPlanContext context, + ExecutionNode node, + string? schemaName, + FusionActivityEnricher enricher) + { + var activity = source.StartActivity("GraphQL Step Execution"); + + if (activity is null) + { + return null; + } + + activity.SetTag(GraphQL.Processing.Type, GraphQL.Processing.TypeValues.StepExecute); + + var operation = context.OperationPlan.Operation; + activity.EnrichOperation(operation.Definition.Operation, operation.Name); + activity.EnrichDocumentInfo(context.RequestContext.OperationDocumentInfo); + + activity.SetTag(GraphQL.Operation.Step.Id, node.Id.ToString(CultureInfo.InvariantCulture)); + activity.SetTag(GraphQL.Operation.Step.Kind, KindValues[node.Type]); + activity.SetTag(GraphQL.Operation.Step.Plan.Id, context.OperationPlan.Id); + + if (node is OperationExecutionNode operationExecutionNode) + { + SetSourceSchemaTags(activity, operationExecutionNode.Operation, schemaName); + } + else if (node is OperationBatchExecutionNode batchExecutionNode) + { + SetSourceSchemaTags(activity, batchExecutionNode.Operation, schemaName); + } + + return new ExecutePlanNodeSpan(activity, context, node, schemaName, enricher); + } + + protected override void OnComplete() + { + if (Activity.Status != ActivityStatusCode.Error) + { + Activity.SetStatus(ActivityStatusCode.Ok); + } + + enricher.EnrichExecutePlanNode(context, node, schemaName, Activity); + } + + private static void SetSourceSchemaTags(Activity activity, OperationSourceText operation, string? schemaName) + { + if (!string.IsNullOrWhiteSpace(schemaName)) + { + activity.SetTag(GraphQL.Source.Name, schemaName); + } + + activity.SetTag(GraphQL.Source.Operation.Name, operation.Name); + activity.SetTag(GraphQL.Source.Operation.Kind, GraphQL.Operation.TypeValues[operation.Type]); + activity.SetTag(GraphQL.Source.Operation.Hash, $"sha256:{operation.Hash}"); + } +} diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Spans/ExecuteRequestSpan.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Spans/ExecuteRequestSpan.cs new file mode 100644 index 00000000000..ab6e528af29 --- /dev/null +++ b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Spans/ExecuteRequestSpan.cs @@ -0,0 +1,51 @@ +using System.Diagnostics; +using HotChocolate.Execution; +using HotChocolate.Language; + +namespace HotChocolate.Diagnostics; + +internal sealed class ExecuteRequestSpan( + Activity activity, + RequestContext context, + InstrumentationOptionsBase options, + ActivityEnricherBase? enricher, + bool shouldDisposeActivity) + : ExecuteRequestSpanBase(activity, context, options, enricher, shouldDisposeActivity) +{ + public static ExecuteRequestSpan? Start( + ActivitySource source, + RequestContext context, + InstrumentationOptionsBase options, + ActivityEnricherBase enricher) + { + var activity = StartActivity(source); + + if (activity is null) + { + return null; + } + + return new ExecuteRequestSpan( + activity, + context, + options, + enricher, + true); + } + + protected override bool TryGetOperationInfo( + out OperationType operationType, + out string? operationName) + { + if (Context.GetOperationPlan() is { Operation: var operation }) + { + operationType = operation.Definition.Operation; + operationName = operation.Name; + return true; + } + + operationType = default; + operationName = null; + return false; + } +} diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Spans/PlanOperationSpan.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Spans/PlanOperationSpan.cs new file mode 100644 index 00000000000..0fb245e03d8 --- /dev/null +++ b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Spans/PlanOperationSpan.cs @@ -0,0 +1,46 @@ +using System.Diagnostics; +using HotChocolate.Execution; +using HotChocolate.Fusion.Diagnostics; +using static HotChocolate.Diagnostics.SemanticConventions; + +namespace HotChocolate.Diagnostics; + +internal sealed class PlanOperationSpan( + Activity activity, + RequestContext context, + FusionActivityEnricher enricher, + string operationPlanId) : SpanBase(activity) +{ + public static PlanOperationSpan? Start( + ActivitySource source, + RequestContext context, + FusionActivityEnricher enricher, + string operationPlanId) + { + var activity = source.StartActivity("GraphQL Operation Planning"); + + if (activity is null) + { + return null; + } + + activity.SetTag(GraphQL.Processing.Type, GraphQL.Processing.TypeValues.Plan); + + activity.EnrichDocumentInfo(context.OperationDocumentInfo); + + return new PlanOperationSpan(activity, context, enricher, operationPlanId); + } + + protected override void OnComplete() + { + if (context.GetOperationPlan() is { } plan) + { + Activity.SetStatus(ActivityStatusCode.Ok); + + var operation = plan.Operation; + Activity.EnrichOperation(operation.Definition.Operation, operation.Name); + } + + enricher.EnrichPlanOperation(context, operationPlanId, Activity); + } +} diff --git a/src/HotChocolate/Fusion/src/Fusion.Execution/Diagnostics/AggregateFusionExecutionDiagnosticEvents.cs b/src/HotChocolate/Fusion/src/Fusion.Execution/Diagnostics/AggregateFusionExecutionDiagnosticEvents.cs index 2f81373196f..c2ef7993abc 100644 --- a/src/HotChocolate/Fusion/src/Fusion.Execution/Diagnostics/AggregateFusionExecutionDiagnosticEvents.cs +++ b/src/HotChocolate/Fusion/src/Fusion.Execution/Diagnostics/AggregateFusionExecutionDiagnosticEvents.cs @@ -186,7 +186,7 @@ public IDisposable ExecuteOperationNode( public IDisposable ExecuteOperationBatchNode( OperationPlanContext context, - ExecutionNode node, + OperationBatchExecutionNode node, string schemaName) { var scopes = new IDisposable[listeners.Length]; @@ -259,24 +259,6 @@ public IDisposable ExecuteSubscriptionNode( return new AggregateActivityScope(scopes); } - public void SubscriptionTransportError( - OperationPlanContext context, - ExecutionNode node, - string schemaName, - ulong subscriptionId, - Exception exception) - { - for (var i = 0; i < listeners.Length; i++) - { - listeners[i].SubscriptionTransportError( - context, - node, - schemaName, - subscriptionId, - exception); - } - } - public void SubscriptionEventError( OperationPlanContext context, ExecutionNode node, diff --git a/src/HotChocolate/Fusion/src/Fusion.Execution/Diagnostics/FusionExecutionDiagnosticEventListener.cs b/src/HotChocolate/Fusion/src/Fusion.Execution/Diagnostics/FusionExecutionDiagnosticEventListener.cs index f54cd83f3e2..aa224864e45 100644 --- a/src/HotChocolate/Fusion/src/Fusion.Execution/Diagnostics/FusionExecutionDiagnosticEventListener.cs +++ b/src/HotChocolate/Fusion/src/Fusion.Execution/Diagnostics/FusionExecutionDiagnosticEventListener.cs @@ -96,7 +96,7 @@ public virtual IDisposable ExecuteOperationNode( /// public virtual IDisposable ExecuteOperationBatchNode( OperationPlanContext context, - ExecutionNode node, + OperationBatchExecutionNode node, string schemaName) => EmptyScope; @@ -137,16 +137,6 @@ public virtual IDisposable ExecuteSubscriptionNode( ulong subscriptionId) => EmptyScope; - /// - public virtual void SubscriptionTransportError( - OperationPlanContext context, - ExecutionNode node, - string schemaName, - ulong subscriptionId, - Exception exception) - { - } - /// public virtual void SubscriptionEventError( OperationPlanContext context, diff --git a/src/HotChocolate/Fusion/src/Fusion.Execution/Diagnostics/IFusionExecutionDiagnosticEvents.cs b/src/HotChocolate/Fusion/src/Fusion.Execution/Diagnostics/IFusionExecutionDiagnosticEvents.cs index 56e50633fc0..81284ae5c0d 100644 --- a/src/HotChocolate/Fusion/src/Fusion.Execution/Diagnostics/IFusionExecutionDiagnosticEvents.cs +++ b/src/HotChocolate/Fusion/src/Fusion.Execution/Diagnostics/IFusionExecutionDiagnosticEvents.cs @@ -130,7 +130,7 @@ IDisposable ExecuteOperationNode( /// IDisposable ExecuteOperationBatchNode( OperationPlanContext context, - ExecutionNode node, + OperationBatchExecutionNode node, string schemaName); /// @@ -234,33 +234,6 @@ void SourceSchemaStoreError( string schemaName, Exception error); - /// - /// Called when a transport error occurs while communicating with a source schema - /// during subscription operations. This includes connection drops, network timeouts, - /// and other communication failures specific to real-time subscriptions. - /// - /// - /// The operation plan context. - /// - /// - /// The execution node that was storing the response. - /// - /// - /// The name of the source schema whose response could not be stored. - /// - /// - /// An internal identifier for the subscription instance. - /// - /// - /// The transport exception that occurred. - /// - void SubscriptionTransportError( - OperationPlanContext context, - ExecutionNode node, - string schemaName, - ulong subscriptionId, - Exception exception); - /// /// Called when an error occurs while processing a subscription event result. /// This covers errors in event handling, data transformation, or result generation diff --git a/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Nodes/OperationExecutionNode.cs b/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Nodes/OperationExecutionNode.cs index aae561d3d1d..e0a7d462099 100644 --- a/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Nodes/OperationExecutionNode.cs +++ b/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Nodes/OperationExecutionNode.cs @@ -317,7 +317,7 @@ internal async Task SubscribeAsync( catch (Exception ex) { AddErrors(context, ex, variables, _responseNames); - context.DiagnosticEvents.SubscriptionTransportError(context, this, schemaName, subscriptionId, ex); + context.DiagnosticEvents.SourceSchemaTransportError(context, this, schemaName, ex); return SubscriptionResult.Failed(subscriptionId, ex); } } diff --git a/src/HotChocolate/Fusion/src/Fusion.Utilities/Extensions/OperationResolverHelper.cs b/src/HotChocolate/Fusion/src/Fusion.Utilities/Extensions/FusionDocumentNodeExtensions.cs similarity index 57% rename from src/HotChocolate/Fusion/src/Fusion.Utilities/Extensions/OperationResolverHelper.cs rename to src/HotChocolate/Fusion/src/Fusion.Utilities/Extensions/FusionDocumentNodeExtensions.cs index 42a6c4696d3..772487fbe6f 100644 --- a/src/HotChocolate/Fusion/src/Fusion.Utilities/Extensions/OperationResolverHelper.cs +++ b/src/HotChocolate/Fusion/src/Fusion.Utilities/Extensions/FusionDocumentNodeExtensions.cs @@ -27,15 +27,13 @@ public static OperationDefinitionNode GetOperation( } else { - // TODO : EXCEPTION - throw new Exception("OperationResolverHelper_MultipleOperation"); + throw OperationResolverHelper_MultipleOperation(operation, op); } } if (operation is null) { - // TODO : EXCEPTION - throw new Exception("OperationResolverHelper_NoOperationFound"); + throw OperationResolverHelper_NoOperationFound(document); } return operation; @@ -51,8 +49,7 @@ public static OperationDefinitionNode GetOperation( } } - // TODO : EXCEPTION - throw new Exception("OperationResolverHelper_InvalidOperationName"); + throw OperationResolverHelper_InvalidOperationName(document, operationName); } } @@ -73,4 +70,29 @@ public static Dictionary GetFragments( return map; } + + private static GraphQLException OperationResolverHelper_NoOperationFound( + DocumentNode documentNode) => + new(ErrorBuilder.New() + .SetMessage("There are no operations in the GraphQL document.") + .AddLocation(documentNode) + .Build()); + + private static GraphQLException OperationResolverHelper_MultipleOperation( + OperationDefinitionNode firstOperation, + OperationDefinitionNode secondOperation) => + new(ErrorBuilder.New() + .SetMessage("The operation name can only be omitted if there is just one operation in a GraphQL document.") + .AddLocation(firstOperation) + .AddLocation(secondOperation) + .Build()); + + private static GraphQLException OperationResolverHelper_InvalidOperationName( + DocumentNode documentNode, + string operationName) => + new(ErrorBuilder.New() + .SetMessage("The specified operation `{0}` cannot be found.", operationName) + .AddLocation(documentNode) + .SetExtension("operationName", operationName) + .Build()); } diff --git a/src/HotChocolate/Fusion/src/Fusion.Utilities/HotChocolate.Fusion.Utilities.csproj b/src/HotChocolate/Fusion/src/Fusion.Utilities/HotChocolate.Fusion.Utilities.csproj index 0fcb3b3147e..52e0cb9bc72 100644 --- a/src/HotChocolate/Fusion/src/Fusion.Utilities/HotChocolate.Fusion.Utilities.csproj +++ b/src/HotChocolate/Fusion/src/Fusion.Utilities/HotChocolate.Fusion.Utilities.csproj @@ -6,6 +6,7 @@ + diff --git a/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/__snapshots__/VariableCoercionTests.InputObject_Invalid_Field.yaml b/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/__snapshots__/VariableCoercionTests.InputObject_Invalid_Field.yaml index 7b2490d8f64..ad5a138da57 100644 --- a/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/__snapshots__/VariableCoercionTests.InputObject_Invalid_Field.yaml +++ b/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/__snapshots__/VariableCoercionTests.InputObject_Invalid_Field.yaml @@ -19,7 +19,7 @@ response: { "message": "The field \u0060invalidField\u0060 is not defined on the input object type \u0060Cat\u0060.", "extensions": { - "variable": "/cat" + "variable": "cat" } } ] diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/ActivityTestHelper.cs b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/ActivityTestHelper.cs index f05657828b7..73d6efe9eac 100644 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/ActivityTestHelper.cs +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/ActivityTestHelper.cs @@ -5,12 +5,16 @@ namespace HotChocolate.Fusion.Diagnostics; public static partial class ActivityTestHelper { + [GeneratedRegex(@" in (?.+?):line (?\d+)", RegexOptions.CultureInvariant)] + private static partial Regex StackTracePathRegex(); + public static IDisposable CaptureActivities(out object activities) { var sync = new object(); var listener = new ActivityListener(); var root = new OrderedDictionary(); var lookup = new Dictionary>(); + var spanLookup = new Dictionary>(); Activity rootActivity = null!; listener.ShouldListenTo = source => @@ -32,6 +36,17 @@ public static IDisposable CaptureActivities(out object activities) { RegisterActivity(a, parentData); lookup[a] = (OrderedDictionary)a.GetCustomProperty("test.data")!; + spanLookup[a.SpanId] = (OrderedDictionary)a.GetCustomProperty("test.data")!; + return; + } + + if (a.Parent is null + && a.ParentSpanId != default + && spanLookup.TryGetValue(a.ParentSpanId, out parentData)) + { + RegisterActivity(a, parentData); + lookup[a] = (OrderedDictionary)a.GetCustomProperty("test.data")!; + spanLookup[a.SpanId] = (OrderedDictionary)a.GetCustomProperty("test.data")!; } } }; @@ -43,6 +58,7 @@ public static IDisposable CaptureActivities(out object activities) rootActivity = HotChocolateFusionActivitySource.Source.StartActivity()!; rootActivity.SetCustomProperty("test.data", root); lookup[rootActivity] = root; + spanLookup[rootActivity.SpanId] = root; activities = root; return new Session(rootActivity, listener); @@ -85,18 +101,25 @@ private static void SerializeActivity(Activity activity) } private static IEnumerable> ScrubEventTags( - IEnumerable> tags) + IEnumerable>? tags) { + if (tags is null) + { + yield break; + } + foreach (var tag in tags) { - if (tag is { Key: "exception.stacktrace", Value: string stackTrace }) + if (tag.Value is string stackTrace + && (tag.Key.Equals("exception.stacktrace", StringComparison.Ordinal) + || tag.Key.EndsWith(".stacktrace", StringComparison.Ordinal))) { yield return new KeyValuePair( tag.Key, StackTracePathRegex().Replace(stackTrace, match => { - var fileName = System.IO.Path.GetFileName(match.Groups[1].Value); - var lineNumber = match.Groups[2].Value; + var fileName = System.IO.Path.GetFileName(match.Groups["path"].Value); + var lineNumber = match.Groups["line"].Value; return $" in {fileName}:line {lineNumber}"; })); } @@ -107,9 +130,6 @@ private static void SerializeActivity(Activity activity) } } - [GeneratedRegex(@" in (.+):line (\d+)")] - private static partial Regex StackTracePathRegex(); - private sealed class Session : IDisposable { private readonly Activity _activity; diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/QueryInstrumentationTests.cs b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/FusionActivityExecutionDiagnosticListenerTests.cs similarity index 64% rename from src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/QueryInstrumentationTests.cs rename to src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/FusionActivityExecutionDiagnosticListenerTests.cs index 333c442cb85..848b1658fc7 100644 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/QueryInstrumentationTests.cs +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/FusionActivityExecutionDiagnosticListenerTests.cs @@ -1,12 +1,14 @@ -using System.Diagnostics; using HotChocolate.Execution; +using HotChocolate.Language; +using HotChocolate.PersistedOperations; +using HotChocolate.Types; using Microsoft.Extensions.DependencyInjection; using static HotChocolate.Fusion.Diagnostics.ActivityTestHelper; namespace HotChocolate.Fusion.Diagnostics; [Collection("Instrumentation")] -public class QueryInstrumentationTests : FusionTestBase +public class FusionActivityExecutionDiagnosticListenerTests : FusionTestBase { [Fact] public async Task Track_Events_Of_A_Simple_Query_Default() @@ -40,9 +42,9 @@ public async Task Track_Events_Of_A_Simple_Query_Default() } [Fact] - public async Task Track_Events_Of_A_Simple_Query_Default_Rename_Root() + public async Task Allow_Document_To_Be_Captured() { - using (CaptureActivities(out _)) + using (CaptureActivities(out var activities)) { // arrange using var server1 = CreateSourceSchema( @@ -55,28 +57,28 @@ public async Task Track_Events_Of_A_Simple_Query_Default_Rename_Root() ], configureGatewayBuilder: b => b.AddInstrumentation(o => { - o.RenameRootActivity = true; o.Scopes = FusionActivityScopes.All; + o.IncludeDocument = true; })); var executor = await gateway.Services.GetRequestExecutorAsync(); var request = OperationRequestBuilder.New() - .SetDocument("{ sayHello }") + .SetDocument("query SayHelloOperation { sayHello }") .Build(); // act await executor.ExecuteAsync(request); // assert - Assert.Equal("CaptureActivities: query { sayHello }", Activity.Current!.DisplayName); + activities.MatchSnapshot(); } } [Fact] - public async Task Parsing_Error_When_Rename_Root_Is_Activated() + public async Task Ensure_That_The_Validation_Activity_Has_An_Error_Status() { - using (CaptureActivities(out _)) + using (CaptureActivities(out var activities)) { // arrange using var server1 = CreateSourceSchema( @@ -89,28 +91,28 @@ public async Task Parsing_Error_When_Rename_Root_Is_Activated() ], configureGatewayBuilder: b => b.AddInstrumentation(o => { - o.RenameRootActivity = true; o.Scopes = FusionActivityScopes.All; + o.IncludeDocument = true; })); var executor = await gateway.Services.GetRequestExecutorAsync(); var request = OperationRequestBuilder.New() - .SetDocument("{ sayHello") + .SetDocument("query SayHelloOperation { sayHello_ }") .Build(); // act await executor.ExecuteAsync(request); // assert - Assert.Equal("CaptureActivities: Begin Parse Document", Activity.Current!.DisplayName); + activities.MatchSnapshot(); } } [Fact] - public async Task Validation_Error_When_Rename_Root_Is_Activated() + public async Task Cause_A_Resolver_Error_That_Deletes_The_Whole_Result() { - using (CaptureActivities(out _)) + using (CaptureActivities(out var activities)) { // arrange using var server1 = CreateSourceSchema( @@ -123,34 +125,34 @@ public async Task Validation_Error_When_Rename_Root_Is_Activated() ], configureGatewayBuilder: b => b.AddInstrumentation(o => { - o.RenameRootActivity = true; o.Scopes = FusionActivityScopes.All; + o.IncludeDocument = true; })); var executor = await gateway.Services.GetRequestExecutorAsync(); var request = OperationRequestBuilder.New() - .SetDocument("{ abc123 }") + .SetDocument("query SayHelloOperation { causeFatalError }") .Build(); // act await executor.ExecuteAsync(request); // assert - Assert.Equal("CaptureActivities: Begin Validate Document", - Activity.Current!.DisplayName); + activities.MatchSnapshot(); } } [Fact] - public async Task Create_Operation_Display_Name_With_1_Field() + public async Task Source_Schema_Transport_Error() { using (CaptureActivities(out var activities)) { // arrange using var server1 = CreateSourceSchema( "a", - b => b.AddQueryType()); + b => b.AddQueryType(), + isOffline: true); using var gateway = await CreateCompositeSchemaAsync( [ @@ -158,14 +160,14 @@ public async Task Create_Operation_Display_Name_With_1_Field() ], configureGatewayBuilder: b => b.AddInstrumentation(o => { - o.RenameRootActivity = true; o.Scopes = FusionActivityScopes.All; + o.IncludeDocument = true; })); var executor = await gateway.Services.GetRequestExecutorAsync(); var request = OperationRequestBuilder.New() - .SetDocument("{ a: sayHello }") + .SetDocument("{ sayHello }") .Build(); // act @@ -177,29 +179,31 @@ public async Task Create_Operation_Display_Name_With_1_Field() } [Fact] - public async Task Create_Operation_Display_Name_With_1_Field_And_Op() + public async Task Track_Events_Of_A_Query_With_Multiple_Sources() { using (CaptureActivities(out var activities)) { // arrange using var server1 = CreateSourceSchema( "a", - b => b.AddQueryType()); + b => b.AddQueryType()); + + using var server2 = CreateSourceSchema( + "b", + b => b.AddQueryType()); using var gateway = await CreateCompositeSchemaAsync( [ - ("a", server1) + ("a", server1), + ("b", server2) ], configureGatewayBuilder: b => b.AddInstrumentation(o => - { - o.RenameRootActivity = true; - o.Scopes = FusionActivityScopes.All; - })); + o.Scopes = FusionActivityScopes.All)); var executor = await gateway.Services.GetRequestExecutorAsync(); var request = OperationRequestBuilder.New() - .SetDocument("query GetA { a: sayHello }") + .SetDocument("{ sayHello sayGoodbye }") .Build(); // act @@ -211,11 +215,14 @@ public async Task Create_Operation_Display_Name_With_1_Field_And_Op() } [Fact] - public async Task Create_Operation_Display_Name_With_3_Field() + public async Task PersistedOperation_LoadsFromStorage_DefaultScopes() { using (CaptureActivities(out var activities)) { // arrange + var storage = new InMemoryOperationDocumentStorage(); + storage.Add("sayHelloOp", "{ sayHello }"); + using var server1 = CreateSourceSchema( "a", b => b.AddQueryType()); @@ -224,20 +231,16 @@ public async Task Create_Operation_Display_Name_With_3_Field() [ ("a", server1) ], - configureGatewayBuilder: b => b.AddInstrumentation(o => - { - o.RenameRootActivity = true; - o.Scopes = FusionActivityScopes.All; - })); + configureGatewayBuilder: b => b + .AddInstrumentation() + .ConfigureSchemaServices( + (_, s) => s.AddSingleton(storage)) + .UsePersistedOperationPipeline()); var executor = await gateway.Services.GetRequestExecutorAsync(); - var request = OperationRequestBuilder.New() - .SetDocument("{ a: sayHello b: sayHello c: sayHello }") - .Build(); - // act - await executor.ExecuteAsync(request); + await executor.ExecuteAsync(OperationRequest.FromId("sayHelloOp")); // assert activities.MatchSnapshot(); @@ -245,7 +248,7 @@ public async Task Create_Operation_Display_Name_With_3_Field() } [Fact] - public async Task Create_Operation_Display_Name_With_4_Field() + public async Task ParsingError_InvalidGraphQLDocument_ReportsErrorStatus() { using (CaptureActivities(out var activities)) { @@ -259,15 +262,12 @@ public async Task Create_Operation_Display_Name_With_4_Field() ("a", server1) ], configureGatewayBuilder: b => b.AddInstrumentation(o => - { - o.RenameRootActivity = true; - o.Scopes = FusionActivityScopes.All; - })); + o.Scopes = FusionActivityScopes.All)); var executor = await gateway.Services.GetRequestExecutorAsync(); var request = OperationRequestBuilder.New() - .SetDocument("{ a: sayHello b: sayHello c: sayHello d: sayHello }") + .SetDocument("{ sayHello") .Build(); // act @@ -279,7 +279,7 @@ public async Task Create_Operation_Display_Name_With_4_Field() } [Fact] - public async Task Track_Events_Of_A_Simple_Query_Detailed() + public async Task ValidationError_UnknownField_ReportsErrorStatus() { using (CaptureActivities(out var activities)) { @@ -298,7 +298,7 @@ public async Task Track_Events_Of_A_Simple_Query_Detailed() var executor = await gateway.Services.GetRequestExecutorAsync(); var request = OperationRequestBuilder.New() - .SetDocument("{ sayHello }") + .SetDocument("{ unknownField123 }") .Build(); // act @@ -310,7 +310,7 @@ public async Task Track_Events_Of_A_Simple_Query_Detailed() } [Fact] - public async Task Ensure_Operation_Name_Is_Used_As_Request_Name() + public async Task DefaultScopes_ExcludesExecuteRequestAndParseDocumentSpans() { using (CaptureActivities(out var activities)) { @@ -323,13 +323,12 @@ public async Task Ensure_Operation_Name_Is_Used_As_Request_Name() [ ("a", server1) ], - configureGatewayBuilder: b => b.AddInstrumentation(o => - o.Scopes = FusionActivityScopes.All)); + configureGatewayBuilder: b => b.AddInstrumentation()); var executor = await gateway.Services.GetRequestExecutorAsync(); var request = OperationRequestBuilder.New() - .SetDocument("query SayHelloOperation { sayHello }") + .SetDocument("{ sayHello }") .Build(); // act @@ -341,7 +340,7 @@ public async Task Ensure_Operation_Name_Is_Used_As_Request_Name() } [Fact] - public async Task Allow_Document_To_Be_Captured() + public async Task AllScopes_IncludesAllSpans() { using (CaptureActivities(out var activities)) { @@ -355,15 +354,12 @@ public async Task Allow_Document_To_Be_Captured() ("a", server1) ], configureGatewayBuilder: b => b.AddInstrumentation(o => - { - o.Scopes = FusionActivityScopes.All; - o.IncludeDocument = true; - })); + o.Scopes = FusionActivityScopes.All)); var executor = await gateway.Services.GetRequestExecutorAsync(); var request = OperationRequestBuilder.New() - .SetDocument("query SayHelloOperation { sayHello }") + .SetDocument("{ sayHello }") .Build(); // act @@ -375,7 +371,7 @@ public async Task Allow_Document_To_Be_Captured() } [Fact] - public async Task Ensure_That_The_Validation_Activity_Has_An_Error_Status() + public async Task CustomScopes_OnlyValidateAndPlan_LimitsSpans() { using (CaptureActivities(out var activities)) { @@ -389,15 +385,13 @@ public async Task Ensure_That_The_Validation_Activity_Has_An_Error_Status() ("a", server1) ], configureGatewayBuilder: b => b.AddInstrumentation(o => - { - o.Scopes = FusionActivityScopes.All; - o.IncludeDocument = true; - })); + o.Scopes = FusionActivityScopes.ValidateDocument + | FusionActivityScopes.PlanOperation)); var executor = await gateway.Services.GetRequestExecutorAsync(); var request = OperationRequestBuilder.New() - .SetDocument("query SayHelloOperation { sayHello_ }") + .SetDocument("{ sayHello }") .Build(); // act @@ -408,19 +402,25 @@ public async Task Ensure_That_The_Validation_Activity_Has_An_Error_Status() } } - [Fact] - public async Task Cause_A_Resolver_Error_That_Deletes_The_Whole_Result() + [Fact(Skip = "This is flaky")] + public async Task MultipleSources_HttpRequestError_MarksNodeSpanAsError() { using (CaptureActivities(out var activities)) { // arrange using var server1 = CreateSourceSchema( "a", - b => b.AddQueryType()); + b => b.AddQueryType()); + + using var server2 = CreateSourceSchema( + "b", + b => b.AddQueryType(), + isOffline: true); using var gateway = await CreateCompositeSchemaAsync( [ - ("a", server1) + ("a", server1), + ("b", server2) ], configureGatewayBuilder: b => b.AddInstrumentation(o => { @@ -431,7 +431,7 @@ public async Task Cause_A_Resolver_Error_That_Deletes_The_Whole_Result() var executor = await gateway.Services.GetRequestExecutorAsync(); var request = OperationRequestBuilder.New() - .SetDocument("query SayHelloOperation { causeFatalError }") + .SetDocument("{ sayHello sayGoodbye }") .Build(); // act @@ -443,18 +443,23 @@ public async Task Cause_A_Resolver_Error_That_Deletes_The_Whole_Result() } [Fact] - public async Task Cause_A_Resolver_Error_That_Deletes_The_Whole_Result_Deep() + public async Task MultipleSources_SourceSchemaResolverError_RecordsDeeplyNestedError() { using (CaptureActivities(out var activities)) { // arrange using var server1 = CreateSourceSchema( "a", - b => b.AddQueryType()); + b => b.AddQueryType()); + + using var server2 = CreateSourceSchema( + "b", + b => b.AddQueryType()); using var gateway = await CreateCompositeSchemaAsync( [ - ("a", server1) + ("a", server1), + ("b", server2) ], configureGatewayBuilder: b => b.AddInstrumentation(o => { @@ -467,14 +472,11 @@ public async Task Cause_A_Resolver_Error_That_Deletes_The_Whole_Result_Deep() var request = OperationRequestBuilder.New() .SetDocument( """ - query SayHelloOperation { - deep { - deeper { - deeps { - deeper { - causeFatalError - } - } + { + sayHello + deepB { + deeperB { + causeFatalError } } } @@ -490,14 +492,52 @@ query SayHelloOperation { } [Fact] - public async Task Track_Events_Of_A_Simple_Query_With_Node_Scopes() + public async Task DocumentCache_SecondExecution_RecordsCacheHitEvent() { + // arrange + using var server1 = CreateSourceSchema( + "a", + b => b.AddQueryType()); + + using var gateway = await CreateCompositeSchemaAsync( + [ + ("a", server1) + ], + configureGatewayBuilder: b => b.AddInstrumentation(o => + o.Scopes = FusionActivityScopes.All)); + + var executor = await gateway.Services.GetRequestExecutorAsync(); + + // act - execute twice so second uses cached document + var request = OperationRequestBuilder.New() + .SetDocument("{ sayHello }") + .SetDocumentHash(new OperationDocumentHash("abc", "sha256", HashFormat.Hex)) + .Build(); + + await executor.ExecuteAsync(request); + + using (CaptureActivities(out var activities)) + { + await executor.ExecuteAsync(request); + + // assert + activities.MatchSnapshot(); + } + } + + [Fact] + public async Task SubscriptionEvent_Records_Subscription_Event_Span() + { + using var cts = new CancellationTokenSource(5000); + using (CaptureActivities(out var activities)) { // arrange using var server1 = CreateSourceSchema( "a", - b => b.AddQueryType()); + b => b + .AddQueryType() + .AddSubscriptionType()); using var gateway = await CreateCompositeSchemaAsync( [ @@ -508,83 +548,110 @@ public async Task Track_Events_Of_A_Simple_Query_With_Node_Scopes() var executor = await gateway.Services.GetRequestExecutorAsync(); - var request = OperationRequestBuilder.New() - .SetDocument("{ sayHello }") - .Build(); - // act - await executor.ExecuteAsync(request); + await using var result = await executor.ExecuteAsync( + "subscription OnMessageSubscription { onMessage }"); + await using var responseStream = result.ExpectResponseStream(); + var results = responseStream.ReadResultsAsync().GetAsyncEnumerator(cts.Token); + + try + { + Assert.True(await results.MoveNextAsync()); + } + finally + { + await results.DisposeAsync(); + } // assert activities.MatchSnapshot(); } } - [Fact] - public async Task Source_Schema_Transport_Error() + [Fact(Skip = "Errors are not correctly triggered")] + public async Task SubscriptionEventError_Records_Subscription_Event_Error() { + using var cts = new CancellationTokenSource(5000); + using (CaptureActivities(out var activities)) { // arrange using var server1 = CreateSourceSchema( "a", - b => b.AddQueryType(), - isOffline: true); + b => b + .AddQueryType() + .AddSubscriptionType()); using var gateway = await CreateCompositeSchemaAsync( [ ("a", server1) ], configureGatewayBuilder: b => b.AddInstrumentation(o => - { - o.Scopes = FusionActivityScopes.All; - o.IncludeDocument = true; - })); + o.Scopes = FusionActivityScopes.All)); var executor = await gateway.Services.GetRequestExecutorAsync(); - var request = OperationRequestBuilder.New() - .SetDocument("{ sayHello }") - .Build(); - // act - await executor.ExecuteAsync(request); + await using var result = await executor.ExecuteAsync( + "subscription OnFailingMessageSubscription { onFailingMessage }"); + await using var responseStream = result.ExpectResponseStream(); + var results = responseStream.ReadResultsAsync().GetAsyncEnumerator(cts.Token); + + try + { + Assert.True(await results.MoveNextAsync()); + } + finally + { + await results.DisposeAsync(); + } // assert activities.MatchSnapshot(); } } - [Fact] - public async Task Track_Events_Of_A_Query_With_Multiple_Sources() + [Fact(Skip = "Errors are not correctly triggered")] + public async Task SubscriptionRequestFails_When_SourceSchema_Is_Offline() { using (CaptureActivities(out var activities)) { // arrange using var server1 = CreateSourceSchema( "a", - b => b.AddQueryType()); - - using var server2 = CreateSourceSchema( - "b", - b => b.AddQueryType()); + b => b + .AddQueryType() + .AddSubscriptionType(), + isOffline: true); using var gateway = await CreateCompositeSchemaAsync( [ - ("a", server1), - ("b", server2) + ("a", server1) ], configureGatewayBuilder: b => b.AddInstrumentation(o => o.Scopes = FusionActivityScopes.All)); var executor = await gateway.Services.GetRequestExecutorAsync(); - var request = OperationRequestBuilder.New() - .SetDocument("{ sayHello sayGoodbye }") - .Build(); - // act - await executor.ExecuteAsync(request); + IExecutionResult? result = null; + + try + { + result = await executor.ExecuteAsync( + "subscription OnMessageSubscription { onMessage }"); + } + catch + { + // expected for failed subscription handshake. + } + finally + { + if (result is not null) + { + await result.DisposeAsync(); + } + } // assert activities.MatchSnapshot(); @@ -612,6 +679,24 @@ public class QueryB public string SayGoodbye() => "goodbye"; } + [GraphQLName("Query")] + public class QueryBWithDeepError + { + public string SayGoodbye() => "goodbye"; + + public DeepB DeepB() => new(); + } + + public class DeepB + { + public DeeperB DeeperB() => new(); + } + + public class DeeperB + { + public string CauseFatalError() => throw new GraphQLException("deep fail"); + } + public class Deep { public Deeper Deeper() => new(); @@ -623,4 +708,55 @@ public class Deeper { public Deep[] Deeps() => [new Deep()]; } + + public class Subscription + { + public async IAsyncEnumerable OnMessageStream() + { + yield return "hello"; + await Task.CompletedTask; + } + + [Subscribe(With = nameof(OnMessageStream))] + public string OnMessage([EventMessage] string message) => message; + + public async IAsyncEnumerable OnFailingMessageStream() + { + yield return "hello"; + await Task.CompletedTask; + } + + [Subscribe(With = nameof(OnFailingMessageStream))] + public string OnFailingMessage([EventMessage] string message) + => throw new InvalidOperationException("Subscription event failed."); + } + + private sealed class InMemoryOperationDocumentStorage : IOperationDocumentStorage + { + private readonly Dictionary _cache = []; + + public void Add(string id, string document) + => _cache[id] = Utf8GraphQLParser.Parse(document); + + public ValueTask TryReadAsync( + OperationDocumentId documentId, + CancellationToken cancellationToken = default) + { + if (_cache.TryGetValue(documentId.Value, out var document)) + { + return new ValueTask(new OperationDocument(document)); + } + + return new ValueTask(default(IOperationDocument)); + } + + public ValueTask SaveAsync( + OperationDocumentId documentId, + IOperationDocument document, + CancellationToken cancellationToken = default) + { + _cache[documentId.Value] = Utf8GraphQLParser.Parse(document.AsSpan()); + return default; + } + } } diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/ServerInstrumentationTests.cs b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/FusionActivityServerDiagnosticListenerTests.cs similarity index 85% rename from src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/ServerInstrumentationTests.cs rename to src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/FusionActivityServerDiagnosticListenerTests.cs index f5d49d69610..fd0d7d27498 100644 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/ServerInstrumentationTests.cs +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/FusionActivityServerDiagnosticListenerTests.cs @@ -1,3 +1,4 @@ +using HotChocolate.Diagnostics; using HotChocolate.Transport.Http; using Microsoft.Extensions.DependencyInjection; using static HotChocolate.Fusion.Diagnostics.ActivityTestHelper; @@ -6,11 +7,11 @@ namespace HotChocolate.Fusion.Diagnostics; [Collection("Instrumentation")] -public class ServerInstrumentationTests : FusionTestBase +public class FusionActivityServerDiagnosticListenerTests : FusionTestBase { private static readonly Uri s_url = new("http://localhost:5000/graphql"); - [Fact(Skip = "This is flaky")] + [Fact] public async Task Http_Post_Single_Request_Default() { using (CaptureActivities(out var activities)) @@ -30,13 +31,14 @@ public async Task Http_Post_Single_Request_Default() // act using var result = await client.PostAsync(request, s_url); + await result.ReadAsResultAsync(); // assert activities.MatchSnapshot(); } } - [Fact(Skip = "This is flaky")] + [Fact] public async Task Http_Post_Single_Request() { using (CaptureActivities(out var activities)) @@ -57,13 +59,14 @@ public async Task Http_Post_Single_Request() // act using var result = await client.PostAsync(request, s_url); + await result.ReadAsResultAsync(); // assert activities.MatchSnapshot(); } } - [Fact(Skip = "This is flaky")] + [Fact] public async Task Http_Get_Single_Request() { using (CaptureActivities(out var activities)) @@ -84,13 +87,14 @@ public async Task Http_Get_Single_Request() // act using var result = await client.GetAsync(request, s_url); + await result.ReadAsResultAsync(); // assert activities.MatchSnapshot(); } } - [Fact(Skip = "This is flaky")] + [Fact] public async Task Http_Post_Variables_Are_Not_Automatically_Added_To_Activities() { using (CaptureActivities(out var activities)) @@ -117,13 +121,14 @@ public async Task Http_Post_Variables_Are_Not_Automatically_Added_To_Activities( // act using var result = await client.PostAsync(request, s_url); + await result.ReadAsResultAsync(); // assert activities.MatchSnapshot(); } } - [Fact(Skip = "This is flaky")] + [Fact] public async Task Http_Post_Add_Variables_To_Http_Activity() { using (CaptureActivities(out var activities)) @@ -154,14 +159,15 @@ public async Task Http_Post_Add_Variables_To_Http_Activity() // act using var result = await client.PostAsync(request, s_url); + await result.ReadAsResultAsync(); // assert activities.MatchSnapshot(); } } - [Fact(Skip = "This is flaky")] - public async Task Http_Post_Add_Query_To_Http_Activity() + [Fact] + public async Task Http_Post_With_Extensions_Map() { using (CaptureActivities(out var activities)) { @@ -173,11 +179,7 @@ public async Task Http_Post_Add_Query_To_Http_Activity() using var gateway = await CreateCompositeSchemaAsync( [("a", server)], configureGatewayBuilder: b => b.AddInstrumentation( - o => - { - o.Scopes = FusionActivityScopes.All; - o.RequestDetails = RequestDetails.Default | RequestDetails.Operation; - })); + o => o.Scopes = FusionActivityScopes.All)); using var client = GraphQLHttpClient.Create(gateway.CreateClient()); @@ -187,18 +189,20 @@ public async Task Http_Post_Add_Query_To_Http_Activity() greeting(name: $name) } """, - variables: new Dictionary { { "name", "World" } }); + variables: new Dictionary { { "name", "World" } }, + extensions: new Dictionary { { "test", "abc" } }); // act using var result = await client.PostAsync(request, s_url); + await result.ReadAsResultAsync(); // assert activities.MatchSnapshot(); } } - [Fact(Skip = "This is flaky")] - public async Task Http_Post_With_Extensions_Map() + [Fact] + public async Task Http_Get_SDL_Download() { using (CaptureActivities(out var activities)) { @@ -212,27 +216,20 @@ public async Task Http_Post_With_Extensions_Map() configureGatewayBuilder: b => b.AddInstrumentation( o => o.Scopes = FusionActivityScopes.All)); - using var client = GraphQLHttpClient.Create(gateway.CreateClient()); - - var request = new OperationRequest( - query: """ - query ($name: String!) { - greeting(name: $name) - } - """, - variables: new Dictionary { { "name", "World" } }, - extensions: new Dictionary { { "test", "abc" } }); + var httpClient = gateway.CreateClient(); + var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:5000/graphql?sdl"); // act - using var result = await client.PostAsync(request, s_url); + var response = await httpClient.SendAsync(request); // assert + await response.Content.ReadAsStringAsync(); activities.MatchSnapshot(); } } - [Fact(Skip = "This is flaky")] - public async Task Http_Get_SDL_Download() + [Fact] + public async Task Http_Post_Parser_Error() { using (CaptureActivities(out var activities)) { @@ -246,20 +243,33 @@ public async Task Http_Get_SDL_Download() configureGatewayBuilder: b => b.AddInstrumentation( o => o.Scopes = FusionActivityScopes.All)); - var httpClient = gateway.CreateClient(); - var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:5000/graphql?sdl"); + using var client = GraphQLHttpClient.Create(gateway.CreateClient()); + + // lang=text + var request = new OperationRequest( + """ + { + deep { + deeper { + 1deeps { + name + } + } + } + } + """); // act - var response = await httpClient.SendAsync(request); + using var result = await client.PostAsync(request, s_url); + await result.ReadAsResultAsync(); // assert - await response.Content.ReadAsStringAsync(); activities.MatchSnapshot(); } } - [Fact(Skip = "Not yet implemented")] - public async Task Http_Post_Capture_Deferred_Response() + [Fact] + public async Task RequestDetails_None_ExcludesAllDetails() { using (CaptureActivities(out var activities)) { @@ -271,25 +281,34 @@ public async Task Http_Post_Capture_Deferred_Response() using var gateway = await CreateCompositeSchemaAsync( [("a", server)], configureGatewayBuilder: b => b.AddInstrumentation( - o => o.Scopes = FusionActivityScopes.All)); + o => + { + o.Scopes = FusionActivityScopes.All; + o.RequestDetails = RequestDetails.None; + })); using var client = GraphQLHttpClient.Create(gateway.CreateClient()); var request = new OperationRequest( - """ - TODO - """); + query: """ + query GetGreeting($name: String!) { + greeting(name: $name) + } + """, + variables: new Dictionary { { "name", "World" } }, + extensions: new Dictionary { { "test", "abc" } }); // act using var result = await client.PostAsync(request, s_url); + await result.ReadAsResultAsync(); // assert activities.MatchSnapshot(); } } - [Fact(Skip = "This is flaky")] - public async Task Http_Post_Parser_Error() + [Fact] + public async Task RequestDetails_All_IncludesAllDetails() { using (CaptureActivities(out var activities)) { @@ -301,34 +320,34 @@ public async Task Http_Post_Parser_Error() using var gateway = await CreateCompositeSchemaAsync( [("a", server)], configureGatewayBuilder: b => b.AddInstrumentation( - o => o.Scopes = FusionActivityScopes.All)); + o => + { + o.Scopes = FusionActivityScopes.All; + o.RequestDetails = RequestDetails.All; + })); using var client = GraphQLHttpClient.Create(gateway.CreateClient()); - // lang=text var request = new OperationRequest( - """ - { - deep { - deeper { - 1deeps { - name - } - } + query: """ + query GetGreeting($name: String!) { + greeting(name: $name) } - } - """); + """, + variables: new Dictionary { { "name", "World" } }, + extensions: new Dictionary { { "test", "abc" } }); // act using var result = await client.PostAsync(request, s_url); + await result.ReadAsResultAsync(); // assert activities.MatchSnapshot(); } } - [Fact(Skip = "This is flaky")] - public async Task Parsing_Error_When_Rename_Root_Is_Activated() + [Fact] + public async Task RequestDetails_DocumentOnly_IncludesDocumentTag() { using (CaptureActivities(out var activities)) { @@ -343,24 +362,24 @@ public async Task Parsing_Error_When_Rename_Root_Is_Activated() o => { o.Scopes = FusionActivityScopes.All; - o.RenameRootActivity = true; + o.RequestDetails = RequestDetails.Document; })); using var client = GraphQLHttpClient.Create(gateway.CreateClient()); - // lang=text - var request = new OperationRequest("{ 1 }"); + var request = new OperationRequest("{ sayHello }"); // act using var result = await client.PostAsync(request, s_url); + await result.ReadAsResultAsync(); // assert activities.MatchSnapshot(); } } - [Fact(Skip = "This is flaky")] - public async Task Validation_Error_When_Rename_Root_Is_Activated() + [Fact] + public async Task RequestDetails_Default_IncludesIdHashOperationNameExtensions() { using (CaptureActivities(out var activities)) { @@ -372,18 +391,21 @@ public async Task Validation_Error_When_Rename_Root_Is_Activated() using var gateway = await CreateCompositeSchemaAsync( [("a", server)], configureGatewayBuilder: b => b.AddInstrumentation( - o => - { - o.Scopes = FusionActivityScopes.All; - o.RenameRootActivity = true; - })); + o => o.Scopes = FusionActivityScopes.All)); using var client = GraphQLHttpClient.Create(gateway.CreateClient()); - var request = new OperationRequest("{ abc }"); + var request = new OperationRequest( + query: """ + query GetGreeting { + sayHello + } + """, + extensions: new Dictionary { { "test", "abc" } }); // act using var result = await client.PostAsync(request, s_url); + await result.ReadAsResultAsync(); // assert activities.MatchSnapshot(); diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/HotChocolate.Fusion.Diagnostics.Tests.csproj b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/HotChocolate.Fusion.Diagnostics.Tests.csproj index 2cd3bc4c9ec..73db7d6bb3c 100644 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/HotChocolate.Fusion.Diagnostics.Tests.csproj +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/HotChocolate.Fusion.Diagnostics.Tests.csproj @@ -7,6 +7,7 @@ + diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.AllScopes_IncludesAllSpans.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.AllScopes_IncludesAllSpans.snap new file mode 100644 index 00000000000..3fef61eea55 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.AllScopes_IncludesAllSpans.snap @@ -0,0 +1,149 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [ + { + "Name": "AddedOperationPlanToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.step.id", + "Value": "1" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "456132b93ebaf15a39534753bf72f9f4bfa1152a08d04bc8a88539feec1cb52c" + }, + { + "Key": "graphql.source.name", + "Value": "a" + }, + { + "Key": "graphql.source.operation.name", + "Value": "Op_f7e9989f_1" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:35c1feb1208268226c7d5d5d0ae122e4d38cb79621e862b1e252d37fc83c530a" + } + ], + "event": [] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Allow_Document_To_Be_Captured.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Allow_Document_To_Be_Captured.snap new file mode 100644 index 00000000000..fd10dc4ecf5 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Allow_Document_To_Be_Captured.snap @@ -0,0 +1,169 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "SayHelloOperation" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:6af18618ae20c266f6ffc352b78cb69b" + }, + { + "Key": "graphql.document.body", + "Value": "query SayHelloOperation {\n sayHello\n}" + } + ], + "event": [ + { + "Name": "AddedOperationPlanToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:6af18618ae20c266f6ffc352b78cb69b" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:6af18618ae20c266f6ffc352b78cb69b" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:6af18618ae20c266f6ffc352b78cb69b" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "SayHelloOperation" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "SayHelloOperation" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:6af18618ae20c266f6ffc352b78cb69b" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "SayHelloOperation" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:6af18618ae20c266f6ffc352b78cb69b" + }, + { + "Key": "graphql.operation.step.id", + "Value": "1" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "1334fb0da1250c6db5db84b6c98ccb2556f066942f8836d6ebd18fd870172787" + }, + { + "Key": "graphql.source.name", + "Value": "a" + }, + { + "Key": "graphql.source.operation.name", + "Value": "SayHelloOperation_6af18618_1" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:80b0d05aefd2459dcda18d0e26977b91c512e4ef58ab3e4e8a82c1ec98249b58" + } + ], + "event": [] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Cause_A_Resolver_Error_That_Deletes_The_Whole_Result.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Cause_A_Resolver_Error_That_Deletes_The_Whole_Result.snap new file mode 100644 index 00000000000..f00d97076dc --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Cause_A_Resolver_Error_That_Deletes_The_Whole_Result.snap @@ -0,0 +1,169 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Error", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "SayHelloOperation" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:851fb754d9ba6b5cc5a55ebcbea2621d" + }, + { + "Key": "graphql.document.body", + "Value": "query SayHelloOperation {\n causeFatalError\n}" + } + ], + "event": [ + { + "Name": "AddedOperationPlanToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:851fb754d9ba6b5cc5a55ebcbea2621d" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:851fb754d9ba6b5cc5a55ebcbea2621d" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:851fb754d9ba6b5cc5a55ebcbea2621d" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "SayHelloOperation" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Error", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "SayHelloOperation" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:851fb754d9ba6b5cc5a55ebcbea2621d" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "SayHelloOperation" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:851fb754d9ba6b5cc5a55ebcbea2621d" + }, + { + "Key": "graphql.operation.step.id", + "Value": "1" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "5f75eb886568e255310bed3eb3e1f7f1c91f1a22f71ac7c36f00d8df27400d8e" + }, + { + "Key": "graphql.source.name", + "Value": "a" + }, + { + "Key": "graphql.source.operation.name", + "Value": "SayHelloOperation_851fb754_1" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:1b35e9142c2e8235f31d2b3ae0de3d2ba54692a7aa6481803c3841fd135f4c4c" + } + ], + "event": [] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.CustomScopes_OnlyValidateAndPlan_LimitsSpans.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.CustomScopes_OnlyValidateAndPlan_LimitsSpans.snap new file mode 100644 index 00000000000..6e5564554ca --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.CustomScopes_OnlyValidateAndPlan_LimitsSpans.snap @@ -0,0 +1,40 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.DefaultScopes_ExcludesExecuteRequestAndParseDocumentSpans.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.DefaultScopes_ExcludesExecuteRequestAndParseDocumentSpans.snap new file mode 100644 index 00000000000..bfbb9fd1ca2 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.DefaultScopes_ExcludesExecuteRequestAndParseDocumentSpans.snap @@ -0,0 +1,88 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.step.id", + "Value": "1" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "456132b93ebaf15a39534753bf72f9f4bfa1152a08d04bc8a88539feec1cb52c" + }, + { + "Key": "graphql.source.name", + "Value": "a" + }, + { + "Key": "graphql.source.operation.name", + "Value": "Op_f7e9989f_1" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:35c1feb1208268226c7d5d5d0ae122e4d38cb79621e862b1e252d37fc83c530a" + } + ], + "event": [] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.DocumentCache_SecondExecution_RecordsCacheHitEvent.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.DocumentCache_SecondExecution_RecordsCacheHitEvent.snap new file mode 100644 index 00000000000..8f8fb30f6f4 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.DocumentCache_SecondExecution_RecordsCacheHitEvent.snap @@ -0,0 +1,97 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [ + { + "Name": "RetrievedDocumentFromCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.step.id", + "Value": "1" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "456132b93ebaf15a39534753bf72f9f4bfa1152a08d04bc8a88539feec1cb52c" + }, + { + "Key": "graphql.source.name", + "Value": "a" + }, + { + "Key": "graphql.source.operation.name", + "Value": "Op_f7e9989f_1" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:35c1feb1208268226c7d5d5d0ae122e4d38cb79621e862b1e252d37fc83c530a" + } + ], + "event": [] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Ensure_That_The_Validation_Activity_Has_An_Error_Status.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Ensure_That_The_Validation_Activity_Has_An_Error_Status.snap new file mode 100644 index 00000000000..5312732e3a8 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Ensure_That_The_Validation_Activity_Has_An_Error_Status.snap @@ -0,0 +1,69 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "GraphQL Operation", + "Status": "Error", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:bb1d246465341a97bdc727d6cd8ead5c" + }, + { + "Key": "graphql.document.body", + "Value": "query SayHelloOperation {\n sayHello_\n}" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:bb1d246465341a97bdc727d6cd8ead5c" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Error", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:bb1d246465341a97bdc727d6cd8ead5c" + } + ], + "event": [ + { + "Name": "exception", + "Tags": [ + { + "Key": "graphql.error.message", + "Value": "The field `sayHello_` does not exist on the type `Query`." + }, + { + "Key": "graphql.error.locations", + "Value": [ + { + "line": 1, + "column": 27 + } + ] + } + ] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.MultipleSources_SourceSchemaResolverError_RecordsDeeplyNestedError.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.MultipleSources_SourceSchemaResolverError_RecordsDeeplyNestedError.snap new file mode 100644 index 00000000000..1f496ab6824 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.MultipleSources_SourceSchemaResolverError_RecordsDeeplyNestedError.snap @@ -0,0 +1,201 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Error", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:241c617bf9ec34fef187d8b149fd8498" + }, + { + "Key": "graphql.document.body", + "Value": "{\n sayHello\n deepB {\n deeperB {\n causeFatalError\n }\n }\n}" + } + ], + "event": [ + { + "Name": "AddedOperationPlanToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:241c617bf9ec34fef187d8b149fd8498" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:241c617bf9ec34fef187d8b149fd8498" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:241c617bf9ec34fef187d8b149fd8498" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Error", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:241c617bf9ec34fef187d8b149fd8498" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:241c617bf9ec34fef187d8b149fd8498" + }, + { + "Key": "graphql.operation.step.id", + "Value": "1" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "6034e1863f163f1cf2ced76832d1f0f496fa230f34e6ea0bb0b1a9de5b0f7db5" + }, + { + "Key": "graphql.source.name", + "Value": "b" + }, + { + "Key": "graphql.source.operation.name", + "Value": "Op_241c617b_1" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:7dfcb1cc48aa6c4d4750dc471f2f71b0c1acf70356383ea909d2c5994dc65d6f" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:241c617bf9ec34fef187d8b149fd8498" + }, + { + "Key": "graphql.operation.step.id", + "Value": "2" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "6034e1863f163f1cf2ced76832d1f0f496fa230f34e6ea0bb0b1a9de5b0f7db5" + }, + { + "Key": "graphql.source.name", + "Value": "a" + }, + { + "Key": "graphql.source.operation.name", + "Value": "Op_241c617b_2" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:23e6ec3292fdaf4949c838051e42d74bee6c27bed8b0720637c4b77d961f3ef3" + } + ], + "event": [] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.ParsingError_InvalidGraphQLDocument_ReportsErrorStatus.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.ParsingError_InvalidGraphQLDocument_ReportsErrorStatus.snap new file mode 100644 index 00000000000..aec02c711c2 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.ParsingError_InvalidGraphQLDocument_ReportsErrorStatus.snap @@ -0,0 +1,48 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "GraphQL Operation", + "Status": "Error", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:bb507ba78696fc3c6ff3a17fb06784d5" + } + ], + "event": [ + { + "Name": "exception", + "Tags": [ + { + "Key": "exception.message", + "Value": "Expected a `RightBrace`-token, but found a `EndOfFile`-token." + }, + { + "Key": "exception.stacktrace", + "Value": "HotChocolate.Language.SyntaxException: Expected a `RightBrace`-token, but found a `EndOfFile`-token.\n at HotChocolate.Language.Utf8GraphQLParser.ParseSelectionSet() in Utf8GraphQLParser.Operations.cs:line 221\n at HotChocolate.Language.Utf8GraphQLParser.ParseShortOperationDefinition() in Utf8GraphQLParser.Operations.cs:line 73\n at HotChocolate.Language.Utf8GraphQLParser.ParseDefinition() in Utf8GraphQLParser.cs:line 215\n at HotChocolate.Language.Utf8GraphQLParser.Parse() in Utf8GraphQLParser.cs:line 98\n at HotChocolate.Language.Utf8GraphQLParser.Parse(String sourceText, ParserOptions options) in Utf8GraphQLParser.cs:line 326\n at HotChocolate.Execution.Pipeline.DocumentParserMiddleware.InvokeAsync(RequestContext context) in DocumentParserMiddleware.cs:line 63" + }, + { + "Key": "exception.type", + "Value": "HotChocolate.Language.SyntaxException" + } + ] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Unset", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:bb507ba78696fc3c6ff3a17fb06784d5" + } + ], + "event": [] + } + ] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.PersistedOperation_LoadsFromStorage_DefaultScopes.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.PersistedOperation_LoadsFromStorage_DefaultScopes.snap new file mode 100644 index 00000000000..2021a373452 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.PersistedOperation_LoadsFromStorage_DefaultScopes.snap @@ -0,0 +1,100 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.document.id", + "Value": "sayHelloOp" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.document.id", + "Value": "sayHelloOp" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.document.id", + "Value": "sayHelloOp" + }, + { + "Key": "graphql.operation.step.id", + "Value": "1" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "456132b93ebaf15a39534753bf72f9f4bfa1152a08d04bc8a88539feec1cb52c" + }, + { + "Key": "graphql.source.name", + "Value": "a" + }, + { + "Key": "graphql.source.operation.name", + "Value": "Op_f7e9989f_1" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:35c1feb1208268226c7d5d5d0ae122e4d38cb79621e862b1e252d37fc83c530a" + } + ], + "event": [] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Source_Schema_Transport_Error.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Source_Schema_Transport_Error.snap new file mode 100644 index 00000000000..9ccdbfeb3ed --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Source_Schema_Transport_Error.snap @@ -0,0 +1,171 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Error", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.document.body", + "Value": "{\n sayHello\n}" + } + ], + "event": [ + { + "Name": "AddedOperationPlanToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Error", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Error", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.step.id", + "Value": "1" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "456132b93ebaf15a39534753bf72f9f4bfa1152a08d04bc8a88539feec1cb52c" + }, + { + "Key": "graphql.source.name", + "Value": "a" + }, + { + "Key": "graphql.source.operation.name", + "Value": "Op_f7e9989f_1" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:35c1feb1208268226c7d5d5d0ae122e4d38cb79621e862b1e252d37fc83c530a" + } + ], + "event": [ + { + "Name": "exception", + "Tags": [ + { + "Key": "exception.message", + "Value": "Response status code does not indicate success: 500 (Internal Server Error)." + }, + { + "Key": "exception.stacktrace", + "Value": "System.Net.Http.HttpRequestException: Response status code does not indicate success: 500 (Internal Server Error).\n at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()\n at HotChocolate.Fusion.Transport.Http.GraphQLHttpResponse.ReadAsResultAsync(CancellationToken cancellationToken) in GraphQLHttpResponse.cs:line 292\n at HotChocolate.Fusion.Execution.Clients.SourceSchemaHttpClient.Response.ReadAsResultStreamAsync(CancellationToken cancellationToken)+MoveNext() in SourceSchemaHttpClient.cs:line 577\n at HotChocolate.Fusion.Execution.Clients.SourceSchemaHttpClient.Response.ReadAsResultStreamAsync(CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource.GetResult()\n at HotChocolate.Fusion.Execution.Nodes.OperationExecutionNode.OnExecuteAsync(OperationPlanContext context, CancellationToken cancellationToken) in OperationExecutionNode.cs:line 159\n at HotChocolate.Fusion.Execution.Nodes.OperationExecutionNode.OnExecuteAsync(OperationPlanContext context, CancellationToken cancellationToken) in OperationExecutionNode.cs:line 159" + }, + { + "Key": "exception.type", + "Value": "System.Net.Http.HttpRequestException" + } + ] + } + ] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.SubscriptionEvent_Records_Subscription_Event_Span.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.SubscriptionEvent_Records_Subscription_Event_Span.snap new file mode 100644 index 00000000000..255ef236318 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.SubscriptionEvent_Records_Subscription_Event_Span.snap @@ -0,0 +1,193 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "subscription", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "subscription" + }, + { + "Key": "graphql.operation.name", + "Value": "OnMessageSubscription" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2" + } + ], + "event": [ + { + "Name": "AddedOperationPlanToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2" + }, + { + "Key": "graphql.operation.type", + "Value": "subscription" + }, + { + "Key": "graphql.operation.name", + "Value": "OnMessageSubscription" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "subscription" + }, + { + "Key": "graphql.operation.name", + "Value": "OnMessageSubscription" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2" + } + ], + "event": [] + } + ] + }, + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "subscription" + }, + { + "Key": "graphql.operation.name", + "Value": "OnMessageSubscription" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2" + }, + { + "Key": "graphql.operation.step.id", + "Value": "1" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "2e566740582971b4b247348976bca6cb8a84255526b80e73273aa94781bca0fc" + }, + { + "Key": "graphql.source.name", + "Value": "a" + }, + { + "Key": "graphql.source.operation.name", + "Value": "OnMessageSubscription_ef859b98_1" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "subscription" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:f9abddbb651ad8e118793eb7c4a7c8db7fe43cae95faba710a0ff35b26b17136" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Subscription Event", + "DisplayName": "GraphQL Subscription Event", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "subscription" + }, + { + "Key": "graphql.operation.name", + "Value": "OnMessageSubscription" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2" + }, + { + "Key": "graphql.subscription.id", + "Value": "1" + } + ], + "event": [] + } + ] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Track_Events_Of_A_Query_With_Multiple_Sources.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Track_Events_Of_A_Query_With_Multiple_Sources.snap new file mode 100644 index 00000000000..5e45bdc3399 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Track_Events_Of_A_Query_With_Multiple_Sources.snap @@ -0,0 +1,197 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:073bf7696c078e52587c88890ef21bbe" + } + ], + "event": [ + { + "Name": "AddedOperationPlanToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:073bf7696c078e52587c88890ef21bbe" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:073bf7696c078e52587c88890ef21bbe" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:073bf7696c078e52587c88890ef21bbe" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:073bf7696c078e52587c88890ef21bbe" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:073bf7696c078e52587c88890ef21bbe" + }, + { + "Key": "graphql.operation.step.id", + "Value": "1" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "9babcd211d7b162261fa15a119462370a3f30c61ea319946c30bc4051a265a5d" + }, + { + "Key": "graphql.source.name", + "Value": "a" + }, + { + "Key": "graphql.source.operation.name", + "Value": "Op_073bf769_1" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:b6db85f78e867baa06bf8b4f45ed381a2b6bc9f7abd15948f0bfff0967fbc308" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:073bf7696c078e52587c88890ef21bbe" + }, + { + "Key": "graphql.operation.step.id", + "Value": "2" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "9babcd211d7b162261fa15a119462370a3f30c61ea319946c30bc4051a265a5d" + }, + { + "Key": "graphql.source.name", + "Value": "b" + }, + { + "Key": "graphql.source.operation.name", + "Value": "Op_073bf769_2" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:1fa50769ad5084334414d5ceb8029c7787ded48ac5cc01dac775b0b814348e88" + } + ], + "event": [] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Track_Events_Of_A_Simple_Query_Default.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Track_Events_Of_A_Simple_Query_Default.snap new file mode 100644 index 00000000000..bfbb9fd1ca2 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Track_Events_Of_A_Simple_Query_Default.snap @@ -0,0 +1,88 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.step.id", + "Value": "1" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "456132b93ebaf15a39534753bf72f9f4bfa1152a08d04bc8a88539feec1cb52c" + }, + { + "Key": "graphql.source.name", + "Value": "a" + }, + { + "Key": "graphql.source.operation.name", + "Value": "Op_f7e9989f_1" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:35c1feb1208268226c7d5d5d0ae122e4d38cb79621e862b1e252d37fc83c530a" + } + ], + "event": [] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.ValidationError_UnknownField_ReportsErrorStatus.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.ValidationError_UnknownField_ReportsErrorStatus.snap new file mode 100644 index 00000000000..469d64a23ed --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.ValidationError_UnknownField_ReportsErrorStatus.snap @@ -0,0 +1,65 @@ +{ + "activities": [ + { + "OperationName": "GraphQL Operation", + "DisplayName": "GraphQL Operation", + "Status": "Error", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:1526f207d516803b71eb8a0db419b57b" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Document Parsing", + "DisplayName": "GraphQL Document Parsing", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.hash", + "Value": "md5:1526f207d516803b71eb8a0db419b57b" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Error", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:1526f207d516803b71eb8a0db419b57b" + } + ], + "event": [ + { + "Name": "exception", + "Tags": [ + { + "Key": "graphql.error.message", + "Value": "The field `unknownField123` does not exist on the type `Query`." + }, + { + "Key": "graphql.error.locations", + "Value": [ + { + "line": 1, + "column": 3 + } + ] + } + ] + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Get_SDL_download.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Get_SDL_Download.snap similarity index 74% rename from src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Get_SDL_download.snap rename to src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Get_SDL_Download.snap index 928bc599863..d77edd7831a 100644 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Get_SDL_download.snap +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Get_SDL_Download.snap @@ -1,10 +1,14 @@ -{ +{ "activities": [ { "OperationName": "ExecuteHttpRequest", "DisplayName": "GraphQL HTTP GET SDL", "Status": "Ok", "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpGetSchema" + }, { "Key": "graphql.schema.name", "Value": "_Default" diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Get_Single_Request.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Get_Single_Request.snap new file mode 100644 index 00000000000..9ff8b608f7b --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Get_Single_Request.snap @@ -0,0 +1,177 @@ +{ + "activities": [ + { + "OperationName": "ExecuteHttpRequest", + "DisplayName": "GraphQL HTTP GET", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpGet" + }, + { + "Key": "graphql.schema.name", + "Value": "_Default" + }, + { + "Key": "graphql.http.request.type", + "Value": "single" + }, + { + "Key": "graphql.http.request.query.hash", + "Value": "f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [], + "activities": [ + { + "OperationName": "Parse HTTP Request", + "DisplayName": "Parse HTTP Request", + "Status": "Ok", + "tags": [], + "event": [] + }, + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [ + { + "Name": "AddedOperationPlanToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.step.id", + "Value": "1" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "456132b93ebaf15a39534753bf72f9f4bfa1152a08d04bc8a88539feec1cb52c" + }, + { + "Key": "graphql.source.name", + "Value": "a" + }, + { + "Key": "graphql.source.operation.name", + "Value": "Op_f7e9989f_1" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:35c1feb1208268226c7d5d5d0ae122e4d38cb79621e862b1e252d37fc83c530a" + } + ], + "event": [] + } + ] + } + ] + }, + { + "OperationName": "Format HTTP Response", + "DisplayName": "Format HTTP Response", + "Status": "Ok", + "tags": [], + "event": [] + } + ] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Post_Add_Variables_To_Http_Activity.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Post_Add_Variables_To_Http_Activity.snap new file mode 100644 index 00000000000..c85efc6ffab --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Post_Add_Variables_To_Http_Activity.snap @@ -0,0 +1,205 @@ +{ + "activities": [ + { + "OperationName": "ExecuteHttpRequest", + "DisplayName": "GraphQL HTTP POST", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, + { + "Key": "graphql.schema.name", + "Value": "_Default" + }, + { + "Key": "graphql.http.request.type", + "Value": "single" + }, + { + "Key": "graphql.http.request.query.id", + "Value": "bfa5986a5299f46421057dd3eb27ec5c" + }, + { + "Key": "graphql.http.request.query.hash", + "Value": "bfa5986a5299f46421057dd3eb27ec5c" + }, + { + "Key": "graphql.http.request.variables", + "Value": "{\"name\":\"World\"}" + } + ], + "event": [], + "activities": [ + { + "OperationName": "Parse HTTP Request", + "DisplayName": "Parse HTTP Request", + "Status": "Ok", + "tags": [], + "event": [] + }, + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + } + ], + "event": [ + { + "Name": "AddedOperationPlanToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Variable Coercion", + "DisplayName": "GraphQL Variable Coercion", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "variable_coercion" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + }, + { + "Key": "graphql.operation.step.id", + "Value": "1" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "d58281f7cf44ca2751c4a435c0249e686bd1c146f6ddae23ed35ec6e4b83eb77" + }, + { + "Key": "graphql.source.name", + "Value": "a" + }, + { + "Key": "graphql.source.operation.name", + "Value": "Op_c46cf8c9_1" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:a6738007b3546a7458414ee647c93aa373bc22ca57256f4a4a5c8ef3aa886470" + } + ], + "event": [] + } + ] + } + ] + }, + { + "OperationName": "Format HTTP Response", + "DisplayName": "Format HTTP Response", + "Status": "Ok", + "tags": [], + "event": [] + } + ] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_Parser_Error.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Post_Parser_Error.snap similarity index 63% rename from src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_Parser_Error.snap rename to src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Post_Parser_Error.snap index 28a52eca1ac..955f4d24f0c 100644 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_Parser_Error.snap +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Post_Parser_Error.snap @@ -5,6 +5,10 @@ "DisplayName": "GraphQL HTTP POST", "Status": "Ok", "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, { "Key": "graphql.schema.name", "Value": "_Default" @@ -13,41 +17,37 @@ "event": [], "activities": [ { - "OperationName": "ParseHttpRequest", + "OperationName": "Parse HTTP Request", "DisplayName": "Parse HTTP Request", "Status": "Error", - "tags": [ - { - "Key": "otel.status_code", - "Value": "ERROR" - } - ], + "tags": [], "event": [ { "Name": "exception", "Tags": [ { - "Key": "exception.message", + "Key": "graphql.error.message", "Value": "Found a NameStart character `d` (100) following a number, which is disallowed." }, { - "Key": "exception.type", + "Key": "graphql.error.code", "Value": "HC0011" }, { - "Key": "graphql.error.location.column", - "Value": 13 - }, - { - "Key": "graphql.error.location.line", - "Value": 4 + "Key": "graphql.error.locations", + "Value": [ + { + "line": 4, + "column": 13 + } + ] } ] } ] }, { - "OperationName": "FormatHttpResponse", + "OperationName": "Format HTTP Response", "DisplayName": "Format HTTP Response", "Status": "Ok", "tags": [], diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Post_Single_Request.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Post_Single_Request.snap new file mode 100644 index 00000000000..879f232de5c --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Post_Single_Request.snap @@ -0,0 +1,181 @@ +{ + "activities": [ + { + "OperationName": "ExecuteHttpRequest", + "DisplayName": "GraphQL HTTP POST", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, + { + "Key": "graphql.schema.name", + "Value": "_Default" + }, + { + "Key": "graphql.http.request.type", + "Value": "single" + }, + { + "Key": "graphql.http.request.query.id", + "Value": "f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.http.request.query.hash", + "Value": "f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [], + "activities": [ + { + "OperationName": "Parse HTTP Request", + "DisplayName": "Parse HTTP Request", + "Status": "Ok", + "tags": [], + "event": [] + }, + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [ + { + "Name": "AddedOperationPlanToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.step.id", + "Value": "1" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "456132b93ebaf15a39534753bf72f9f4bfa1152a08d04bc8a88539feec1cb52c" + }, + { + "Key": "graphql.source.name", + "Value": "a" + }, + { + "Key": "graphql.source.operation.name", + "Value": "Op_f7e9989f_1" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:35c1feb1208268226c7d5d5d0ae122e4d38cb79621e862b1e252d37fc83c530a" + } + ], + "event": [] + } + ] + } + ] + }, + { + "OperationName": "Format HTTP Response", + "DisplayName": "Format HTTP Response", + "Status": "Ok", + "tags": [], + "event": [] + } + ] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Post_Single_Request_Default.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Post_Single_Request_Default.snap new file mode 100644 index 00000000000..4676e5c2059 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Post_Single_Request_Default.snap @@ -0,0 +1,149 @@ +{ + "activities": [ + { + "OperationName": "ExecuteHttpRequest", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, + { + "Key": "graphql.schema.name", + "Value": "_Default" + }, + { + "Key": "graphql.http.request.type", + "Value": "single" + }, + { + "Key": "graphql.http.request.query.id", + "Value": "f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.http.request.query.hash", + "Value": "f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [ + { + "Name": "AddedOperationPlanToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "Parse HTTP Request", + "DisplayName": "Parse HTTP Request", + "Status": "Ok", + "tags": [], + "event": [] + }, + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.step.id", + "Value": "1" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "456132b93ebaf15a39534753bf72f9f4bfa1152a08d04bc8a88539feec1cb52c" + }, + { + "Key": "graphql.source.name", + "Value": "a" + }, + { + "Key": "graphql.source.operation.name", + "Value": "Op_f7e9989f_1" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:35c1feb1208268226c7d5d5d0ae122e4d38cb79621e862b1e252d37fc83c530a" + } + ], + "event": [] + }, + { + "OperationName": "Format HTTP Response", + "DisplayName": "Format HTTP Response", + "Status": "Ok", + "tags": [], + "event": [] + } + ] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Post_Variables_Are_Not_Automatically_Added_To_Activities.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Post_Variables_Are_Not_Automatically_Added_To_Activities.snap new file mode 100644 index 00000000000..5363d19dcde --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Post_Variables_Are_Not_Automatically_Added_To_Activities.snap @@ -0,0 +1,201 @@ +{ + "activities": [ + { + "OperationName": "ExecuteHttpRequest", + "DisplayName": "GraphQL HTTP POST", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, + { + "Key": "graphql.schema.name", + "Value": "_Default" + }, + { + "Key": "graphql.http.request.type", + "Value": "single" + }, + { + "Key": "graphql.http.request.query.id", + "Value": "bfa5986a5299f46421057dd3eb27ec5c" + }, + { + "Key": "graphql.http.request.query.hash", + "Value": "bfa5986a5299f46421057dd3eb27ec5c" + } + ], + "event": [], + "activities": [ + { + "OperationName": "Parse HTTP Request", + "DisplayName": "Parse HTTP Request", + "Status": "Ok", + "tags": [], + "event": [] + }, + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + } + ], + "event": [ + { + "Name": "AddedOperationPlanToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Variable Coercion", + "DisplayName": "GraphQL Variable Coercion", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "variable_coercion" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + }, + { + "Key": "graphql.operation.step.id", + "Value": "1" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "d58281f7cf44ca2751c4a435c0249e686bd1c146f6ddae23ed35ec6e4b83eb77" + }, + { + "Key": "graphql.source.name", + "Value": "a" + }, + { + "Key": "graphql.source.operation.name", + "Value": "Op_c46cf8c9_1" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:a6738007b3546a7458414ee647c93aa373bc22ca57256f4a4a5c8ef3aa886470" + } + ], + "event": [] + } + ] + } + ] + }, + { + "OperationName": "Format HTTP Response", + "DisplayName": "Format HTTP Response", + "Status": "Ok", + "tags": [], + "event": [] + } + ] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Post_With_Extensions_Map.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Post_With_Extensions_Map.snap new file mode 100644 index 00000000000..3fa877086c4 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.Http_Post_With_Extensions_Map.snap @@ -0,0 +1,205 @@ +{ + "activities": [ + { + "OperationName": "ExecuteHttpRequest", + "DisplayName": "GraphQL HTTP POST", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, + { + "Key": "graphql.schema.name", + "Value": "_Default" + }, + { + "Key": "graphql.http.request.type", + "Value": "single" + }, + { + "Key": "graphql.http.request.query.id", + "Value": "bfa5986a5299f46421057dd3eb27ec5c" + }, + { + "Key": "graphql.http.request.query.hash", + "Value": "bfa5986a5299f46421057dd3eb27ec5c" + }, + { + "Key": "graphql.http.request.extensions", + "Value": "{\"test\":\"abc\"}" + } + ], + "event": [], + "activities": [ + { + "OperationName": "Parse HTTP Request", + "DisplayName": "Parse HTTP Request", + "Status": "Ok", + "tags": [], + "event": [] + }, + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + } + ], + "event": [ + { + "Name": "AddedOperationPlanToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Variable Coercion", + "DisplayName": "GraphQL Variable Coercion", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "variable_coercion" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:c46cf8c9811934ddea095f10ee722dc4" + }, + { + "Key": "graphql.operation.step.id", + "Value": "1" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "d58281f7cf44ca2751c4a435c0249e686bd1c146f6ddae23ed35ec6e4b83eb77" + }, + { + "Key": "graphql.source.name", + "Value": "a" + }, + { + "Key": "graphql.source.operation.name", + "Value": "Op_c46cf8c9_1" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:a6738007b3546a7458414ee647c93aa373bc22ca57256f4a4a5c8ef3aa886470" + } + ], + "event": [] + } + ] + } + ] + }, + { + "OperationName": "Format HTTP Response", + "DisplayName": "Format HTTP Response", + "Status": "Ok", + "tags": [], + "event": [] + } + ] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.RequestDetails_All_IncludesAllDetails.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.RequestDetails_All_IncludesAllDetails.snap new file mode 100644 index 00000000000..90f30afb640 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.RequestDetails_All_IncludesAllDetails.snap @@ -0,0 +1,233 @@ +{ + "activities": [ + { + "OperationName": "ExecuteHttpRequest", + "DisplayName": "GraphQL HTTP POST", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, + { + "Key": "graphql.schema.name", + "Value": "_Default" + }, + { + "Key": "graphql.http.request.type", + "Value": "single" + }, + { + "Key": "graphql.http.request.query.id", + "Value": "08c6969c8d553f85d6541aa54ef01acb" + }, + { + "Key": "graphql.http.request.query.hash", + "Value": "08c6969c8d553f85d6541aa54ef01acb" + }, + { + "Key": "graphql.http.request.query.body", + "Value": "query GetGreeting(\n $name: String!\n) {\n greeting(name: $name)\n}" + }, + { + "Key": "graphql.http.request.variables", + "Value": "{\"name\":\"World\"}" + }, + { + "Key": "graphql.http.request.extensions", + "Value": "{\"test\":\"abc\"}" + } + ], + "event": [], + "activities": [ + { + "OperationName": "Parse HTTP Request", + "DisplayName": "Parse HTTP Request", + "Status": "Ok", + "tags": [], + "event": [] + }, + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetGreeting" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:111e40f921c6c6a35bc7eb0e3873630e" + } + ], + "event": [ + { + "Name": "AddedOperationPlanToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:111e40f921c6c6a35bc7eb0e3873630e" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:111e40f921c6c6a35bc7eb0e3873630e" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetGreeting" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Variable Coercion", + "DisplayName": "GraphQL Variable Coercion", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "variable_coercion" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetGreeting" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:111e40f921c6c6a35bc7eb0e3873630e" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetGreeting" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:111e40f921c6c6a35bc7eb0e3873630e" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetGreeting" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:111e40f921c6c6a35bc7eb0e3873630e" + }, + { + "Key": "graphql.operation.step.id", + "Value": "1" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "12136be79ce453a7feac5df83310a20585dbdb9c9675bc55b25c2c2ea1f871e1" + }, + { + "Key": "graphql.source.name", + "Value": "a" + }, + { + "Key": "graphql.source.operation.name", + "Value": "GetGreeting_111e40f9_1" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:00692b6fbeb8014bcc1eb8932d5924dc0c5e5ea3e158e34979ec322c2e1b3c40" + } + ], + "event": [] + } + ] + } + ] + }, + { + "OperationName": "Format HTTP Response", + "DisplayName": "Format HTTP Response", + "Status": "Ok", + "tags": [], + "event": [] + } + ] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.RequestDetails_Default_IncludesIdHashOperationNameExtensions.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.RequestDetails_Default_IncludesIdHashOperationNameExtensions.snap new file mode 100644 index 00000000000..d4b1970c371 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.RequestDetails_Default_IncludesIdHashOperationNameExtensions.snap @@ -0,0 +1,201 @@ +{ + "activities": [ + { + "OperationName": "ExecuteHttpRequest", + "DisplayName": "GraphQL HTTP POST", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, + { + "Key": "graphql.schema.name", + "Value": "_Default" + }, + { + "Key": "graphql.http.request.type", + "Value": "single" + }, + { + "Key": "graphql.http.request.query.id", + "Value": "82d594dc9843e825769c127e8ae2db6f" + }, + { + "Key": "graphql.http.request.query.hash", + "Value": "82d594dc9843e825769c127e8ae2db6f" + }, + { + "Key": "graphql.http.request.extensions", + "Value": "{\"test\":\"abc\"}" + } + ], + "event": [], + "activities": [ + { + "OperationName": "Parse HTTP Request", + "DisplayName": "Parse HTTP Request", + "Status": "Ok", + "tags": [], + "event": [] + }, + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetGreeting" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:cf203f646caf64f424638bd2f09e490a" + } + ], + "event": [ + { + "Name": "AddedOperationPlanToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:cf203f646caf64f424638bd2f09e490a" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:cf203f646caf64f424638bd2f09e490a" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetGreeting" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetGreeting" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:cf203f646caf64f424638bd2f09e490a" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetGreeting" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:cf203f646caf64f424638bd2f09e490a" + }, + { + "Key": "graphql.operation.step.id", + "Value": "1" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "f274a33c8d687ab897e52e6be6346ef3ccdd10a864a4088c571073d755df2d92" + }, + { + "Key": "graphql.source.name", + "Value": "a" + }, + { + "Key": "graphql.source.operation.name", + "Value": "GetGreeting_cf203f64_1" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:579285518f37f039b04a0268fd6058a3903e369efccd77c48e7c045c35f25428" + } + ], + "event": [] + } + ] + } + ] + }, + { + "OperationName": "Format HTTP Response", + "DisplayName": "Format HTTP Response", + "Status": "Ok", + "tags": [], + "event": [] + } + ] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.RequestDetails_DocumentOnly_IncludesDocumentTag.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.RequestDetails_DocumentOnly_IncludesDocumentTag.snap new file mode 100644 index 00000000000..b222a1e8ef9 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.RequestDetails_DocumentOnly_IncludesDocumentTag.snap @@ -0,0 +1,177 @@ +{ + "activities": [ + { + "OperationName": "ExecuteHttpRequest", + "DisplayName": "GraphQL HTTP POST", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, + { + "Key": "graphql.schema.name", + "Value": "_Default" + }, + { + "Key": "graphql.http.request.type", + "Value": "single" + }, + { + "Key": "graphql.http.request.query.body", + "Value": "{\n sayHello\n}" + } + ], + "event": [], + "activities": [ + { + "OperationName": "Parse HTTP Request", + "DisplayName": "Parse HTTP Request", + "Status": "Ok", + "tags": [], + "event": [] + }, + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [ + { + "Name": "AddedOperationPlanToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:f7e9989fbb67af7fa747a9983313c9e5" + }, + { + "Key": "graphql.operation.step.id", + "Value": "1" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "456132b93ebaf15a39534753bf72f9f4bfa1152a08d04bc8a88539feec1cb52c" + }, + { + "Key": "graphql.source.name", + "Value": "a" + }, + { + "Key": "graphql.source.operation.name", + "Value": "Op_f7e9989f_1" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:35c1feb1208268226c7d5d5d0ae122e4d38cb79621e862b1e252d37fc83c530a" + } + ], + "event": [] + } + ] + } + ] + }, + { + "OperationName": "Format HTTP Response", + "DisplayName": "Format HTTP Response", + "Status": "Ok", + "tags": [], + "event": [] + } + ] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.RequestDetails_None_ExcludesAllDetails.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.RequestDetails_None_ExcludesAllDetails.snap new file mode 100644 index 00000000000..271d5d3e822 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.RequestDetails_None_ExcludesAllDetails.snap @@ -0,0 +1,209 @@ +{ + "activities": [ + { + "OperationName": "ExecuteHttpRequest", + "DisplayName": "GraphQL HTTP POST", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.http.kind", + "Value": "HttpPost" + }, + { + "Key": "graphql.schema.name", + "Value": "_Default" + } + ], + "event": [], + "activities": [ + { + "OperationName": "Parse HTTP Request", + "DisplayName": "Parse HTTP Request", + "Status": "Ok", + "tags": [], + "event": [] + }, + { + "OperationName": "GraphQL Operation", + "DisplayName": "query", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetGreeting" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:111e40f921c6c6a35bc7eb0e3873630e" + } + ], + "event": [ + { + "Name": "AddedOperationPlanToCache", + "Tags": [] + }, + { + "Name": "AddedDocumentToCache", + "Tags": [] + } + ], + "activities": [ + { + "OperationName": "GraphQL Document Validation", + "DisplayName": "GraphQL Document Validation", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "validate" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:111e40f921c6c6a35bc7eb0e3873630e" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Planning", + "DisplayName": "GraphQL Operation Planning", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "plan" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:111e40f921c6c6a35bc7eb0e3873630e" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetGreeting" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Variable Coercion", + "DisplayName": "GraphQL Variable Coercion", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "variable_coercion" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetGreeting" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:111e40f921c6c6a35bc7eb0e3873630e" + } + ], + "event": [] + }, + { + "OperationName": "GraphQL Operation Execution", + "DisplayName": "GraphQL Operation Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetGreeting" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:111e40f921c6c6a35bc7eb0e3873630e" + } + ], + "event": [], + "activities": [ + { + "OperationName": "GraphQL Step Execution", + "DisplayName": "GraphQL Step Execution", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.processing.type", + "Value": "step_execute" + }, + { + "Key": "graphql.operation.type", + "Value": "query" + }, + { + "Key": "graphql.operation.name", + "Value": "GetGreeting" + }, + { + "Key": "graphql.document.hash", + "Value": "md5:111e40f921c6c6a35bc7eb0e3873630e" + }, + { + "Key": "graphql.operation.step.id", + "Value": "1" + }, + { + "Key": "graphql.operation.step.kind", + "Value": "operation" + }, + { + "Key": "graphql.operation.step.plan.id", + "Value": "12136be79ce453a7feac5df83310a20585dbdb9c9675bc55b25c2c2ea1f871e1" + }, + { + "Key": "graphql.source.name", + "Value": "a" + }, + { + "Key": "graphql.source.operation.name", + "Value": "GetGreeting_111e40f9_1" + }, + { + "Key": "graphql.source.operation.kind", + "Value": "query" + }, + { + "Key": "graphql.source.operation.hash", + "Value": "sha256:00692b6fbeb8014bcc1eb8932d5924dc0c5e5ea3e158e34979ec322c2e1b3c40" + } + ], + "event": [] + } + ] + } + ] + }, + { + "OperationName": "Format HTTP Response", + "DisplayName": "Format HTTP Response", + "Status": "Ok", + "tags": [], + "event": [] + } + ] + } + ] +} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Allow_Document_To_Be_Captured.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Allow_Document_To_Be_Captured.snap deleted file mode 100644 index d3e100c155c..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Allow_Document_To_Be_Captured.snap +++ /dev/null @@ -1,112 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "query SayHelloOperation { sayHello }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "6af18618ae20c266f6ffc352b78cb69b" - }, - { - "Key": "graphql.document.hash", - "Value": "6af18618ae20c266f6ffc352b78cb69b" - }, - { - "Key": "graphql.operation.id", - "Value": "1334fb0da1250c6db5db84b6c98ccb2556f066942f8836d6ebd18fd870172787" - }, - { - "Key": "graphql.operation.name", - "Value": "SayHelloOperation" - }, - { - "Key": "graphql.document.body", - "Value": "query SayHelloOperation {\n sayHello\n}" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "6af18618ae20c266f6ffc352b78cb69b" - }, - { - "Key": "graphql.document.hash", - "Value": "6af18618ae20c266f6ffc352b78cb69b" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "PlanOperation", - "DisplayName": "Plan Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation SayHelloOperation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ExecuteOperationNode", - "DisplayName": "Execute Operation Node (a)", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.fusion.node.type", - "Value": "Operation" - }, - { - "Key": "graphql.fusion.node.schema", - "Value": "a" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Cause_A_Resolver_Error_That_Deletes_The_Whole_Result.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Cause_A_Resolver_Error_That_Deletes_The_Whole_Result.snap deleted file mode 100644 index 86e0de8de0f..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Cause_A_Resolver_Error_That_Deletes_The_Whole_Result.snap +++ /dev/null @@ -1,116 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "query SayHelloOperation { causeFatalError }", - "Status": "Error", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "851fb754d9ba6b5cc5a55ebcbea2621d" - }, - { - "Key": "graphql.document.hash", - "Value": "851fb754d9ba6b5cc5a55ebcbea2621d" - }, - { - "Key": "graphql.operation.id", - "Value": "5f75eb886568e255310bed3eb3e1f7f1c91f1a22f71ac7c36f00d8df27400d8e" - }, - { - "Key": "graphql.operation.name", - "Value": "SayHelloOperation" - }, - { - "Key": "graphql.document.body", - "Value": "query SayHelloOperation {\n causeFatalError\n}" - }, - { - "Key": "otel.status_code", - "Value": "ERROR" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "851fb754d9ba6b5cc5a55ebcbea2621d" - }, - { - "Key": "graphql.document.hash", - "Value": "851fb754d9ba6b5cc5a55ebcbea2621d" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "PlanOperation", - "DisplayName": "Plan Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation SayHelloOperation", - "Status": "Error", - "tags": [ - { - "Key": "otel.status_code", - "Value": "ERROR" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ExecuteOperationNode", - "DisplayName": "Execute Operation Node (a)", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.fusion.node.type", - "Value": "Operation" - }, - { - "Key": "graphql.fusion.node.schema", - "Value": "a" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Cause_A_Resolver_Error_That_Deletes_The_Whole_Result_Deep.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Cause_A_Resolver_Error_That_Deletes_The_Whole_Result_Deep.snap deleted file mode 100644 index 158121c23ce..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Cause_A_Resolver_Error_That_Deletes_The_Whole_Result_Deep.snap +++ /dev/null @@ -1,84 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "Execute Request", - "Status": "Error", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "803df9346db185e9dc0b22dd3909aa70" - }, - { - "Key": "graphql.document.hash", - "Value": "803df9346db185e9dc0b22dd3909aa70" - }, - { - "Key": "graphql.document.body", - "Value": "query SayHelloOperation {\n deep {\n deeper {\n deeps {\n deeper {\n causeFatalError\n }\n }\n }\n }\n}" - }, - { - "Key": "otel.status_code", - "Value": "ERROR" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Error", - "tags": [ - { - "Key": "otel.status_code", - "Value": "ERROR" - }, - { - "Key": "graphql.document.id", - "Value": "803df9346db185e9dc0b22dd3909aa70" - }, - { - "Key": "graphql.document.hash", - "Value": "803df9346db185e9dc0b22dd3909aa70" - } - ], - "event": [ - { - "Name": "exception", - "Tags": [ - { - "Key": "exception.message", - "Value": "The field `causeFatalError` does not exist on the type `Deeper`." - }, - { - "Key": "exception.type", - "Value": "GRAPHQL_ERROR" - }, - { - "Key": "graphql.error.location.column", - "Value": 21 - }, - { - "Key": "graphql.error.location.line", - "Value": 6 - } - ] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_Operation_Display_Name_With_1_Field.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_Operation_Display_Name_With_1_Field.snap deleted file mode 100644 index 85a94d3f7b2..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_Operation_Display_Name_With_1_Field.snap +++ /dev/null @@ -1,104 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "query { a }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "452ea802c4d1bf2a81a7411b0b361d9f" - }, - { - "Key": "graphql.document.hash", - "Value": "452ea802c4d1bf2a81a7411b0b361d9f" - }, - { - "Key": "graphql.operation.id", - "Value": "91d3f369067488892e5c81c27598c0d43b5ecfe5ad824925965ac60c70351919" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "452ea802c4d1bf2a81a7411b0b361d9f" - }, - { - "Key": "graphql.document.hash", - "Value": "452ea802c4d1bf2a81a7411b0b361d9f" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "PlanOperation", - "DisplayName": "Plan Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ExecuteOperationNode", - "DisplayName": "Execute Operation Node (a)", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.fusion.node.type", - "Value": "Operation" - }, - { - "Key": "graphql.fusion.node.schema", - "Value": "a" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_Operation_Display_Name_With_1_Field_And_Op.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_Operation_Display_Name_With_1_Field_And_Op.snap deleted file mode 100644 index 0ed0deda928..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_Operation_Display_Name_With_1_Field_And_Op.snap +++ /dev/null @@ -1,108 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "query GetA { a }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "cee0e2939ece72d650cb0331f4be4669" - }, - { - "Key": "graphql.document.hash", - "Value": "cee0e2939ece72d650cb0331f4be4669" - }, - { - "Key": "graphql.operation.id", - "Value": "155189958e55686347a7f921c0f7a1ef143f829f5b116365a297651606d5703f" - }, - { - "Key": "graphql.operation.name", - "Value": "GetA" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "cee0e2939ece72d650cb0331f4be4669" - }, - { - "Key": "graphql.document.hash", - "Value": "cee0e2939ece72d650cb0331f4be4669" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "PlanOperation", - "DisplayName": "Plan Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation GetA", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ExecuteOperationNode", - "DisplayName": "Execute Operation Node (a)", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.fusion.node.type", - "Value": "Operation" - }, - { - "Key": "graphql.fusion.node.schema", - "Value": "a" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_Operation_Display_Name_With_3_Field.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_Operation_Display_Name_With_3_Field.snap deleted file mode 100644 index f4870f0ca44..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_Operation_Display_Name_With_3_Field.snap +++ /dev/null @@ -1,104 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "query { a b c }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "2e55fbe10a9e3ddf26935a8f8d15ec89" - }, - { - "Key": "graphql.document.hash", - "Value": "2e55fbe10a9e3ddf26935a8f8d15ec89" - }, - { - "Key": "graphql.operation.id", - "Value": "8b26a2633c9b68833461d6b2249f54600493dfc632399469d5c108e79410ca7b" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "2e55fbe10a9e3ddf26935a8f8d15ec89" - }, - { - "Key": "graphql.document.hash", - "Value": "2e55fbe10a9e3ddf26935a8f8d15ec89" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "PlanOperation", - "DisplayName": "Plan Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ExecuteOperationNode", - "DisplayName": "Execute Operation Node (a)", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.fusion.node.type", - "Value": "Operation" - }, - { - "Key": "graphql.fusion.node.schema", - "Value": "a" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_Operation_Display_Name_With_4_Field.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_Operation_Display_Name_With_4_Field.snap deleted file mode 100644 index 1af065e2ac9..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Create_Operation_Display_Name_With_4_Field.snap +++ /dev/null @@ -1,104 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "query { a b c ... }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "a5f924bb2f5f8651014e92e1cc2428c7" - }, - { - "Key": "graphql.document.hash", - "Value": "a5f924bb2f5f8651014e92e1cc2428c7" - }, - { - "Key": "graphql.operation.id", - "Value": "1c020b5562fde1e7673b1f4750bdf6d35f2789819d03bb6b5d088bf445231501" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "a5f924bb2f5f8651014e92e1cc2428c7" - }, - { - "Key": "graphql.document.hash", - "Value": "a5f924bb2f5f8651014e92e1cc2428c7" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "PlanOperation", - "DisplayName": "Plan Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ExecuteOperationNode", - "DisplayName": "Execute Operation Node (a)", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.fusion.node.type", - "Value": "Operation" - }, - { - "Key": "graphql.fusion.node.schema", - "Value": "a" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Ensure_Operation_Name_Is_Used_As_Request_Name.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Ensure_Operation_Name_Is_Used_As_Request_Name.snap deleted file mode 100644 index b58f55ac83f..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Ensure_Operation_Name_Is_Used_As_Request_Name.snap +++ /dev/null @@ -1,108 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "query SayHelloOperation { sayHello }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "6af18618ae20c266f6ffc352b78cb69b" - }, - { - "Key": "graphql.document.hash", - "Value": "6af18618ae20c266f6ffc352b78cb69b" - }, - { - "Key": "graphql.operation.id", - "Value": "1334fb0da1250c6db5db84b6c98ccb2556f066942f8836d6ebd18fd870172787" - }, - { - "Key": "graphql.operation.name", - "Value": "SayHelloOperation" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "6af18618ae20c266f6ffc352b78cb69b" - }, - { - "Key": "graphql.document.hash", - "Value": "6af18618ae20c266f6ffc352b78cb69b" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "PlanOperation", - "DisplayName": "Plan Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation SayHelloOperation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ExecuteOperationNode", - "DisplayName": "Execute Operation Node (a)", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.fusion.node.type", - "Value": "Operation" - }, - { - "Key": "graphql.fusion.node.schema", - "Value": "a" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Ensure_That_The_Validation_Activity_Has_An_Error_Status.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Ensure_That_The_Validation_Activity_Has_An_Error_Status.snap deleted file mode 100644 index 725e9a44a05..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Ensure_That_The_Validation_Activity_Has_An_Error_Status.snap +++ /dev/null @@ -1,84 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "Execute Request", - "Status": "Error", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "bb1d246465341a97bdc727d6cd8ead5c" - }, - { - "Key": "graphql.document.hash", - "Value": "bb1d246465341a97bdc727d6cd8ead5c" - }, - { - "Key": "graphql.document.body", - "Value": "query SayHelloOperation {\n sayHello_\n}" - }, - { - "Key": "otel.status_code", - "Value": "ERROR" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Error", - "tags": [ - { - "Key": "otel.status_code", - "Value": "ERROR" - }, - { - "Key": "graphql.document.id", - "Value": "bb1d246465341a97bdc727d6cd8ead5c" - }, - { - "Key": "graphql.document.hash", - "Value": "bb1d246465341a97bdc727d6cd8ead5c" - } - ], - "event": [ - { - "Name": "exception", - "Tags": [ - { - "Key": "exception.message", - "Value": "The field `sayHello_` does not exist on the type `Query`." - }, - { - "Key": "exception.type", - "Value": "GRAPHQL_ERROR" - }, - { - "Key": "graphql.error.location.column", - "Value": 27 - }, - { - "Key": "graphql.error.location.line", - "Value": 1 - } - ] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Source_Schema_Transport_Error.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Source_Schema_Transport_Error.snap deleted file mode 100644 index 4a93931c9fa..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Source_Schema_Transport_Error.snap +++ /dev/null @@ -1,130 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "query { sayHello }", - "Status": "Error", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.document.hash", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.operation.id", - "Value": "456132b93ebaf15a39534753bf72f9f4bfa1152a08d04bc8a88539feec1cb52c" - }, - { - "Key": "graphql.document.body", - "Value": "{\n sayHello\n}" - }, - { - "Key": "otel.status_code", - "Value": "ERROR" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.document.hash", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "PlanOperation", - "DisplayName": "Plan Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", - "Status": "Error", - "tags": [ - { - "Key": "otel.status_code", - "Value": "ERROR" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ExecuteOperationNode", - "DisplayName": "Execute Operation Node (a)", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.fusion.node.type", - "Value": "Operation" - }, - { - "Key": "graphql.fusion.node.schema", - "Value": "a" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [ - { - "Name": "exception", - "Tags": [ - { - "Key": "exception.type", - "Value": "System.Net.Http.HttpRequestException" - }, - { - "Key": "exception.stacktrace", - "Value": "System.Net.Http.HttpRequestException: Response status code does not indicate success: 500 (Internal Server Error).\n at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()\n at HotChocolate.Fusion.Transport.Http.GraphQLHttpResponse.ReadAsResultAsync(CancellationToken cancellationToken) in GraphQLHttpResponse.cs:line 292\n at HotChocolate.Fusion.Execution.Clients.SourceSchemaHttpClient.Response.ReadAsResultStreamAsync(CancellationToken cancellationToken)+MoveNext() in SourceSchemaHttpClient.cs:line 577\n at HotChocolate.Fusion.Execution.Clients.SourceSchemaHttpClient.Response.ReadAsResultStreamAsync(CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource.GetResult()\n at HotChocolate.Fusion.Execution.Nodes.OperationExecutionNode.OnExecuteAsync(OperationPlanContext context, CancellationToken cancellationToken) in OperationExecutionNode.cs:line 159\n at HotChocolate.Fusion.Execution.Nodes.OperationExecutionNode.OnExecuteAsync(OperationPlanContext context, CancellationToken cancellationToken) in OperationExecutionNode.cs:line 159" - }, - { - "Key": "exception.message", - "Value": "Response status code does not indicate success: 500 (Internal Server Error)." - } - ] - } - ] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_Events_Of_A_Query_With_Multiple_Sources.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_Events_Of_A_Query_With_Multiple_Sources.snap deleted file mode 100644 index fa4b3838f11..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_Events_Of_A_Query_With_Multiple_Sources.snap +++ /dev/null @@ -1,124 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "query { sayHello sayGoodbye }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "073bf7696c078e52587c88890ef21bbe" - }, - { - "Key": "graphql.document.hash", - "Value": "073bf7696c078e52587c88890ef21bbe" - }, - { - "Key": "graphql.operation.id", - "Value": "9babcd211d7b162261fa15a119462370a3f30c61ea319946c30bc4051a265a5d" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "073bf7696c078e52587c88890ef21bbe" - }, - { - "Key": "graphql.document.hash", - "Value": "073bf7696c078e52587c88890ef21bbe" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "PlanOperation", - "DisplayName": "Plan Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ExecuteOperationNode", - "DisplayName": "Execute Operation Node (a)", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.fusion.node.type", - "Value": "Operation" - }, - { - "Key": "graphql.fusion.node.schema", - "Value": "a" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperationNode", - "DisplayName": "Execute Operation Node (b)", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.fusion.node.type", - "Value": "Operation" - }, - { - "Key": "graphql.fusion.node.schema", - "Value": "b" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_Events_Of_A_Simple_Query_Default.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_Events_Of_A_Simple_Query_Default.snap deleted file mode 100644 index 5ecaa32bc36..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_Events_Of_A_Simple_Query_Default.snap +++ /dev/null @@ -1,56 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.document.hash", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "PlanOperation", - "DisplayName": "Plan Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperationNode", - "DisplayName": "Execute Operation Node (a)", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.fusion.node.type", - "Value": "Operation" - }, - { - "Key": "graphql.fusion.node.schema", - "Value": "a" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_Events_Of_A_Simple_Query_Detailed.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_Events_Of_A_Simple_Query_Detailed.snap deleted file mode 100644 index c2b6eb5d039..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_Events_Of_A_Simple_Query_Detailed.snap +++ /dev/null @@ -1,104 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "query { sayHello }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.document.hash", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.operation.id", - "Value": "456132b93ebaf15a39534753bf72f9f4bfa1152a08d04bc8a88539feec1cb52c" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.document.hash", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "PlanOperation", - "DisplayName": "Plan Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ExecuteOperationNode", - "DisplayName": "Execute Operation Node (a)", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.fusion.node.type", - "Value": "Operation" - }, - { - "Key": "graphql.fusion.node.schema", - "Value": "a" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_Events_Of_A_Simple_Query_With_Node_Scopes.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_Events_Of_A_Simple_Query_With_Node_Scopes.snap deleted file mode 100644 index c2b6eb5d039..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_Events_Of_A_Simple_Query_With_Node_Scopes.snap +++ /dev/null @@ -1,104 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteRequest", - "DisplayName": "query { sayHello }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.document.hash", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.operation.id", - "Value": "456132b93ebaf15a39534753bf72f9f4bfa1152a08d04bc8a88539feec1cb52c" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseDocument", - "DisplayName": "Parse Document", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.document.hash", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "PlanOperation", - "DisplayName": "Plan Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ExecuteOperationNode", - "DisplayName": "Execute Operation Node (a)", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.fusion.node.type", - "Value": "Operation" - }, - { - "Key": "graphql.fusion.node.schema", - "Value": "a" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Get_Single_Request.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Get_Single_Request.snap deleted file mode 100644 index bb203ba5752..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Get_Single_Request.snap +++ /dev/null @@ -1,138 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteHttpRequest", - "DisplayName": "GraphQL HTTP GET", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.schema.name", - "Value": "_Default" - }, - { - "Key": "graphql.http.request.type", - "Value": "single" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseHttpRequest", - "DisplayName": "Parse HTTP Request", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteRequest", - "DisplayName": "query { sayHello }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.document.hash", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.operation.id", - "Value": "456132b93ebaf15a39534753bf72f9f4bfa1152a08d04bc8a88539feec1cb52c" - } - ], - "event": [ - { - "Name": "AddedOperationPlanToCache", - "Tags": [] - }, - { - "Name": "AddedDocumentToCache", - "Tags": [] - } - ], - "activities": [ - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.document.hash", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "PlanOperation", - "DisplayName": "Plan Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ExecuteOperationNode", - "DisplayName": "Execute Operation Node (a)", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.fusion.node.type", - "Value": "Operation" - }, - { - "Key": "graphql.fusion.node.schema", - "Value": "a" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - }, - { - "OperationName": "FormatHttpResponse", - "DisplayName": "Format HTTP Response", - "Status": "Ok", - "tags": [], - "event": [] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_Add_Query_To_Http_Activity.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_Add_Query_To_Http_Activity.snap deleted file mode 100644 index 1f787a37f1b..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_Add_Query_To_Http_Activity.snap +++ /dev/null @@ -1,150 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteHttpRequest", - "DisplayName": "GraphQL HTTP POST", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.schema.name", - "Value": "_Default" - }, - { - "Key": "graphql.http.request.type", - "Value": "single" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseHttpRequest", - "DisplayName": "Parse HTTP Request", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteRequest", - "DisplayName": "query { greeting }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "bfa5986a5299f46421057dd3eb27ec5c" - }, - { - "Key": "graphql.document.hash", - "Value": "c46cf8c9811934ddea095f10ee722dc4" - }, - { - "Key": "graphql.operation.id", - "Value": "d58281f7cf44ca2751c4a435c0249e686bd1c146f6ddae23ed35ec6e4b83eb77" - } - ], - "event": [ - { - "Name": "AddedOperationPlanToCache", - "Tags": [] - }, - { - "Name": "AddedDocumentToCache", - "Tags": [] - } - ], - "activities": [ - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "bfa5986a5299f46421057dd3eb27ec5c" - }, - { - "Key": "graphql.document.hash", - "Value": "c46cf8c9811934ddea095f10ee722dc4" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "PlanOperation", - "DisplayName": "Plan Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "CoerceVariables", - "DisplayName": "Coerce Variable", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ExecuteOperationNode", - "DisplayName": "Execute Operation Node (a)", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.fusion.node.type", - "Value": "Operation" - }, - { - "Key": "graphql.fusion.node.schema", - "Value": "a" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - }, - { - "OperationName": "FormatHttpResponse", - "DisplayName": "Format HTTP Response", - "Status": "Ok", - "tags": [], - "event": [] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_Add_Variables_To_Http_Activity.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_Add_Variables_To_Http_Activity.snap deleted file mode 100644 index 46c0ade8f33..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_Add_Variables_To_Http_Activity.snap +++ /dev/null @@ -1,154 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteHttpRequest", - "DisplayName": "GraphQL HTTP POST", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.schema.name", - "Value": "_Default" - }, - { - "Key": "graphql.http.request.type", - "Value": "single" - }, - { - "Key": "graphql.http.request.variables", - "Value": "{\"name\":\"World\"}" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseHttpRequest", - "DisplayName": "Parse HTTP Request", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteRequest", - "DisplayName": "query { greeting }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "bfa5986a5299f46421057dd3eb27ec5c" - }, - { - "Key": "graphql.document.hash", - "Value": "c46cf8c9811934ddea095f10ee722dc4" - }, - { - "Key": "graphql.operation.id", - "Value": "d58281f7cf44ca2751c4a435c0249e686bd1c146f6ddae23ed35ec6e4b83eb77" - } - ], - "event": [ - { - "Name": "AddedOperationPlanToCache", - "Tags": [] - }, - { - "Name": "AddedDocumentToCache", - "Tags": [] - } - ], - "activities": [ - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "bfa5986a5299f46421057dd3eb27ec5c" - }, - { - "Key": "graphql.document.hash", - "Value": "c46cf8c9811934ddea095f10ee722dc4" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "PlanOperation", - "DisplayName": "Plan Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "CoerceVariables", - "DisplayName": "Coerce Variable", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ExecuteOperationNode", - "DisplayName": "Execute Operation Node (a)", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.fusion.node.type", - "Value": "Operation" - }, - { - "Key": "graphql.fusion.node.schema", - "Value": "a" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - }, - { - "OperationName": "FormatHttpResponse", - "DisplayName": "Format HTTP Response", - "Status": "Ok", - "tags": [], - "event": [] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_Single_Request.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_Single_Request.snap deleted file mode 100644 index 268c3ad7374..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_Single_Request.snap +++ /dev/null @@ -1,138 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteHttpRequest", - "DisplayName": "GraphQL HTTP POST", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.schema.name", - "Value": "_Default" - }, - { - "Key": "graphql.http.request.type", - "Value": "single" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseHttpRequest", - "DisplayName": "Parse HTTP Request", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteRequest", - "DisplayName": "query { sayHello }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.document.hash", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.operation.id", - "Value": "456132b93ebaf15a39534753bf72f9f4bfa1152a08d04bc8a88539feec1cb52c" - } - ], - "event": [ - { - "Name": "AddedOperationPlanToCache", - "Tags": [] - }, - { - "Name": "AddedDocumentToCache", - "Tags": [] - } - ], - "activities": [ - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.document.hash", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "PlanOperation", - "DisplayName": "Plan Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ExecuteOperationNode", - "DisplayName": "Execute Operation Node (a)", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.fusion.node.type", - "Value": "Operation" - }, - { - "Key": "graphql.fusion.node.schema", - "Value": "a" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - }, - { - "OperationName": "FormatHttpResponse", - "DisplayName": "Format HTTP Response", - "Status": "Ok", - "tags": [], - "event": [] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_Single_Request_Default.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_Single_Request_Default.snap deleted file mode 100644 index 86acae93e01..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_Single_Request_Default.snap +++ /dev/null @@ -1,114 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteHttpRequest", - "DisplayName": "query { sayHello }", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.schema.name", - "Value": "_Default" - }, - { - "Key": "graphql.http.request.type", - "Value": "single" - }, - { - "Key": "graphql.document.id", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.document.hash", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.operation.id", - "Value": "456132b93ebaf15a39534753bf72f9f4bfa1152a08d04bc8a88539feec1cb52c" - } - ], - "event": [ - { - "Name": "AddedOperationPlanToCache", - "Tags": [] - }, - { - "Name": "AddedDocumentToCache", - "Tags": [] - } - ], - "activities": [ - { - "OperationName": "ParseHttpRequest", - "DisplayName": "Parse HTTP Request", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "graphql.document.hash", - "Value": "f7e9989fbb67af7fa747a9983313c9e5" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "PlanOperation", - "DisplayName": "Plan Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperationNode", - "DisplayName": "Execute Operation Node (a)", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.fusion.node.type", - "Value": "Operation" - }, - { - "Key": "graphql.fusion.node.schema", - "Value": "a" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "FormatHttpResponse", - "DisplayName": "Format HTTP Response", - "Status": "Ok", - "tags": [], - "event": [] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_Variables_Are_Not_Automatically_Added_To_Activities.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_Variables_Are_Not_Automatically_Added_To_Activities.snap deleted file mode 100644 index 1f787a37f1b..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_Variables_Are_Not_Automatically_Added_To_Activities.snap +++ /dev/null @@ -1,150 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteHttpRequest", - "DisplayName": "GraphQL HTTP POST", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.schema.name", - "Value": "_Default" - }, - { - "Key": "graphql.http.request.type", - "Value": "single" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseHttpRequest", - "DisplayName": "Parse HTTP Request", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteRequest", - "DisplayName": "query { greeting }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "bfa5986a5299f46421057dd3eb27ec5c" - }, - { - "Key": "graphql.document.hash", - "Value": "c46cf8c9811934ddea095f10ee722dc4" - }, - { - "Key": "graphql.operation.id", - "Value": "d58281f7cf44ca2751c4a435c0249e686bd1c146f6ddae23ed35ec6e4b83eb77" - } - ], - "event": [ - { - "Name": "AddedOperationPlanToCache", - "Tags": [] - }, - { - "Name": "AddedDocumentToCache", - "Tags": [] - } - ], - "activities": [ - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "bfa5986a5299f46421057dd3eb27ec5c" - }, - { - "Key": "graphql.document.hash", - "Value": "c46cf8c9811934ddea095f10ee722dc4" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "PlanOperation", - "DisplayName": "Plan Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "CoerceVariables", - "DisplayName": "Coerce Variable", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ExecuteOperationNode", - "DisplayName": "Execute Operation Node (a)", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.fusion.node.type", - "Value": "Operation" - }, - { - "Key": "graphql.fusion.node.schema", - "Value": "a" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - }, - { - "OperationName": "FormatHttpResponse", - "DisplayName": "Format HTTP Response", - "Status": "Ok", - "tags": [], - "event": [] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_With_Extensions_Map.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_With_Extensions_Map.snap deleted file mode 100644 index 2baf19b2df6..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_With_Extensions_Map.snap +++ /dev/null @@ -1,154 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteHttpRequest", - "DisplayName": "GraphQL HTTP POST", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.schema.name", - "Value": "_Default" - }, - { - "Key": "graphql.http.request.type", - "Value": "single" - }, - { - "Key": "graphql.http.request.extensions", - "Value": "{\"test\":\"abc\"}" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseHttpRequest", - "DisplayName": "Parse HTTP Request", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteRequest", - "DisplayName": "query { greeting }", - "Status": "Unset", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "bfa5986a5299f46421057dd3eb27ec5c" - }, - { - "Key": "graphql.document.hash", - "Value": "c46cf8c9811934ddea095f10ee722dc4" - }, - { - "Key": "graphql.operation.id", - "Value": "d58281f7cf44ca2751c4a435c0249e686bd1c146f6ddae23ed35ec6e4b83eb77" - } - ], - "event": [ - { - "Name": "AddedOperationPlanToCache", - "Tags": [] - }, - { - "Name": "AddedDocumentToCache", - "Tags": [] - } - ], - "activities": [ - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "bfa5986a5299f46421057dd3eb27ec5c" - }, - { - "Key": "graphql.document.hash", - "Value": "c46cf8c9811934ddea095f10ee722dc4" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "PlanOperation", - "DisplayName": "Plan Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "CoerceVariables", - "DisplayName": "Coerce Variable", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteOperation", - "DisplayName": "Execute Operation", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ExecuteOperationNode", - "DisplayName": "Execute Operation Node (a)", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.fusion.node.type", - "Value": "Operation" - }, - { - "Key": "graphql.fusion.node.schema", - "Value": "a" - }, - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - } - ] - } - ] - }, - { - "OperationName": "FormatHttpResponse", - "DisplayName": "Format HTTP Response", - "Status": "Ok", - "tags": [], - "event": [] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Parsing_Error_When_Rename_Root_Is_Activated.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Parsing_Error_When_Rename_Root_Is_Activated.snap deleted file mode 100644 index 71a7c84c3a4..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Parsing_Error_When_Rename_Root_Is_Activated.snap +++ /dev/null @@ -1,59 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteHttpRequest", - "DisplayName": "GraphQL HTTP POST: Begin Parse HTTP Request", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.schema.name", - "Value": "_Default" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseHttpRequest", - "DisplayName": "Parse HTTP Request", - "Status": "Error", - "tags": [ - { - "Key": "otel.status_code", - "Value": "ERROR" - } - ], - "event": [ - { - "Name": "exception", - "Tags": [ - { - "Key": "exception.message", - "Value": "Expected a `Name`-token, but found a `Integer`-token." - }, - { - "Key": "exception.type", - "Value": "HC0011" - }, - { - "Key": "graphql.error.location.column", - "Value": 3 - }, - { - "Key": "graphql.error.location.line", - "Value": 1 - } - ] - } - ] - }, - { - "OperationName": "FormatHttpResponse", - "DisplayName": "Format HTTP Response", - "Status": "Ok", - "tags": [], - "event": [] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Validation_Error_When_Rename_Root_Is_Activated.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Validation_Error_When_Rename_Root_Is_Activated.snap deleted file mode 100644 index a8e45a9ef6d..00000000000 --- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Validation_Error_When_Rename_Root_Is_Activated.snap +++ /dev/null @@ -1,105 +0,0 @@ -{ - "activities": [ - { - "OperationName": "ExecuteHttpRequest", - "DisplayName": "GraphQL HTTP POST: Begin Validate Document", - "Status": "Ok", - "tags": [ - { - "Key": "graphql.schema.name", - "Value": "_Default" - }, - { - "Key": "graphql.http.request.type", - "Value": "single" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ParseHttpRequest", - "DisplayName": "Parse HTTP Request", - "Status": "Ok", - "tags": [ - { - "Key": "otel.status_code", - "Value": "OK" - } - ], - "event": [] - }, - { - "OperationName": "ExecuteRequest", - "DisplayName": "Execute Request", - "Status": "Error", - "tags": [ - { - "Key": "graphql.document.id", - "Value": "346f68539881f0624dca2927281d1a2f" - }, - { - "Key": "graphql.document.hash", - "Value": "346f68539881f0624dca2927281d1a2f" - }, - { - "Key": "otel.status_code", - "Value": "ERROR" - } - ], - "event": [], - "activities": [ - { - "OperationName": "ValidateDocument", - "DisplayName": "Validate Document", - "Status": "Error", - "tags": [ - { - "Key": "otel.status_code", - "Value": "ERROR" - }, - { - "Key": "graphql.document.id", - "Value": "346f68539881f0624dca2927281d1a2f" - }, - { - "Key": "graphql.document.hash", - "Value": "346f68539881f0624dca2927281d1a2f" - } - ], - "event": [ - { - "Name": "exception", - "Tags": [ - { - "Key": "exception.message", - "Value": "The field `abc` does not exist on the type `Query`." - }, - { - "Key": "exception.type", - "Value": "GRAPHQL_ERROR" - }, - { - "Key": "graphql.error.location.column", - "Value": 3 - }, - { - "Key": "graphql.error.location.line", - "Value": 1 - } - ] - } - ] - } - ] - }, - { - "OperationName": "FormatHttpResponse", - "DisplayName": "Format HTTP Response", - "Status": "Ok", - "tags": [], - "event": [] - } - ] - } - ] -} diff --git a/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/Text/Json/CompositeResultDocumentTests.cs b/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/Text/Json/CompositeResultDocumentTests.cs index 0e6bfaffd23..7b5b6f04306 100644 --- a/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/Text/Json/CompositeResultDocumentTests.cs +++ b/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/Text/Json/CompositeResultDocumentTests.cs @@ -360,7 +360,7 @@ fragment Product on Product { var path = productBySlug.GetProperty("name").Path; // assert - Assert.Equal("/productBySlug/name", path.ToString()); + Assert.Equal("productBySlug.name", path.ToString()); } [Fact] @@ -413,7 +413,7 @@ public void Path_Array_Index() var path = name.Path; // assert - Assert.Equal("/users/nodes[0]/name", path.ToString()); + Assert.Equal("users.nodes[0].name", path.ToString()); } [Fact] diff --git a/src/HotChocolate/Language/src/Language.Web/DocumentHashProviderBase.cs b/src/HotChocolate/Language/src/Language.Web/DocumentHashProviderBase.cs index 82495992d0d..fa72800232b 100644 --- a/src/HotChocolate/Language/src/Language.Web/DocumentHashProviderBase.cs +++ b/src/HotChocolate/Language/src/Language.Web/DocumentHashProviderBase.cs @@ -15,6 +15,8 @@ internal DocumentHashProviderBase(HashFormat format) public abstract string Name { get; } + public abstract string AlgorithmName { get; } + public HashFormat Format { get; } public OperationDocumentHash ComputeHash(ReadOnlySpan document) @@ -27,7 +29,7 @@ public OperationDocumentHash ComputeHash(ReadOnlySpan document) { var hash = ComputeHash(rented, document.Length); var formattedHash = FormatHash(hash, Format); - return new OperationDocumentHash(formattedHash, Name, Format); + return new OperationDocumentHash(formattedHash, AlgorithmName, Format); } finally { @@ -35,7 +37,7 @@ public OperationDocumentHash ComputeHash(ReadOnlySpan document) } #else var hash = ComputeHash(document, Format); - return new OperationDocumentHash(hash, Name, Format); + return new OperationDocumentHash(hash, AlgorithmName, Format); #endif } @@ -65,7 +67,7 @@ public OperationDocumentHash ComputeHash(ReadOnlySequence document) } #else var hash = ComputeHash(document, Format); - return new OperationDocumentHash(hash, Name, Format); + return new OperationDocumentHash(hash, AlgorithmName, Format); #endif } diff --git a/src/HotChocolate/Language/src/Language.Web/IDocumentHashProvider.cs b/src/HotChocolate/Language/src/Language.Web/IDocumentHashProvider.cs index e0264a71342..47c4ccc1d35 100644 --- a/src/HotChocolate/Language/src/Language.Web/IDocumentHashProvider.cs +++ b/src/HotChocolate/Language/src/Language.Web/IDocumentHashProvider.cs @@ -12,6 +12,11 @@ public interface IDocumentHashProvider /// string Name { get; } + /// + /// The name of the algorithm used for hashing. + /// + string AlgorithmName { get; } + /// /// Gets the format of the document hash. /// diff --git a/src/HotChocolate/Language/src/Language.Web/MD5DocumentHashProvider.cs b/src/HotChocolate/Language/src/Language.Web/MD5DocumentHashProvider.cs index 70afad060b5..0196bba9b2b 100644 --- a/src/HotChocolate/Language/src/Language.Web/MD5DocumentHashProvider.cs +++ b/src/HotChocolate/Language/src/Language.Web/MD5DocumentHashProvider.cs @@ -18,6 +18,8 @@ public MD5DocumentHashProvider(HashFormat format) public override string Name => "md5Hash"; + public override string AlgorithmName => "md5"; + #if NETSTANDARD2_0 protected override byte[] ComputeHash(byte[] document, int length) { diff --git a/src/HotChocolate/Language/src/Language.Web/Sha1DocumentHashProvider.cs b/src/HotChocolate/Language/src/Language.Web/Sha1DocumentHashProvider.cs index 0d3547d1933..38f7e62f81e 100644 --- a/src/HotChocolate/Language/src/Language.Web/Sha1DocumentHashProvider.cs +++ b/src/HotChocolate/Language/src/Language.Web/Sha1DocumentHashProvider.cs @@ -22,6 +22,8 @@ public Sha1DocumentHashProvider(HashFormat format) public override string Name => "sha1Hash"; + public override string AlgorithmName => "sha1"; + #if NETSTANDARD2_0 protected override byte[] ComputeHash(byte[] document, int length) => _sha.Value!.ComputeHash(document, 0, length); diff --git a/src/HotChocolate/Language/src/Language.Web/Sha256DocumentHashProvider.cs b/src/HotChocolate/Language/src/Language.Web/Sha256DocumentHashProvider.cs index 0bdfa968d02..6412260f720 100644 --- a/src/HotChocolate/Language/src/Language.Web/Sha256DocumentHashProvider.cs +++ b/src/HotChocolate/Language/src/Language.Web/Sha256DocumentHashProvider.cs @@ -22,6 +22,8 @@ public Sha256DocumentHashProvider(HashFormat format) public override string Name => "sha256Hash"; + public override string AlgorithmName => "sha256"; + #if NETSTANDARD2_0 protected override byte[] ComputeHash(byte[] document, int length) { diff --git a/src/HotChocolate/Language/src/Language.Web/Utf8GraphQLRequestParser.cs b/src/HotChocolate/Language/src/Language.Web/Utf8GraphQLRequestParser.cs index 014e690e2a3..f2cf19074eb 100644 --- a/src/HotChocolate/Language/src/Language.Web/Utf8GraphQLRequestParser.cs +++ b/src/HotChocolate/Language/src/Language.Web/Utf8GraphQLRequestParser.cs @@ -328,7 +328,7 @@ private readonly GraphQLRequest ParseRequest(ref Utf8JsonReader reader, Operatio && TryExtractHash(extensions, out var hash)) { documentId = new OperationDocumentId(hash); - documentHash = new OperationDocumentHash(hash, _hashProvider!.Name, _hashProvider.Format); + documentHash = new OperationDocumentHash(hash, _hashProvider!.AlgorithmName, _hashProvider.Format); } // Parse the GraphQL document if provided diff --git a/src/HotChocolate/Language/test/Language.Web.Tests/Utf8GraphQLRequestParserTests.cs b/src/HotChocolate/Language/test/Language.Web.Tests/Utf8GraphQLRequestParserTests.cs index cd729e3471c..cba67360c1c 100644 --- a/src/HotChocolate/Language/test/Language.Web.Tests/Utf8GraphQLRequestParserTests.cs +++ b/src/HotChocolate/Language/test/Language.Web.Tests/Utf8GraphQLRequestParserTests.cs @@ -458,7 +458,7 @@ public void Parse_Apollo_AQP_SignatureQuery() Assert.True(request.Extensions!.RootElement.TryGetProperty("persistedQuery", out _)); Assert.Null(request.Document); Assert.Equal("hashOfQuery", request.DocumentHash?.Value); - Assert.Equal("sha256Hash", request.DocumentHash?.AlgorithmName); + Assert.Equal("sha256", request.DocumentHash?.AlgorithmName); } [Fact] diff --git a/src/HotChocolate/PersistedOperations/src/PersistedOperations.Pipeline/Execution/Pipeline/ReadPersistedOperationMiddleware.cs b/src/HotChocolate/PersistedOperations/src/PersistedOperations.Pipeline/Execution/Pipeline/ReadPersistedOperationMiddleware.cs index 43f23ce6e04..0905fb5d30d 100644 --- a/src/HotChocolate/PersistedOperations/src/PersistedOperations.Pipeline/Execution/Pipeline/ReadPersistedOperationMiddleware.cs +++ b/src/HotChocolate/PersistedOperations/src/PersistedOperations.Pipeline/Execution/Pipeline/ReadPersistedOperationMiddleware.cs @@ -86,7 +86,7 @@ private static DocumentNode GetOrParseDocument(IOperationDocument document) private OperationDocumentHash GetDocumentHash(IOperationDocument document) { if (document is IOperationDocumentHashProvider hashProvider - && _documentHashAlgorithm.Name.Equals(hashProvider.Hash.AlgorithmName) + && _documentHashAlgorithm.AlgorithmName.Equals(hashProvider.Hash.AlgorithmName) && _documentHashAlgorithm.Format.Equals(hashProvider.Hash.Format)) { return hashProvider.Hash; diff --git a/website/src/docs/hotchocolate/v16/migrating/migrate-from-15-to-16.md b/website/src/docs/hotchocolate/v16/migrating/migrate-from-15-to-16.md index e47a95b9a5b..a9f75cf24c9 100644 --- a/website/src/docs/hotchocolate/v16/migrating/migrate-from-15-to-16.md +++ b/website/src/docs/hotchocolate/v16/migrating/migrate-from-15-to-16.md @@ -687,6 +687,128 @@ builder .AddType(new DurationType("TimeSpan")); ``` +## AddInstrumentation + +### `InstrumentationOptions` changes + +- `RenameRootActivity` was removed. +- `RequestDetails.Operation` was renamed to `RequestDetails.OperationName`. +- `RequestDetails.Query` was renamed to `RequestDetails.Document`. + +## OpenTelemetry span and status changes + +The OpenTelemetry spans and attributes emitted by `AddInstrumentation()` have been updated to align with the [proposed OpenTelemetry semantic conventions for GraphQL](https://github.com/graphql/otel-wg/blob/main/spec). + +If you have dashboards or alerts that filter on the old attribute names or values, update them accordingly. + +Besides changes to the attributes, the most notable change is that the name of the root GraphQL span has been changed to just include the operation type (`query`, `mutation` or `subscription`), and no longer the operation name, to keep the cardinality low. The operation name can still be retrieved from the `graphql.operation.name` span attribute. + +### Removed attributes + +| Attribute | +| ----------------------------- | +| `graphql.operation.id` | +| `graphql.selection.type` | +| `graphql.selection.hierarchy` | + +### Renamed attributes + +| Old Attribute | New Attribute | +| --------------------------------------- | ------------------------------------- | +| `graphql.operation.kind` | `graphql.operation.type` | +| `graphql.selection.field.declaringType` | `graphql.selection.field.parent_type` | +| `graphql.dataLoader.keys.count` | `graphql.dataloader.batch.size` | +| `graphql.dataLoader.keys` | `graphql.dataloader.batch.keys` | +| `graphql.fusion.node.schema` | `graphql.source.name` | +| `graphql.fusion.node.type` | `graphql.operation.step.kind` | +| `graphql.error.location.line/column` | `graphql.error.locations` | + +### Changed attribute values + +| Attribute | Old Value | New Value | +| ------------------------ | ------------------------------------- | --------------------------------------------------- | +| `graphql.operation.type` | `Query` / `Mutation` / `Subscription` | `query` / `mutation` / `subscription` | +| `graphql.http.kind` | `operation-batch` | `operation_batch` | +| `graphql.document.hash` | `` | `:` , e.g. `md5:` | +| `graphql.document.id` | - | Value is only set if document is a trusted document | + +### Custom enricher changes + +If you've implemented a custom `ActivityEnricher`, you no longer need to pass the `ObjectPool` down to the base class: + +```diff +public class CustomActivityEnricher( +- ObjectPool stringBuilderPool, + InstrumentationOptions options +-) : ActivityEnricher(stringBuilderPool, options); ++) : ActivityEnricher(options); +``` + +There have also been some changes to the methods you can override in your enricher: + +| v15 | v16 | +| ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `EnrichParserErrors(HttpContext, IError, Activity)` | Replaced by `EnrichParserErrors(HttpContext, IReadOnlyList, Activity)`. | +| `EnrichRequestError(RequestContext, Activity, Exception)` | Replaced by `EnrichRequestError(RequestContext, Exception, Activity)`. | +| `EnrichRequestError(RequestContext, Activity, IError)` | Replaced by `EnrichRequestError(RequestContext, IError, Activity)`. | +| `EnrichValidationError(RequestContext, Activity, IError)` | Replaced by `EnrichValidationErrors(RequestContext, IReadOnlyList, Activity)`. | +| `EnrichAnalyzeOperationComplexity(RequestContext, Activity)` | Replaced by `EnrichAnalyzeOperationCost(RequestContext, Activity)`. | +| `EnrichDataLoaderBatch(IDataLoader, IReadOnlyList, Activity)` | Replaced by `EnrichExecuteBatch(IDataLoader, IReadOnlyList, Activity)`. | +| `EnrichResolverError(RequestContext, IError, Activity)` | Removed. Use `EnrichRequestError(...)` for request-level errors and `EnrichResolverError(IMiddlewareContext, IError, Activity)` for field resolver errors. | +| `EnrichRequestVariables(...)` | Removed. | +| `EnrichBatchVariables(...)` | Removed. | +| `EnrichRequestExtensions(...)` | Removed. | +| `EnrichBatchExtensions(...)` | Removed. | +| `CreateOperationDisplayName(...)` | Removed. | +| `CreateRootActivityName(...)` | Removed. | +| `EnrichError(...)` | Removed. | + +> Note: Overriding enricher methods without calling `base` no longer prevents the standard span attributes from being emitted. The semantic-convention attributes are now applied by the instrumentation itself, and custom enrichers are only intended for adding extra information. + +## Diagnostic Listeners + +We removed the following methods from the `IExecutionDiagnosticEventListener` since they no longer apply: + +- `ExecuteStream` +- `ExecuteDeferredTask` +- `DispatchBatch` +- `SubscriptionTransportError` +- `SubscriptionEventResult` + +Some other methods also had a change in their signature - simply override them again to fix any compilation issues. + + + # Deprecations Things that will continue to function this release, but we encourage you to move away from.