Skip to content

Commit

Permalink
Align with HotChocolate.Diagnostics implementation (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
glucaci authored Jan 13, 2022
1 parent 5796716 commit 4d019f9
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 57 deletions.
14 changes: 0 additions & 14 deletions src/Elastic.Apm.GraphQL.HotChocolate/Scopes/OperationDetails.cs
Original file line number Diff line number Diff line change
@@ -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<string> selections,
bool hasFailed)
{
Name = name;
RootSelection = rootSelection;
Selections = selections;
HasFailed = hasFailed;
}

public string? Name { get; }
public string? RootSelection { get; }
public IImmutableSet<string> Selections { get; }
public bool HasFailed { get; }
}
}
110 changes: 67 additions & 43 deletions src/Elastic.Apm.GraphQL.HotChocolate/Scopes/RequestActivityScope.cs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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,
Expand All @@ -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<IError>? errors))
{
Expand All @@ -59,54 +80,57 @@ public void Dispose()
}
}

private OperationDetails GetOperationDetails()
private string? CreateOperationDisplayName()
{
IReadOnlyList<IDefinitionNode>? 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<IDefinitionNode> 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());
}
}
}

0 comments on commit 4d019f9

Please sign in to comment.