Skip to content

Commit

Permalink
Initial implementation for feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
jamescrosswell committed Sep 14, 2023
1 parent 0646b3e commit ad6968c
Show file tree
Hide file tree
Showing 13 changed files with 145 additions and 6 deletions.
2 changes: 2 additions & 0 deletions Sentry.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=Enricher/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=enrichers/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=instrumenter/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
38 changes: 38 additions & 0 deletions samples/Sentry.Samples.OpenTelemetry.AspNetCore/FakeAuthHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;

namespace Sentry.Samples.OpenTelemetry.AspNetCore;

public class FakeAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public const string UserId = "UserId";

public const string AuthenticationScheme = "Test";

public FakeAuthHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock) : base(options, logger, encoder, clock)
{
}

protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var claims = new List<Claim>
{
new(ClaimTypes.Name, "Fake user"),
new(ClaimTypes.NameIdentifier, "fake-user")
};

var identity = new ClaimsIdentity(claims, AuthenticationScheme);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, AuthenticationScheme);

var result = AuthenticateResult.Success(ticket);

return Task.FromResult(result);
}
}
15 changes: 15 additions & 0 deletions samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Authentication;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using Sentry.OpenTelemetry;
Expand All @@ -22,11 +23,18 @@
{
//options.Dsn = "...Your DSN...";
options.Debug = builder.Environment.IsDevelopment();
options.SendDefaultPii = true;
options.TracesSampleRate = 1.0;
options.UseOpenTelemetry(); // <-- Configure Sentry to use OpenTelemetry trace information
});

builder.Services
.AddAuthorization()
.AddAuthentication(FakeAuthHandler.AuthenticationScheme)
.AddScheme<AuthenticationSchemeOptions, FakeAuthHandler>(FakeAuthHandler.AuthenticationScheme, _ => { });

var app = builder.Build();
app.UseAuthorization();

