diff --git a/src/VirtoCommerce.Xapi.Core/Infrastructure/GraphQLHttpMiddlewareWithLogs.cs b/src/VirtoCommerce.Xapi.Core/Infrastructure/GraphQLHttpMiddlewareWithLogs.cs
index 061f060..32126a0 100644
--- a/src/VirtoCommerce.Xapi.Core/Infrastructure/GraphQLHttpMiddlewareWithLogs.cs
+++ b/src/VirtoCommerce.Xapi.Core/Infrastructure/GraphQLHttpMiddlewareWithLogs.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using System.Net.WebSockets;
using System.Threading.Tasks;
@@ -7,10 +8,7 @@
using GraphQL.Server.Transports.AspNetCore;
using GraphQL.Transport;
using GraphQL.Types;
-using Microsoft.ApplicationInsights;
-using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
@@ -18,11 +16,15 @@
namespace VirtoCommerce.Xapi.Core.Infrastructure
{
+ ///
+ /// Relies solely on telemetry
+ /// instead of direct Microsoft.ApplicationInsights types. Any configured
+ /// OpenTelemetry exporter (Application Insights, OTLP, etc.) picks up the data automatically.
+ ///
public class GraphQLHttpMiddlewareWithLogs : GraphQLHttpMiddleware
where TSchema : ISchema
{
private readonly ILogger _logger;
- private readonly TelemetryClient _telemetryClient;
public GraphQLHttpMiddlewareWithLogs(
RequestDelegate next,
@@ -31,58 +33,46 @@ public GraphQLHttpMiddlewareWithLogs(
IServiceScopeFactory serviceScopeFactory,
GraphQLHttpMiddlewareOptions options,
IHostApplicationLifetime hostApplicationLifetime,
- ILogger> logger,
- TelemetryClient telemetryClient = null)
+ ILogger> logger)
: base(next, serializer, documentExecuter, serviceScopeFactory, options, hostApplicationLifetime)
{
_logger = logger;
- _telemetryClient = telemetryClient;
}
- protected override async Task ExecuteRequestAsync(HttpContext context, GraphQLRequest request, IServiceProvider serviceProvider, IDictionary userContext)
+ protected override async Task ExecuteRequestAsync(
+ HttpContext context,
+ GraphQLRequest request,
+ IServiceProvider serviceProvider,
+ IDictionary userContext)
{
- // process Playground schema introspection queries without AppInsights logging
- if (_telemetryClient is null || request?.OperationName == "IntrospectionQuery")
+ // skip Playground schema introspection queries
+ if (request?.OperationName == "IntrospectionQuery")
{
return await base.ExecuteRequestAsync(context, request, serviceProvider, userContext);
}
- // prepare AppInsights telemetry
- var appInsightsOperationName = $"POST graphql/{request?.OperationName}";
-
- var requestTelemetry = new RequestTelemetry
+ // enrich the server activity created by AspNetCore OTel instrumentation
+ var activity = Activity.Current;
+ if (activity != null && request?.OperationName.IsNullOrEmpty() == false)
{
- Name = appInsightsOperationName,
- Url = new Uri(context.Request.GetEncodedUrl()),
- };
-
- //Replace W3C Trace Context id generation https://www.w3.org/TR/trace-context/ to unique value
- requestTelemetry.Context.Operation.Id = Guid.NewGuid().ToString("N");
- requestTelemetry.Context.Operation.Name = appInsightsOperationName;
- requestTelemetry.Properties["Type"] = "GraphQL";
-
- using var operation = _telemetryClient.StartOperation(requestTelemetry);
+ activity.DisplayName = $"POST graphql/{request.OperationName}";
+ activity.SetTag("url.path", $"graphql/{request.OperationName}");
+ activity.SetTag("graphql.type", "GraphQL");
+ }
- // execute GraphQL query
var result = await base.ExecuteRequestAsync(context, request, serviceProvider, userContext);
- requestTelemetry.Success = result.Errors.IsNullOrEmpty();
- if (requestTelemetry.Success != true)
+ if (!result.Errors.IsNullOrEmpty() && activity != null)
{
- // pass an error response code to trigger AppInsights operation failure state
- requestTelemetry.ResponseCode = "500";
-
- Exception exception = result.Errors?.Count > 1
- ? new AggregateException(result.Errors)
- : result.Errors?.FirstOrDefault();
-
- var exceptionTelemetry = new ExceptionTelemetry(exception);
-
- // link exception with the operation
- exceptionTelemetry.Context.Operation.ParentId = requestTelemetry.Context.Operation.Id;
- exceptionTelemetry.Context.Operation.Name = appInsightsOperationName;
+ var exception = result.Errors.Count > 1
+ ? (Exception)new AggregateException(result.Errors)
+ : result.Errors.FirstOrDefault();
- _telemetryClient.TrackException(exceptionTelemetry);
+ activity.SetStatus(ActivityStatusCode.Error, exception?.Message);
+ if (exception != null)
+ {
+ activity.AddException(exception);
+ }
}
return result;
diff --git a/src/VirtoCommerce.Xapi.Core/Infrastructure/IgnorePlainCoreXapiGraphQLTelemetryProcessor.cs b/src/VirtoCommerce.Xapi.Core/Infrastructure/IgnorePlainCoreXapiGraphQLTelemetryProcessor.cs
index 19f32d0..041b1c8 100644
--- a/src/VirtoCommerce.Xapi.Core/Infrastructure/IgnorePlainCoreXapiGraphQLTelemetryProcessor.cs
+++ b/src/VirtoCommerce.Xapi.Core/Infrastructure/IgnorePlainCoreXapiGraphQLTelemetryProcessor.cs
@@ -1,31 +1,25 @@
-using Microsoft.ApplicationInsights.Channel;
-using Microsoft.ApplicationInsights.DataContracts;
-using Microsoft.ApplicationInsights.Extensibility;
+using System.Diagnostics;
+using OpenTelemetry;
using VirtoCommerce.Platform.Core.Common;
-namespace VirtoCommerce.Xapi.Core.Infrastructure
-{
- ///
- /// AppInsights telemetry processor that skips default Graphql queries
- ///
- public class IgnorePlainCoreXapiGraphQLTelemetryProcessor : ITelemetryProcessor
- {
- private ITelemetryProcessor Next { get; set; }
- public IgnorePlainCoreXapiGraphQLTelemetryProcessor(ITelemetryProcessor next)
- {
- Next = next;
- }
+namespace VirtoCommerce.ApplicationInsights.Data.Telemetry;
- public void Process(ITelemetry item)
+///
+/// OpenTelemetry processor that suppresses activities whose urls match GraphQL related path but
+/// graphql.type tag is empty meaning this activity was not generated by GraphQLHttpMiddlewareWithLogs.
+/// Clearing the flag prevents the activity
+/// from being exported to Application Insights.
+///
+public class IgnorePlainCoreXapiGraphQLTelemetryProcessor : BaseProcessor
+{
+ public override void OnEnd(Activity activity)
+ {
+ var urlPath = activity?.GetTagItem("url.path")?.ToString();
+ var graphQlTag = activity?.GetTagItem("graphql.type");
+ if (urlPath != null && urlPath.ContainsIgnoreCase("/graphql") && graphQlTag == null)
{
- // skip plain "POST /graphql" (without operation name) requests to reduce AppInsights telemetry flood
- if (item is RequestTelemetry request && request.Name?.EqualsIgnoreCase("POST /graphql") == true)
- {
- return;
- }
-
- Next.Process(item);
+ activity.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded;
}
}
}
diff --git a/src/VirtoCommerce.Xapi.Core/VirtoCommerce.Xapi.Core.csproj b/src/VirtoCommerce.Xapi.Core/VirtoCommerce.Xapi.Core.csproj
index 51476be..7e9120a 100644
--- a/src/VirtoCommerce.Xapi.Core/VirtoCommerce.Xapi.Core.csproj
+++ b/src/VirtoCommerce.Xapi.Core/VirtoCommerce.Xapi.Core.csproj
@@ -1,4 +1,4 @@
-
+
Experiene API functionality
net10.0
@@ -23,16 +23,16 @@
-
+
+
-
diff --git a/src/VirtoCommerce.Xapi.Data/VirtoCommerce.Xapi.Data.csproj b/src/VirtoCommerce.Xapi.Data/VirtoCommerce.Xapi.Data.csproj
index 2c71222..8b4ff46 100644
--- a/src/VirtoCommerce.Xapi.Data/VirtoCommerce.Xapi.Data.csproj
+++ b/src/VirtoCommerce.Xapi.Data/VirtoCommerce.Xapi.Data.csproj
@@ -1,4 +1,4 @@
-
+
net10.0
@@ -9,7 +9,7 @@
snupkg
-
+
diff --git a/src/VirtoCommerce.Xapi.Web/Module.cs b/src/VirtoCommerce.Xapi.Web/Module.cs
index 23b6931..8c521aa 100644
--- a/src/VirtoCommerce.Xapi.Web/Module.cs
+++ b/src/VirtoCommerce.Xapi.Web/Module.cs
@@ -10,13 +10,14 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
+using OpenTelemetry.Trace;
+using VirtoCommerce.ApplicationInsights.Data.Telemetry;
using VirtoCommerce.Platform.Core.DeveloperTools;
using VirtoCommerce.Platform.Core.Modularity;
using VirtoCommerce.Platform.Core.Settings;
using VirtoCommerce.StoreModule.Core.Model;
using VirtoCommerce.Xapi.Core;
using VirtoCommerce.Xapi.Core.Extensions;
-using VirtoCommerce.Xapi.Core.Infrastructure;
using VirtoCommerce.Xapi.Core.Infrastructure.Validation;
using VirtoCommerce.Xapi.Core.Models;
using VirtoCommerce.Xapi.Core.Subscriptions;
@@ -51,7 +52,7 @@ private bool IsComplexityValidationEnabled
public void Initialize(IServiceCollection serviceCollection)
{
- serviceCollection.AddApplicationInsightsTelemetryProcessor();
+ serviceCollection.AddOpenTelemetry().WithTracing(t => t.AddProcessor(new IgnorePlainCoreXapiGraphQLTelemetryProcessor()));
#pragma warning disable CS0618 // Type or member is obsolete
// Use legacy type naming for backward compatibility
diff --git a/src/VirtoCommerce.Xapi.Web/module.manifest b/src/VirtoCommerce.Xapi.Web/module.manifest
index c53c57c..8a461c7 100644
--- a/src/VirtoCommerce.Xapi.Web/module.manifest
+++ b/src/VirtoCommerce.Xapi.Web/module.manifest
@@ -4,9 +4,9 @@
3.1012.0
- 3.1027.0
+ 3.1039.0-alpha.13307-vcst-4717
-
+