From 4d019f9bcc804665af37eb64d485e1d0e6b0a432 Mon Sep 17 00:00:00 2001 From: Gabriel Lucaci Date: Thu, 13 Jan 2022 17:10:18 +0100 Subject: [PATCH] Align with HotChocolate.Diagnostics implementation (#20) --- .../Scopes/OperationDetails.cs | 14 --- .../Scopes/RequestActivityScope.cs | 110 +++++++++++------- 2 files changed, 67 insertions(+), 57 deletions(-) diff --git a/src/Elastic.Apm.GraphQL.HotChocolate/Scopes/OperationDetails.cs b/src/Elastic.Apm.GraphQL.HotChocolate/Scopes/OperationDetails.cs index 1a0b1ab..7739c0a 100644 --- a/src/Elastic.Apm.GraphQL.HotChocolate/Scopes/OperationDetails.cs +++ b/src/Elastic.Apm.GraphQL.HotChocolate/Scopes/OperationDetails.cs @@ -1,30 +1,16 @@ -using System.Collections.Immutable; - namespace Elastic.Apm.GraphQL.HotChocolate { public class OperationDetails { - internal static readonly OperationDetails Empty = new OperationDetails( - "unnamed", - "unknown", - new[] { "unknown" }.ToImmutableHashSet(), - true); - internal OperationDetails( string? name, - string? rootSelection, - IImmutableSet selections, bool hasFailed) { Name = name; - RootSelection = rootSelection; - Selections = selections; HasFailed = hasFailed; } public string? Name { get; } - public string? RootSelection { get; } - public IImmutableSet Selections { get; } public bool HasFailed { get; } } } diff --git a/src/Elastic.Apm.GraphQL.HotChocolate/Scopes/RequestActivityScope.cs b/src/Elastic.Apm.GraphQL.HotChocolate/Scopes/RequestActivityScope.cs index 26040af..5e2c5a2 100644 --- a/src/Elastic.Apm.GraphQL.HotChocolate/Scopes/RequestActivityScope.cs +++ b/src/Elastic.Apm.GraphQL.HotChocolate/Scopes/RequestActivityScope.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; +using System.Text; using Elastic.Apm.Api; using Elastic.Apm.Logging; using HotChocolate.Execution; -using HotChocolate.Language; +using HotChocolate.Execution.Processing; using IError = HotChocolate.IError; namespace Elastic.Apm.GraphQL.HotChocolate @@ -18,6 +18,7 @@ internal class RequestActivityScope : IDisposable private readonly ITransaction _transaction; private readonly IApmAgent _apmAgent; private readonly HotChocolateDiagnosticOptions _options; + private bool _disposed; internal RequestActivityScope( IRequestContext context, @@ -32,14 +33,34 @@ internal RequestActivityScope( } public void Dispose() + { + if (!_disposed) + { + EnrichTransaction(); + _disposed = true; + } + } + + private void EnrichTransaction() { try { OperationDetails operationDetails = GetOperationDetails(); - _transaction.Name = $"[{operationDetails.Name}] {operationDetails.RootSelection}"; + _transaction.Name = operationDetails.Name; _transaction.Type = ApiConstants.TypeRequest; - _transaction.SetLabel("selections", string.Join(";", operationDetails.Selections)); + _transaction.SetLabel("graphql.document.id", _context.DocumentId); + _transaction.SetLabel("graphql.document.hash", _context.DocumentHash); + _transaction.SetLabel("graphql.document.valid", _context.IsValidDocument); + _transaction.SetLabel("graphql.operation.id", _context.OperationId); + _transaction.SetLabel("graphql.operation.kind", _context.Operation?.Type.ToString()); + _transaction.SetLabel("graphql.operation.name", _context.Operation?.Name?.Value); + + if (_context.Result is IQueryResult result) + { + var errorCount = result.Errors?.Count ?? 0; + _transaction.SetLabel("graphql.errors.count", errorCount); + } if (_context.Result.HasErrors(out IReadOnlyList? errors)) { @@ -59,54 +80,57 @@ public void Dispose() } } - private OperationDetails GetOperationDetails() + private string? CreateOperationDisplayName() { - IReadOnlyList? definitions = _context.Document?.Definitions; - if (definitions?.Count > 0) + if (_context.Operation is { } operation) { - var name = GetOperationName(definitions); - - var selections = definitions.GetFieldNodes() - .Select(x => string.IsNullOrEmpty(x.Name.Value) ? "unknown" : x.Name.Value) - .DefaultIfEmpty("unknown") - .ToImmutableHashSet(); - - var rootSelection = definitions.Count > 1 - ? "multiple" - : name == "exec_batch" - ? selections.Count > 1 - ? $"multiple ({definitions.FirstSelectionsCount()})" - : $"{selections.FirstOrDefault()} ({definitions.FirstSelectionsCount()})" - : selections.FirstOrDefault(); - - return new OperationDetails(name, rootSelection, selections, _context.HasFailed()); - } + var displayName = new StringBuilder(); - return OperationDetails.Empty; - } + ISelectionSet rootSelectionSet = operation.GetRootSelectionSet(); - private string? GetOperationName(IReadOnlyList definition) - { - var name = _context.Request.OperationName; + displayName.Append('{'); + displayName.Append(' '); - if (string.IsNullOrEmpty(name) && - definition.Count == 1 && - definition[0] is OperationDefinitionNode node) - { - name = node.Name?.Value; - } + foreach (ISelection selection in rootSelectionSet.Selections.Take(3)) + { + if (displayName.Length > 2) + { + displayName.Append(' '); + } - if (string.IsNullOrEmpty(name) || name == "fetch") - { - name = _context.Request.QueryId; - } + displayName.Append(selection.ResponseName); + } - if (string.IsNullOrEmpty(name)) - { - name = Constants.DefaultOperation; + if (rootSelectionSet.Selections.Count > 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.Value); + } + + displayName.Insert(0, ' '); + displayName.Insert(0, operation.Definition.Operation.ToString().ToLowerInvariant()); + + return displayName.ToString(); } - return name; + return null; + } + + private OperationDetails GetOperationDetails() + { + var operationDisplayName = CreateOperationDisplayName(); + return new OperationDetails(operationDisplayName ?? "unnamed", _context.HasFailed()); } } }