If you are a regular reader of my blog, you've certainly seen my earlier post on how to integrate Application Insights telemetry in your HotChocolate based GraphQL backend.
IMPORTANT: Although the code below will still work, I would recommend to switch to the integrated OpenTelemetry functionality and send the information to Application Insights in this way.
Today I had to upgrade an application to HotChocolate 13 and (again) I noticed that the application no longer compiled. So for people who are using the ApplicationInsightsDiagnosticsListener
I shared, here is an updated version that works for HotChocolate 13:
public class ApplicationInsightsDiagnosticListener : ExecutionDiagnosticEventListener | |
{ | |
private readonly TelemetryClient _telemetryClient; | |
public ApplicationInsightsDiagnosticListener(TelemetryClient telemetryClient) | |
{ | |
_telemetryClient = telemetryClient; | |
} | |
public override IDisposable ExecuteRequest(IRequestContext context) | |
{ | |
return new RequestScope(_telemetryClient, context); | |
} | |
private class RequestScope : IDisposable | |
{ | |
private TelemetryClient _telemetryClient; | |
private IRequestContext _context; | |
private IOperationHolder<RequestTelemetry> _operation; | |
private bool _disposed=false; | |
public RequestScope(TelemetryClient telemetryClient, IRequestContext context) | |
{ | |
_telemetryClient = telemetryClient; | |
_context = context; | |
var httpContext = GetHttpContextFrom(context); | |
if (httpContext == null) return; | |
// Create a new request | |
var requestTelemetry = new RequestTelemetry() | |
{ | |
Name = $"{httpContext.Request.Method} {httpContext.Request.GetUri().GetLeftPart(UriPartial.Path)}", | |
Url = new Uri(httpContext.Request.GetUri().AbsoluteUri) | |
}; | |
requestTelemetry.Context.Operation.Id = GetOperationIdFrom(httpContext); | |
requestTelemetry.Context.Operation.ParentId = GetOperationIdFrom(httpContext); | |
requestTelemetry.Properties.Add("GraphQL Query", context.Request.Query.ToString()); | |
requestTelemetry.Properties.Add("GraphQL Query Parameters", context.Request.VariableValues.GetQueryParameters()); | |
// Start the operation, and store it | |
_operation = _telemetryClient.StartOperation(requestTelemetry); | |
} | |
public void Dispose() | |
{ | |
if (_disposed) | |
return; | |
var httpContext = GetHttpContextFrom(_context); | |
if (httpContext == null) return; | |
if (_context.ContextData.ContainsKey(WellKnownContextData.OperationComplexity)) | |
_operation.Telemetry.Properties.Add("GraphQL Query Complexity", _context.ContextData[WellKnownContextData.OperationComplexity].ToString()); | |
// handle any final request logging here (GraphQL query errors, success, etc) | |
if (_context.Result is IQueryResult queryResult && queryResult.Errors is not null) | |
{ | |
HandleErrors(queryResult.Errors); | |
_operation.Telemetry.Success = false; | |
_operation.Telemetry.ResponseCode = "500"; | |
} | |
else | |
{ | |
_operation.Telemetry.Success = true; | |
_operation.Telemetry.ResponseCode = "200"; | |
} | |
// Then stop the operation. This completes the request. | |
_telemetryClient.StopOperation(_operation); | |
_disposed = true; | |
} | |
private HttpContext GetHttpContextFrom(IRequestContext context) | |
{ | |
if (!context.ContextData.ContainsKey("HttpContext")) | |
return null; | |
return context.ContextData["HttpContext"] as HttpContext; | |
} | |
private string GetOperationIdFrom(HttpContext context) | |
{ | |
return context.TraceIdentifier; | |
} | |
private void HandleErrors(IReadOnlyCollection<IError> errors) | |
{ | |
foreach (var error in errors) | |
{ | |
if (error.Exception == null) | |
{ | |
var exceptionTelemetry = new ExceptionTelemetry(); | |
exceptionTelemetry.Message = error.Message; | |
_telemetryClient.TrackException(exceptionTelemetry); | |
} | |
else | |
{ | |
_telemetryClient.TrackException(error.Exception); | |
} | |
} | |
} | |
} | |
} |
As I wanted to track the full operation I have overwritten the ExecuteRequest
method, however if you only want to log any exception, I would recommend to override the RequestError
and ResolverError
methods:
public class ApplicationInsightsDiagnosticListener : ExecutionDiagnosticEventListener | |
{ | |
private readonly TelemetryClient _telemetryClient; | |
public ApplicationInsightsDiagnosticListener(TelemetryClient telemetryClient) | |
{ | |
_telemetryClient = telemetryClient; | |
} | |
public override void RequestError(IRequestContext context, Exception exception) | |
{ | |
_telemetryClient.TrackException(error.Exception); | |
} | |
public override void ResolverError(IMiddlewareContext context, IError error) | |
{ | |
if (error.Exception == null) | |
{ | |
var exceptionTelemetry = new ExceptionTelemetry(); | |
exceptionTelemetry.Message = error.Message; | |
_telemetryClient.TrackException(exceptionTelemetry); | |
} | |
else | |
{ | |
_telemetryClient.TrackException(error.Exception); | |
} | |
} | |
} |
More information
HotChocolate OpenTelemetry (bartwullems.blogspot.com)
GraphQL HotChocolate 11 - Updated Application Insights monitoring (bartwullems.blogspot.com)