Skip to content

Commit

Permalink
chore(graphql): Removed Application Insights SDK, added OpenTelemetry (
Browse files Browse the repository at this point in the history
…#1349)

<!--- Provide a general summary of your changes in the Title above -->

## Description
Removed Application Insights SDK, added OpenTelemetry

<!--- Describe your changes in detail -->

## Related Issue(s)

- #1101 

## Verification

- [x] **Your** code builds clean without any errors or warnings
- [x] Manual testing done (required)
- [ ] Relevant automated test added (if you find this hard, leave it and
we'll help out)

## Documentation

- [ ] Documentation is updated (either in `docs`-directory, Altinnpedia
or a separate linked PR in
[altinn-studio-docs.](https://github.com/Altinn/altinn-studio-docs), if
applicable)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Introduced `OpenTelemetryEventListener` for enhanced tracing and
monitoring of GraphQL requests.
- Updated telemetry configuration to streamline logging and ensure
consistent telemetry setup.

- **Bug Fixes**
- Removed the deprecated `ApplicationInsightEventListener`, improving
the overall telemetry approach.

- **Refactor**
- Adjusted service collection to replace the event listener for GraphQL
with the new OpenTelemetry implementation.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Ole Jørgen Skogstad <skogstad@softis.net>
  • Loading branch information
knuhau and oskogstad authored Nov 5, 2024
1 parent 5a96bc0 commit 16f3b38
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 120 deletions.

This file was deleted.

112 changes: 112 additions & 0 deletions src/Digdir.Domain.Dialogporten.GraphQL/OpenTelemetryEventListener.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using System.Diagnostics;
using HotChocolate.Execution;
using HotChocolate.Execution.Instrumentation;
using Microsoft.AspNetCore.Http.Extensions;
using OpenTelemetry.Trace;

namespace Digdir.Domain.Dialogporten.GraphQL;

public sealed class OpenTelemetryEventListener : ExecutionDiagnosticEventListener
{
private static readonly ActivitySource ActivitySource = new("Dialogporten.GraphQL");

public override IDisposable ExecuteRequest(IRequestContext context)
{
var httpContext = GetHttpContextFrom(context);
if (httpContext == null)
return EmptyScope;

#if DEBUG
if (context.Request.OperationName == "IntrospectionQuery")
return EmptyScope;
#endif

var operationName = context.Request.OperationName ?? "UnknownOperation";
var operationPath = $"{operationName} - {context.Request.QueryHash}";

var activity = ActivitySource.StartActivity($"GraphQL {operationPath}", ActivityKind.Server);

if (activity == null)
return EmptyScope;

activity.SetTag("graphql.operation_name", operationName);
activity.SetTag("graphql.query_hash", context.Request.QueryHash);
activity.SetTag("http.url", httpContext.Request.GetDisplayUrl());
activity.SetTag("user.id", httpContext.User.Identity?.Name ?? "Not authenticated");
activity.SetTag("http.method", httpContext.Request.Method);
activity.SetTag("http.route", httpContext.Request.Path);

return new ScopeWithEndAction(() => OnEndRequest(context, activity));
}

public override void RequestError(IRequestContext context, Exception exception)
{
var currentActivity = Activity.Current;
if (currentActivity != null)
{
currentActivity.RecordException(exception);
currentActivity.SetStatus(ActivityStatusCode.Error, exception.Message);
}
base.RequestError(context, exception);
}

public override void ValidationErrors(IRequestContext context, IReadOnlyList<IError> errors)
{
foreach (var error in errors)
{
var currentActivity = Activity.Current;
currentActivity?.AddEvent(new ActivityEvent("ValidationError", default, new ActivityTagsCollection
{
{ "message", error.Message }
}));
}

base.ValidationErrors(context, errors);
}

private static HttpContext? GetHttpContextFrom(IRequestContext context) =>
context.ContextData.TryGetValue("HttpContext", out var value) ? value as HttpContext : null;

private static void OnEndRequest(IRequestContext context, Activity activity)
{
var httpContext = GetHttpContextFrom(context);
if (context.Exception != null)
{
activity.RecordException(context.Exception);
activity.SetStatus(ActivityStatusCode.Error, context.Exception.Message);
}

if (context.Result is QueryResult { Errors: not null } queryResult)
{
foreach (var error in queryResult.Errors)
{
if (error.Exception is null)
{
continue;
}

activity.RecordException(error.Exception);
activity.SetStatus(ActivityStatusCode.Error, error.Exception.Message);
}
}

if (httpContext != null)
{
activity.SetTag("http.status_code", httpContext.Response.StatusCode);
}

activity.Dispose();
}
}

internal sealed class ScopeWithEndAction : IDisposable
{
private readonly Action _disposeAction;

public ScopeWithEndAction(Action disposeAction)
{
_disposeAction = disposeAction;
}

public void Dispose() => _disposeAction.Invoke();
}
18 changes: 13 additions & 5 deletions src/Digdir.Domain.Dialogporten.GraphQL/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,20 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;

const string DialogportenGraphQLSource = "Dialogporten.GraphQL";

var telemetryConfiguration = TelemetryConfiguration.CreateDefault();
// Using two-stage initialization to catch startup errors.
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Warning()
.Enrich.FromLogContext()
.WriteTo.Console(formatProvider: CultureInfo.InvariantCulture)
.WriteTo.ApplicationInsights(TelemetryConfiguration.CreateDefault(), TelemetryConverter.Traces)
.WriteTo.ApplicationInsights(telemetryConfiguration, TelemetryConverter.Traces)
.CreateBootstrapLogger();

try
{
BuildAndRun(args);
BuildAndRun(args, telemetryConfiguration);
}
catch (Exception ex) when (ex is not OperationCanceledException)
{
Expand All @@ -39,7 +42,7 @@
Log.CloseAndFlush();
}

static void BuildAndRun(string[] args)
static void BuildAndRun(string[] args, TelemetryConfiguration telemetryConfiguration)
{
var builder = WebApplication.CreateBuilder(args);

Expand All @@ -49,7 +52,7 @@ static void BuildAndRun(string[] args)
.ReadFrom.Services(services)
.Enrich.FromLogContext()
.WriteTo.Console(formatProvider: CultureInfo.InvariantCulture)
.WriteTo.ApplicationInsights(services.GetRequiredService<TelemetryConfiguration>(), TelemetryConverter.Traces));
.WriteTo.ApplicationInsights(telemetryConfiguration, TelemetryConverter.Traces));

builder.Configuration
.AddAzureConfiguration(builder.Environment.EnvironmentName)
Expand All @@ -64,6 +67,11 @@ static void BuildAndRun(string[] args)
var thisAssembly = Assembly.GetExecutingAssembly();

builder.ConfigureTelemetry();
builder.Services.AddOpenTelemetry()
.WithTracing(tracerProviderBuilder =>
{
tracerProviderBuilder.AddSource(DialogportenGraphQLSource);
});

builder.Services
// Options setup
Expand All @@ -75,7 +83,7 @@ static void BuildAndRun(string[] args)
.WithPubCapabilities()
.Build()
.AddAutoMapper(Assembly.GetExecutingAssembly())
.AddApplicationInsightsTelemetry()
.AddHttpContextAccessor()
.AddScoped<IUser, ApplicationUser>()
.AddValidatorsFromAssembly(thisAssembly, ServiceLifetime.Transient, includeInternalTypes: true)
.AddAzureAppConfiguration()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public static IServiceCollection AddDialogportenGraphQl(this IServiceCollection
.AddSubscriptionType<Subscriptions>()
.AddAuthorization()
.RegisterDbContext<DialogDbContext>()
.AddDiagnosticEventListener<ApplicationInsightEventListener>()
.AddDiagnosticEventListener<OpenTelemetryEventListener>()
.AddQueryType<Queries>()
.AddMutationType<Mutations>()
.AddType<DialogByIdDeleted>()
Expand Down

0 comments on commit 16f3b38

Please sign in to comment.