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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
Here is the full example:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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