var httpClient = new HttpClient();
app.MapGet("/hello", async context =>
Expand All @@ -48,6 +56,13 @@

app.MapGet("/echo/{name}", (string name) => $"Hi {name}!");

app.MapGet("/private", async context =>
{
var user = context.User;
var result = $"Hello {user.Identity?.Name}";
await context.Response.WriteAsync(result);
}).RequireAuthorization();

app.MapGet("/throw", _ => throw new Exception("test"));

app.Run();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"profiles": {
"http": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "hello",
"applicationUrl": "http://localhost:5092",
"applicationUrl": "https://localhost:5092",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
Expand Down
15 changes: 14 additions & 1 deletion src/Sentry.AspNetCore/DefaultUserFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,21 @@

namespace Sentry.AspNetCore;

internal class DefaultUserFactory : IUserFactory
internal class DefaultUserFactory : IUserFactory, ISentryUserFactory
{
private readonly IHttpContextAccessor? _httpContextAccessor;

public DefaultUserFactory()
{
}

public DefaultUserFactory(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}

public User? Create() => _httpContextAccessor?.HttpContext is {} httpContext ? Create(httpContext) : null;

public User? Create(HttpContext context)
{
var principal = context.User;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection.Extensions;
using Sentry;
using Sentry.AspNetCore;
using Sentry.Extensibility;
using Sentry.Extensions.Logging.Extensions.DependencyInjection;
Expand All @@ -20,7 +21,10 @@ public static ISentryBuilder AddSentry(this IServiceCollection services)
{
services.AddSingleton<ISentryEventProcessor, AspNetCoreEventProcessor>();
services.AddSingleton<ISentryEventExceptionProcessor, AspNetCoreExceptionProcessor>();

services.AddHttpContextAccessor();
services.TryAddSingleton<IUserFactory, DefaultUserFactory>();
services.TryAddSingleton<ISentryUserFactory, DefaultUserFactory>();

services
.AddSingleton<IRequestPayloadExtractor, FormRequestPayloadExtractor>()
Expand Down
6 changes: 6 additions & 0 deletions src/Sentry.AspNetCore/IUserFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
namespace Sentry.AspNetCore;

/// <summary>
/// <para>
/// Sentry User Factory
/// </para>
/// <para>
/// Note: This interface is tightly coupled to AspNetCore and Will be removed in version 4.0.0. Please consider using
/// <see cref="ISentryUserFactory"/> with <see cref="IHttpContextAccessor"/> instead.
/// </para>
/// </summary>
public interface IUserFactory
{
Expand Down
26 changes: 26 additions & 0 deletions src/Sentry.OpenTelemetry/AspNetCoreEnricher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace Sentry.OpenTelemetry;

internal class AspNetCoreEnricher : IOpenTelemetryEnricher
{
private readonly ISentryUserFactory _userFactory;

internal AspNetCoreEnricher(ISentryUserFactory userFactory)
{
_userFactory = userFactory;
}

public void Enrich(ISpan span, Activity activity, IHub hub, SentryOptions? options)
{
var sendPii = options?.SendDefaultPii ?? false;
if (sendPii)
{
hub.ConfigureScope(scope =>
{
if (!scope.HasUser() && _userFactory.Create() is {} user)
{
scope.User = user;
}
});
}
}
}
6 changes: 6 additions & 0 deletions src/Sentry.OpenTelemetry/IOpenTelemetryEnricher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Sentry.OpenTelemetry;

internal interface IOpenTelemetryEnricher
{
void Enrich(ISpan span, Activity activity, IHub hub, SentryOptions? options);
}
13 changes: 12 additions & 1 deletion src/Sentry.OpenTelemetry/SentrySpanProcessor.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using OpenTelemetry;
using OpenTelemetry.Trace;
using Sentry.Extensibility;
using Sentry.Internal;
using Sentry.Internal.Extensions;

namespace Sentry.OpenTelemetry;
Expand All @@ -11,6 +12,7 @@ namespace Sentry.OpenTelemetry;
public class SentrySpanProcessor : BaseProcessor<Activity>
{
private readonly IHub _hub;
private readonly IEnumerable<IOpenTelemetryEnricher> _enrichers;

// ReSharper disable once MemberCanBePrivate.Global - Used by tests
internal readonly ConcurrentDictionary<ActivitySpanId, ISpan> _map = new();
Expand All @@ -27,9 +29,14 @@ public SentrySpanProcessor() : this(SentrySdk.CurrentHub)
/// <summary>
/// Constructs a <see cref="SentrySpanProcessor"/>.
/// </summary>
public SentrySpanProcessor(IHub hub)
public SentrySpanProcessor(IHub hub) : this(hub, null)
{
}

internal SentrySpanProcessor(IHub hub, List<IOpenTelemetryEnricher>? enrichers)
{
_hub = hub;
_enrichers = enrichers ?? Enumerable.Empty<IOpenTelemetryEnricher>();
_options = hub.GetSentryOptions();

if (_options is not { })
Expand Down Expand Up @@ -165,6 +172,10 @@ public override void OnEnd(Activity data)
GenerateSentryErrorsFromOtelSpan(data, attributes);

var status = GetSpanStatus(data.Status, attributes);
foreach (var enricher in _enrichers)
{
enricher.Enrich(span, data, _hub, _options);
}
span.Finish(status);

_map.TryRemove(data.SpanId, out _);
Expand Down
15 changes: 14 additions & 1 deletion src/Sentry.OpenTelemetry/TracerProviderBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Microsoft.Extensions.DependencyInjection;
using OpenTelemetry;
using OpenTelemetry.Context.Propagation;
using OpenTelemetry.Trace;
Expand Down Expand Up @@ -29,6 +30,18 @@ public static TracerProviderBuilder AddSentry(this TracerProviderBuilder tracerP
{
defaultTextMapPropagator ??= new SentryPropagator();
Sdk.SetDefaultTextMapPropagator(defaultTextMapPropagator);
return tracerProviderBuilder.AddProcessor<SentrySpanProcessor>();
return tracerProviderBuilder.AddProcessor(services =>
{
List<IOpenTelemetryEnricher> enrichers = new();
// AspNetCoreEnricher
var userFactory = services.GetService<ISentryUserFactory>();
if (userFactory != null)
{
enrichers.Add(new AspNetCoreEnricher(userFactory));
}
return new SentrySpanProcessor(SentrySdk.CurrentHub, enrichers);
});
}
}
6 changes: 6 additions & 0 deletions src/Sentry/ISentryUserFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Sentry;

internal interface ISentryUserFactory
{
public User? Create();
}
1 change: 0 additions & 1 deletion src/Sentry/Sentry.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@
<InternalsVisibleTo Include="Sentry.NLog" PublicKey="$(SentryPublicKey)" />
<InternalsVisibleTo Include="Sentry.NLog.Tests" PublicKey="$(SentryPublicKey)" />
<InternalsVisibleTo Include="Sentry.OpenTelemetry" PublicKey="$(SentryPublicKey)" />
<InternalsVisibleTo Include="Sentry.OpenTelemetry.AspNetCore" PublicKey="$(SentryPublicKey)" />
<InternalsVisibleTo Include="Sentry.OpenTelemetry.Tests" PublicKey="$(SentryPublicKey)" />
<InternalsVisibleTo Include="Sentry.Profiling" PublicKey="$(SentryPublicKey)" />
<InternalsVisibleTo Include="Sentry.Profiling.Tests" PublicKey="$(SentryPublicKey)" />
Expand Down

0 comments on commit ad6968c

Please sign in to comment.