Update GraphQL tags and errors (#1592)

update GraphQL tags and errors

Co-authored-by: Paulo Janotti <pjanotti@splunk.com>
This commit is contained in:
Rasmus Kuusmann 2022-11-08 20:23:36 +02:00 committed by GitHub
parent c02d67acc6
commit 64de5b4765
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 34 additions and 102 deletions

View File

@ -12,6 +12,7 @@ This component adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.h
- Replace `OTEL_DOTNET_AUTO_LOAD_TRACER_AT_STARTUP` by `OTEL_DOTNET_AUTO_TRACES_ENABLED`
and `OTEL_DOTNET_AUTO_LOAD_METER_AT_STARTUP` by `OTEL_DOTNET_AUTO_METRICS_ENABLED`.
- Disable OpenTracing by default. OpenTracing can be re-enabled via `OTEL_DOTNET_AUTO_OPENTRACING_ENABLED`.
- GraphQL exceptions are recorded as OTel events.
### Removed

View File

@ -89,6 +89,7 @@ OpenTelemetry.AutoInstrumentation.Instrumentations.GraphQL.IExecutionContext.Err
OpenTelemetry.AutoInstrumentation.Instrumentations.GraphQL.IExecutionContext.Operation.get -> OpenTelemetry.AutoInstrumentation.Instrumentations.GraphQL.IOperation
OpenTelemetry.AutoInstrumentation.Instrumentations.GraphQL.IExecutionError
OpenTelemetry.AutoInstrumentation.Instrumentations.GraphQL.IExecutionError.Code.get -> string
OpenTelemetry.AutoInstrumentation.Instrumentations.GraphQL.IExecutionError.InnerException.get -> System.Exception
OpenTelemetry.AutoInstrumentation.Instrumentations.GraphQL.IExecutionError.Locations.get -> System.Collections.Generic.IEnumerable<object>
OpenTelemetry.AutoInstrumentation.Instrumentations.GraphQL.IExecutionError.Message.get -> string
OpenTelemetry.AutoInstrumentation.Instrumentations.GraphQL.IExecutionError.Path.get -> System.Collections.Generic.IEnumerable<string>

View File

@ -89,6 +89,7 @@ OpenTelemetry.AutoInstrumentation.Instrumentations.GraphQL.IExecutionContext.Err
OpenTelemetry.AutoInstrumentation.Instrumentations.GraphQL.IExecutionContext.Operation.get -> OpenTelemetry.AutoInstrumentation.Instrumentations.GraphQL.IOperation
OpenTelemetry.AutoInstrumentation.Instrumentations.GraphQL.IExecutionError
OpenTelemetry.AutoInstrumentation.Instrumentations.GraphQL.IExecutionError.Code.get -> string
OpenTelemetry.AutoInstrumentation.Instrumentations.GraphQL.IExecutionError.InnerException.get -> System.Exception
OpenTelemetry.AutoInstrumentation.Instrumentations.GraphQL.IExecutionError.Locations.get -> System.Collections.Generic.IEnumerable<object>
OpenTelemetry.AutoInstrumentation.Instrumentations.GraphQL.IExecutionError.Message.get -> string
OpenTelemetry.AutoInstrumentation.Instrumentations.GraphQL.IExecutionError.Path.get -> System.Collections.Generic.IEnumerable<string>

View File

@ -36,8 +36,6 @@ namespace OpenTelemetry.AutoInstrumentation.Instrumentations.GraphQL;
MaximumVersion = GraphQLCommon.Major2)]
public class ExecuteAsyncIntegration
{
private const string ErrorType = "GraphQL.ExecutionError";
/// <summary>
/// OnMethodBegin callback
/// </summary>
@ -78,7 +76,7 @@ public class ExecuteAsyncIntegration
}
else if (state.State is IExecutionContext context)
{
GraphQLCommon.RecordExecutionErrorsIfPresent(activity, ErrorType, context.Errors);
GraphQLCommon.RecordExecutionErrorsIfPresent(activity, context.Errors);
}
}
finally

View File

