Update GraphQL tags and errors (#1592)
update GraphQL tags and errors Co-authored-by: Paulo Janotti <pjanotti@splunk.com>
This commit is contained in:
		
							parent
							
								
									c02d67acc6
								
							
						
					
					
						commit
						64de5b4765
					
				| 
						 | 
				
			
			@ -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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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; }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue