diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/ActivityEnricher.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/ActivityEnricher.cs index 49a6cfbf391..4901ab1a26a 100644 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/ActivityEnricher.cs +++ b/src/HotChocolate/Diagnostics/src/Diagnostics/ActivityEnricher.cs @@ -12,6 +12,8 @@ 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; @@ -124,7 +126,8 @@ public virtual void EnrichSingleRequest( if (request.Variables is not null && (_options.RequestDetails & RequestDetails.Variables) == RequestDetails.Variables) { - EnrichRequestVariables(context, request, CreateVariablesNode(request.Variables), activity); + var node = CreateVariablesNode(request.Variables); + EnrichRequestVariables(context, request, node, activity); } if (request.Extensions is not null && @@ -146,7 +149,7 @@ public virtual void EnrichBatchRequest( var request = batch[i]; if (request.QueryId is not null && - (_options.RequestDetails & RequestDetails.Id) == RequestDetails.Id) + (_options.RequestDetails & RequestDetails.Id) == RequestDetails.Id) { activity.SetTag($"graphql.http.request[{i}].query.id", request.QueryId); } @@ -172,7 +175,8 @@ public virtual void EnrichBatchRequest( if (request.Variables is not null && (_options.RequestDetails & RequestDetails.Variables) == RequestDetails.Variables) { - EnrichBatchVariables(context, request, CreateVariablesNode(request.Variables), i, activity); + var node = CreateVariablesNode(request.Variables); + EnrichBatchVariables(context, request, node, i, activity); } if (request.Extensions is not null && @@ -218,7 +222,8 @@ public virtual void EnrichOperationBatchRequest( if (request.Variables is not null && (_options.RequestDetails & RequestDetails.Variables) == RequestDetails.Variables) { - EnrichRequestVariables(context, request, CreateVariablesNode(request.Variables), activity); + var node = CreateVariablesNode(request.Variables); + EnrichRequestVariables(context, request, node, activity); } if (request.Extensions is not null && @@ -582,33 +587,33 @@ public virtual void EnrichDataLoaderBatch( protected virtual void EnrichError(IError error, Activity activity) { - var tags = new List> + if (error.Exception is { } exception) + { + activity.RecordException(exception); + } + + var tags = new ActivityTagsCollection { - new("graphql.error.message", error.Message), - new("graphql.error.code", error.Code), + new(AttributeExceptionMessage, error.Message), + new(AttributeExceptionType, error.Code ?? "GRAPHQL_ERROR"), }; - if (error.Locations is { Count: > 0, }) + if (error.Path is not null) { - if (error.Locations.Count == 1) - { - tags.Add(new("graphql.error.location.column", error.Locations[0].Column)); - tags.Add(new("graphql.error.location.line", error.Locations[0].Line)); - } - else - { - for (var i = 0; i < error.Locations.Count; i++) - { - tags.Add(new($"graphql.error.location[{i}].column", error.Locations[i].Column)); - tags.Add(new($"graphql.error.location[{i}].line", error.Locations[i].Line)); - } - } + 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("Error", tags: new(tags))); + activity.AddEvent(new ActivityEvent(AttributeExceptionEventName, default, tags)); } - private static ISyntaxNode CreateVariablesNode(IReadOnlyList>? variableSet) + private static ISyntaxNode CreateVariablesNode( + IReadOnlyList>? variableSet) { if (variableSet is null or { Count: 0, }) { @@ -637,7 +642,7 @@ private static ISyntaxNode CreateVariablesNode(IReadOnlyList + { + o.Scopes = ActivityScopes.All; + o.IncludeDocument = true; + }) + .AddQueryType() + .ExecuteRequestAsync( + """ + query SayHelloOperation { + deep { + deeper { + deeps { + deeper { + causeFatalError + } + } + } + } + } + """); + + // assert +#if NET7_0_OR_GREATER + activities.MatchSnapshot(new SnapshotNameExtension("_NET7")); +#else + activities.MatchSnapshot(); +#endif + } + } + public class SimpleQuery { public string SayHello() => "hello"; public string CauseFatalError() => throw new GraphQLException("fail"); + public Deep Deep() => new Deep(); + public Task DataLoader(CustomDataLoader dataLoader, string key) => dataLoader.LoadAsync(key); } + + public class Deep + { + public Deeper Deeper() => new Deeper(); + + 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__/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 index e021e950f81..d59fa861e4c 100644 --- 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 @@ -137,9 +137,11 @@ ], "event": [ { - "Name": "Error", + "Name": "exception", "Tags": { - "graphql.error.message": "fail", + "exception.message": "fail", + "exception.type": "GRAPHQL_ERROR", + "graphql.error.path": "/causeFatalError", "graphql.error.location.column": 27, "graphql.error.location.line": 1 } diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Cause_a_resolver_error_that_deletes_the_whole_result__NET7.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Cause_a_resolver_error_that_deletes_the_whole_result__NET7.snap index a7e1374cbef..f50860cdcec 100644 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Cause_a_resolver_error_that_deletes_the_whole_result__NET7.snap +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Cause_a_resolver_error_that_deletes_the_whole_result__NET7.snap @@ -137,12 +137,20 @@ ], "event": [ { - "Name": "Error", + "Name": "exception", "Tags": [ { - "Key": "graphql.error.message", + "Key": "exception.message", "Value": "fail" }, + { + "Key": "exception.type", + "Value": "GRAPHQL_ERROR" + }, + { + "Key": "graphql.error.path", + "Value": "/causeFatalError" + }, { "Key": "graphql.error.location.column", "Value": 27 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 new file mode 100644 index 00000000000..e8fcadaf5c3 --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Cause_a_resolver_error_that_deletes_the_whole_result_deep.snap @@ -0,0 +1,73 @@ +{ + "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": { + "exception.message": "The field `causeFatalError` does not exist on the type `Deeper`.", + "exception.type": "GRAPHQL_ERROR", + "graphql.error.path": "/deep/deeper/deeps/deeper", + "graphql.error.location.column": 21, + "graphql.error.location.line": 6 + } + } + ] + } + ] + } + ] +} diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Cause_a_resolver_error_that_deletes_the_whole_result_deep__NET7.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Cause_a_resolver_error_that_deletes_the_whole_result_deep__NET7.snap new file mode 100644 index 00000000000..df987de347c --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Cause_a_resolver_error_that_deletes_the_whole_result_deep__NET7.snap @@ -0,0 +1,88 @@ +{ + "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.path", + "Value": "/deep/deeper/deeps/deeper" + }, + { + "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.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 index b592ddf4b7c..58598c42d1a 100644 --- 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 @@ -56,9 +56,10 @@ ], "event": [ { - "Name": "Error", + "Name": "exception", "Tags": { - "graphql.error.message": "The field `sayHello_` does not exist on the type `SimpleQuery`.", + "exception.message": "The field `sayHello_` does not exist on the type `SimpleQuery`.", + "exception.type": "GRAPHQL_ERROR", "graphql.error.location.column": 27, "graphql.error.location.line": 1 } diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Ensure_that_the_validation_activity_has_an_error_status__NET7.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Ensure_that_the_validation_activity_has_an_error_status__NET7.snap index d08035787a7..c13dbaa8b92 100644 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Ensure_that_the_validation_activity_has_an_error_status__NET7.snap +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Ensure_that_the_validation_activity_has_an_error_status__NET7.snap @@ -56,12 +56,16 @@ ], "event": [ { - "Name": "Error", + "Name": "exception", "Tags": [ { - "Key": "graphql.error.message", + "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 diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_parser_error.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_parser_error.snap index 5a2e4f411b6..e1d8c23836e 100644 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_parser_error.snap +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_parser_error.snap @@ -24,10 +24,10 @@ ], "event": [ { - "Name": "Error", + "Name": "exception", "Tags": { - "graphql.error.message": "Found a NameStart character `n` (110) following a number, which is disallowed.", - "graphql.error.code": "HC0011", + "exception.message": "Found a NameStart character `n` (110) following a number, which is disallowed.", + "exception.type": "HC0011", "graphql.error.location.column": 37, "graphql.error.location.line": 10 } diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_parser_error__NET7.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_parser_error__NET7.snap index 5a6d9f80cdc..e33490a9a8c 100644 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_parser_error__NET7.snap +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Http_Post_parser_error__NET7.snap @@ -24,14 +24,14 @@ ], "event": [ { - "Name": "Error", + "Name": "exception", "Tags": [ { - "Key": "graphql.error.message", + "Key": "exception.message", "Value": "Found a NameStart character `n` (110) following a number, which is disallowed." }, { - "Key": "graphql.error.code", + "Key": "exception.type", "Value": "HC0011" }, { 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 index 75699f922ff..988f5cf7dd5 100644 --- 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 @@ -24,10 +24,10 @@ ], "event": [ { - "Name": "Error", + "Name": "exception", "Tags": { - "graphql.error.message": "Expected a `Name`-token, but found a `Integer`-token.", - "graphql.error.code": "HC0011", + "exception.message": "Expected a `Name`-token, but found a `Integer`-token.", + "exception.type": "HC0011", "graphql.error.location.column": 21, "graphql.error.location.line": 3 } diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Parsing_error_when_rename_root_is_activated__NET7.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Parsing_error_when_rename_root_is_activated__NET7.snap index 3ae24244d96..b0834861a59 100644 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Parsing_error_when_rename_root_is_activated__NET7.snap +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Parsing_error_when_rename_root_is_activated__NET7.snap @@ -24,14 +24,14 @@ ], "event": [ { - "Name": "Error", + "Name": "exception", "Tags": [ { - "Key": "graphql.error.message", + "Key": "exception.message", "Value": "Expected a `Name`-token, but found a `Integer`-token." }, { - "Key": "graphql.error.code", + "Key": "exception.type", "Value": "HC0011" }, { 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 index 5a824009483..29c52863bfc 100644 --- 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 @@ -68,9 +68,10 @@ ], "event": [ { - "Name": "Error", + "Name": "exception", "Tags": { - "graphql.error.message": "The field `abc` does not exist on the type `Query`.", + "exception.message": "The field `abc` does not exist on the type `Query`.", + "exception.type": "GRAPHQL_ERROR", "graphql.error.location.column": 21, "graphql.error.location.line": 3 } diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Validation_error_when_rename_root_is_activated__NET7.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Validation_error_when_rename_root_is_activated__NET7.snap index 2dcb421703c..746f536dda4 100644 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Validation_error_when_rename_root_is_activated__NET7.snap +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ServerInstrumentationTests.Validation_error_when_rename_root_is_activated__NET7.snap @@ -68,12 +68,16 @@ ], "event": [ { - "Name": "Error", + "Name": "exception", "Tags": [ { - "Key": "graphql.error.message", + "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