@ -16,13 +16,9 @@
using System;
using System.Diagnostics;
using System.Text;
using OpenTelemetry.AutoInstrumentation.Configuration;
using OpenTelemetry.AutoInstrumentation.DuckTyping;
using OpenTelemetry.AutoInstrumentation.Logging;
using OpenTelemetry.AutoInstrumentation.Tagging;
using OpenTelemetry.AutoInstrumentation.Util;
using OpenTelemetry.Trace;
namespace OpenTelemetry.AutoInstrumentation.Instrumentations.GraphQL;
@ -72,86 +68,22 @@ internal class GraphQLCommon
return activity;
}
internal static void RecordExecutionErrorsIfPresent(Activity activity, string errorType, IExecutionErrors executionErrors)
internal static void RecordExecutionErrorsIfPresent(Activity activity, IExecutionErrors executionErrors)
{
var errorCount = executionErrors?.Count ?? 0;
if (errorCount > 0)
{
activity.SetTag(Tags.Status, Status.Error);
activity.SetTag(Tags.ErrorMsg, $"{errorCount} error(s)");
activity.SetTag(Tags.ErrorType, errorType);
activity.SetTag(Tags.ErrorStack, ConstructErrorMessage(executionErrors));
}
}
private static string ConstructErrorMessage(IExecutionErrors executionErrors)
{
if (executionErrors == null)
{
return string.Empty;
}
var builder = new StringBuilder();
try
{
const string tab = " ";
builder.AppendLine("errors: [");
for (int i = 0; i < executionErrors.Count; i++)
for (int i = 0; i < errorCount; i++)
{
var executionError = executionErrors[i];
Exception ex = executionErrors[i].InnerException;
builder.AppendLine($"{tab}{{");
var message = executionError.Message;
if (message != null)
if (ex != null)
{
builder.AppendLine($"{tab + tab}\"message\": \"{message.Replace("\r", "\\r").Replace("\n", "\\n")}\",");
activity.SetException(ex);
}
var path = executionError.Path;
if (path != null)
{
builder.AppendLine($"{tab + tab}\"path\": \"{string.Join(".", path)}\",");
}
var code = executionError.Code;
if (code != null)
{
builder.AppendLine($"{tab + tab}\"code\": \"{code}\",");
}
builder.AppendLine($"{tab + tab}\"locations\": [");
var locations = executionError.Locations;
if (locations != null)
{
foreach (var location in locations)
{
if (location.TryDuckCast<ErrorLocationStruct>(out var locationProxy))
{
builder.AppendLine($"{tab + tab + tab}{{");
builder.AppendLine($"{tab + tab + tab + tab}\"line\": {locationProxy.Line},");
builder.AppendLine($"{tab + tab + tab + tab}\"column\": {locationProxy.Column}");
builder.AppendLine($"{tab + tab + tab}}},");
}
}
}
builder.AppendLine($"{tab + tab}]");
builder.AppendLine($"{tab}}},");
}
builder.AppendLine("]");
}
catch (Exception ex)
{
Log.Error(ex, "Error creating GraphQL error message.");
return "errors: []";
}
return builder.ToString();
}
private static string GetOperation(string operationName, string operationType)

View File

@ -14,6 +14,7 @@
// limitations under the License.
// </copyright>
using System;
using System.Collections.Generic;
namespace OpenTelemetry.AutoInstrumentation.Instrumentations.GraphQL;
@ -42,4 +43,9 @@ public interface IExecutionError
/// Gets the path in the document where the error applies
/// </summary>
IEnumerable<string> Path { get; }
/// <summary>
/// Gets the original thrown exception.
/// </summary>
Exception InnerException { get; }
}

View File

@ -14,8 +14,6 @@
// limitations under the License.
// </copyright>
using System.Diagnostics;
namespace OpenTelemetry.AutoInstrumentation.Tagging;
/// <summary>
@ -23,26 +21,6 @@ namespace OpenTelemetry.AutoInstrumentation.Tagging;
/// </summary>
internal static class Tags
{
/// <summary>
/// The error message of an exception
/// </summary>
public const string ErrorMsg = "error.msg";
/// <summary>
/// The type of an exception
/// </summary>
public const string ErrorType = "error.type";
/// <summary>
/// The stack trace of an exception
/// </summary>
public const string ErrorStack = "error.stack";
/// <summary>
/// The status of a span
/// </summary>
public const string Status = "status";
/// <summary>
/// The hostname of a outgoing server connection.
/// </summary>

View File

@ -64,7 +64,7 @@ public class GraphQLTests : TestHelper
// FAILURE: query fails 'execute' step
Request(requests, body: @"{""query"":""subscription NotImplementedSub{throwNotImplementedException{name}}""}");
Expect(collector, spanName: "subscription NotImplementedSub", graphQLOperationType: "subscription", graphQLOperationName: "NotImplementedSub", graphQLDocument: "subscription NotImplementedSub{throwNotImplementedException{name}}", setDocument: setDocument, failsExecution: true);
Expect(collector, spanName: "subscription NotImplementedSub", graphQLOperationType: "subscription", graphQLOperationName: "NotImplementedSub", graphQLDocument: "subscription NotImplementedSub{throwNotImplementedException{name}}", setDocument: setDocument, verifyFailure: VerifyNotImplementedException);
SetEnvironmentVariable("OTEL_DOTNET_AUTO_GRAPHQL_SET_DOCUMENT", setDocument.ToString());
int aspNetCorePort = TcpPortProvider.GetOpenPort();
@ -103,6 +103,21 @@ public class GraphQLTests : TestHelper
});
}
private static bool VerifyNotImplementedException(Span span)
{
var exceptionEvent = span.Events.SingleOrDefault();
if (exceptionEvent == null)
{
return false;
}
return
exceptionEvent.Attributes.Any(x => x.Key == "exception.type" && x.Value?.StringValue == "System.NotImplementedException") &&
exceptionEvent.Attributes.Any(x => x.Key == "exception.message") &&
exceptionEvent.Attributes.Any(x => x.Key == "exception.stacktrace");
}
private static void Expect(
MockSpansCollector collector,
string spanName,
@ -110,7 +125,7 @@ public class GraphQLTests : TestHelper
string graphQLOperationName,
string graphQLDocument,
bool setDocument,
bool failsExecution = false)
Predicate<Span> verifyFailure = null)
{
bool Predicate(Span span)
{
@ -124,9 +139,9 @@ public class GraphQLTests : TestHelper
return false;
}
if (failsExecution && !span.Attributes.Any(attr => attr.Key == "error.msg"))
if (verifyFailure != null)
{
return false;
return verifyFailure(span);
}
if (!span.Attributes.Any(attr => attr.Key == "graphql.operation.type" && attr.Value?.StringValue == graphQLOperationType))