Breaking

Tuesday, November 30, 2021

GraphQL HotChocolate 12 - Updated Application Insights monitoring

It seems that with every release of HotChocolate, I can write a follow up post. With the release of HotChocolate 11, I wrote a blog post on how to integrate it with Application Insights.

With the HotChocolate 12, there was again a small update in the available interfaces and API’s.

Let’s check the changes we have to make…

  • First of all, our diagnostic class should no longer inherit from DiagnosticEventListener but from ExecutionDiagnosticEventListener.
public class ApplicationInsightsDiagnosticListener : ExecutionDiagnosticEventListener
{
private readonly TelemetryClient _telemetryClient;
public ApplicationInsightsDiagnosticListener(TelemetryClient telemetryClient)
{
_telemetryClient = telemetryClient;
}
}
  • The signature of the ExecuteRequest method has changed as well. Instead of returning an IActivityScope it should return an IDisposable:
public override IDisposable ExecuteRequest(IRequestContext context)
{
return new RequestScope(_telemetryClient, context);
}
  • This also means that our RequestScope no longer needs to implement the IActivityScope interface but only needs to implement IDisposable:
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());
// Start the operation, and store it
_operation = _telemetryClient.StartOperation(requestTelemetry);
}
public void Dispose()
{
if (_disposed)
return;
var httpContext = GetHttpContextFrom(_context);
if (httpContext == null) return;
// handle any final request logging here (GraphQL query errors, success, etc)
if (_context.Result.Errors is null)
{
_operation.Telemetry.Success = true;
_operation.Telemetry.ResponseCode = "200";
}
else
{
HandleErrors(_context.Result.Errors);
_operation.Telemetry.Success = false;
_operation.Telemetry.ResponseCode = "500";
}
// Then stop the operation. This completes the request.
_telemetryClient.StopOperation(_operation);
_disposed = true;
}
}
view raw RequestScope.cs hosted with ❤ by GitHub

Here is the full example:

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());
// Start the operation, and store it
_operation = _telemetryClient.StartOperation(requestTelemetry);
}
public void Dispose()
{
if (_disposed)
return;
var httpContext = GetHttpContextFrom(_context);
if (httpContext == null) return;
// handle any final request logging here (GraphQL query errors, success, etc)
if (_context.Result.Errors is null)
{
_operation.Telemetry.Success = true;
_operation.Telemetry.ResponseCode = "200";
}
else
{
HandleErrors(_context.Result.Errors);
_operation.Telemetry.Success = false;
_operation.Telemetry.ResponseCode = "500";
}
// 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);
}
}
}
}
}
}

No comments:

Post a Comment