Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add diagnostic source integration. #1154

Merged
merged 78 commits into from
Aug 25, 2021
Merged
Show file tree
Hide file tree
Changes from 72 commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
815e6cf
wip
lucas-zimerman Jul 8, 2021
4fdfbfa
WIP
lucas-zimerman Jul 8, 2021
bbde0c7
fix merge
lucas-zimerman Jul 19, 2021
37ab73a
wip
lucas-zimerman Jul 19, 2021
a423991
ef
lucas-zimerman Jul 19, 2021
76e877c
wip
lucas-zimerman Jul 19, 2021
59f1600
Solution cleanup
lucas-zimerman Jul 19, 2021
34d8bfc
cleanup
lucas-zimerman Jul 21, 2021
2db9dc5
class internal
lucas-zimerman Jul 21, 2021
e794140
wip, why span parents are wrong in test?
lucas-zimerman Jul 22, 2021
c5c046e
wip
lucas-zimerman Jul 22, 2021
ea16749
error test
lucas-zimerman Jul 23, 2021
fd7ca8f
wip
lucas-zimerman Jul 23, 2021
dfc91a6
Merge branch 'main' into feat/efcore-performance
lucas-zimerman Jul 23, 2021
73bc50f
small refactor
lucas-zimerman Jul 23, 2021
fb6da9c
WIP
lucas-zimerman Jul 26, 2021
807d1fa
Merge branch 'main' into feat/efcore-performance
lucas-zimerman Jul 26, 2021
a973736
fix sln
lucas-zimerman Jul 26, 2021
24c5365
update changelog.
lucas-zimerman Jul 26, 2021
e6dfc58
fix sln
lucas-zimerman Jul 26, 2021
762a18e
Small refactor.
lucas-zimerman Jul 26, 2021
d9b0e70
refactor name
lucas-zimerman Jul 26, 2021
2c7e03b
wip
lucas-zimerman Jul 27, 2021
130ac53
Merge branch 'main' into feat/efcore-performance
bruno-garcia Jul 27, 2021
2fcb156
wip
lucas-zimerman Jul 27, 2021
d8c7c6c
Merge branch 'feat/efcore-performance' of https://github.com/getsentr…
lucas-zimerman Jul 27, 2021
78496c5
small refactor.
lucas-zimerman Jul 27, 2021
9292187
Merge branch 'main' into feat/efcore-performance
lucas-zimerman Jul 27, 2021
e8f3a59
refactor.
lucas-zimerman Jul 27, 2021
257e817
Merge branch 'feat/efcore-performance' of https://github.com/getsentr…
lucas-zimerman Jul 27, 2021
52451c5
small small refactor.
lucas-zimerman Jul 27, 2021
e38b70d
rm ref
lucas-zimerman Jul 27, 2021
a6bba9a
refactors
lucas-zimerman Jul 28, 2021
066f12c
Edit test to allow multiple connections to the database.
lucas-zimerman Jul 28, 2021
1c2b2bb
test
lucas-zimerman Jul 28, 2021
01d9f0c
fix compiler conflict.
lucas-zimerman Jul 28, 2021
8c286b0
test
lucas-zimerman Jul 28, 2021
71f81de
test
lucas-zimerman Jul 28, 2021
950facf
requested changes.
lucas-zimerman Jul 29, 2021
5367914
line/text refactor.
lucas-zimerman Jul 29, 2021
854aa22
add tests for net461 and netcore 2.1
lucas-zimerman Jul 29, 2021
d54b12d
Update tests.
lucas-zimerman Jul 29, 2021
811febc
removed debug log.
lucas-zimerman Jul 29, 2021
20eb8ee
remove unused string
lucas-zimerman Jul 29, 2021
46cebd9
add newline to E logging, removed unused references on EFCore tests a…
lucas-zimerman Jul 29, 2021
04de4a0
span fix.
lucas-zimerman Jul 29, 2021
3085821
Add SQL listener
lucas-zimerman Aug 2, 2021
7eea529
Add tests.
lucas-zimerman Aug 2, 2021
faeac84
small cleanup
lucas-zimerman Aug 3, 2021
217bc9c
Merge branch 'main' into feat/sql-performance
lucas-zimerman Aug 4, 2021
a4047c0
removed junk.
lucas-zimerman Aug 4, 2021
0ebb6a6
update changelog.
lucas-zimerman Aug 4, 2021
e049f19
moved Diagnostic code to a new package.
lucas-zimerman Aug 6, 2021
905f0ce
Integrated Sentry with DiagnosticSource.
lucas-zimerman Aug 6, 2021
11cb53e
ext logging cleanup and unsubscripe listener.
lucas-zimerman Aug 6, 2021
1d28c17
migrated tests
lucas-zimerman Aug 6, 2021
29e64e7
Tests working.
lucas-zimerman Aug 6, 2021
ce8d91d
Refactors requested from PR 1149
lucas-zimerman Aug 6, 2021
9a66ab5
Fix tests.
lucas-zimerman Aug 6, 2021
252f103
more project fixes.
lucas-zimerman Aug 6, 2021
2959493
fixed tests.
lucas-zimerman Aug 6, 2021
aaeb0a2
Merge branch 'main' into feat/diagnostic-source
lucas-zimerman Aug 9, 2021
78113f8
Disable EF Connection and Query spans if integrated with SQL client.
lucas-zimerman Aug 9, 2021
ace6885
Merge branch 'feat/diagnostic-source' of https://github.com/getsentry…
lucas-zimerman Aug 9, 2021
f7c8ca7
fix gitignore and add tests for disabled ef core.
lucas-zimerman Aug 9, 2021
3aa910e
fix logic.
lucas-zimerman Aug 9, 2021
5025406
Add dispose.
lucas-zimerman Aug 9, 2021
6a2d844
changelog.
lucas-zimerman Aug 9, 2021
b25c58f
Merge branch 'main' into feat/diagnostic-source
bruno-garcia Aug 21, 2021
e5d93f4
Merge branch 'main' into feat/diagnostic-source
lucas-zimerman Aug 23, 2021
ac36bf0
Apply suggestions from code review
lucas-zimerman Aug 23, 2021
241a8ac
Merge branch 'main' into feat/diagnostic-source
bruno-garcia Aug 25, 2021
124c438
Merge branch 'main' into feat/diagnostic-source
lucas-zimerman Aug 25, 2021
f92a238
Format code
getsentry-bot Aug 25, 2021
3dbfd35
Apply suggestions from code review
lucas-zimerman Aug 25, 2021
bff0761
fix changelog.
lucas-zimerman Aug 25, 2021
171839e
Add option to disable the new integration.
lucas-zimerman Aug 25, 2021
e705666
fix check for defined constant.
lucas-zimerman Aug 25, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@

### Features

- EF Core and SQLClient performance monitoring integration ([#1154](https://github.com/getsentry/sentry-dotnet/pull/1154))
- Improved SDK diagnostic logs ([#1161](https://github.com/getsentry/sentry-dotnet/pull/1161))
- Add Scope observer to SentryOptions ([#1153](https://github.com/getsentry/sentry-dotnet/pull/1153))

### Fixes

lucas-zimerman marked this conversation as resolved.
Show resolved Hide resolved
- Fix end session from Hub adapter not being passed to SentrySDK ([#1158](https://github.com/getsentry/sentry-dotnet/pull/1158))
- Installation id catches dir not exist([#1159](https://github.com/getsentry/sentry-dotnet/pull/1159))
- Set error status to transaction if http has exception and ok status ([#1143](https://github.com/getsentry/sentry-dotnet/pull/1143))
- Fix max breadcrumbs limit when MaxBreadcrumbs is zero or lower ([#1145](https://github.com/getsentry/sentry-dotnet/pull/1145))

Expand Down
14 changes: 14 additions & 0 deletions Sentry.sln
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Tunnel", "src\Sentry
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Tunnel.Tests", "test\Sentry.Tunnel.Tests\Sentry.Tunnel.Tests.csproj", "{BB54EF08-2FA1-498B-827B-32D905FB0F9F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Diagnostics.DiagnosticSource", "src\Sentry.Diagnostics.DiagnosticSource\Sentry.Diagnostics.DiagnosticSource.csproj", "{0BE821AB-88D4-4EB5-B012-C964FF58087A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Diagnostics.DiagnosticSource.Tests", "test\Sentry.Diagnostics.DiagnosticSource.Tests\Sentry.Diagnostics.DiagnosticSource.Tests.csproj", "{D870B028-16ED-4551-8B0F-5529479D04C9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -301,6 +305,14 @@ Global
{BB54EF08-2FA1-498B-827B-32D905FB0F9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BB54EF08-2FA1-498B-827B-32D905FB0F9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BB54EF08-2FA1-498B-827B-32D905FB0F9F}.Release|Any CPU.Build.0 = Release|Any CPU
{0BE821AB-88D4-4EB5-B012-C964FF58087A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0BE821AB-88D4-4EB5-B012-C964FF58087A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0BE821AB-88D4-4EB5-B012-C964FF58087A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0BE821AB-88D4-4EB5-B012-C964FF58087A}.Release|Any CPU.Build.0 = Release|Any CPU
{D870B028-16ED-4551-8B0F-5529479D04C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D870B028-16ED-4551-8B0F-5529479D04C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D870B028-16ED-4551-8B0F-5529479D04C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D870B028-16ED-4551-8B0F-5529479D04C9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -349,6 +361,8 @@ Global
{066522A4-8380-4D29-8DD0-973B1EDF0B39} = {83263231-1A2A-4733-B759-EEFF14E8C5D5}
{D2F8BF0E-7749-4C92-A4F1-7B96A9878458} = {AF6AF4C7-8AA2-4D59-8064-2D79560904EB}
{BB54EF08-2FA1-498B-827B-32D905FB0F9F} = {83263231-1A2A-4733-B759-EEFF14E8C5D5}
{0BE821AB-88D4-4EB5-B012-C964FF58087A} = {AF6AF4C7-8AA2-4D59-8064-2D79560904EB}
{D870B028-16ED-4551-8B0F-5529479D04C9} = {83263231-1A2A-4733-B759-EEFF14E8C5D5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0C652B1A-DF72-4EE5-A98B-194FE2C054F6}
Expand Down
1 change: 1 addition & 0 deletions src/Sentry.AspNet/Sentry.AspNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

<ItemGroup>
<ProjectReference Include="..\Sentry\Sentry.csproj" />
<ProjectReference Include="..\Sentry.Diagnostics.DiagnosticSource\Sentry.Diagnostics.DiagnosticSource.csproj" />
</ItemGroup>

</Project>
2 changes: 2 additions & 0 deletions src/Sentry.AspNet/SentryAspNetOptionsExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Web;
using Sentry.AspNet.Internal;
using Sentry.Extensibility;
Expand Down Expand Up @@ -28,6 +29,7 @@ public static void AddAspNet(this SentryOptions options, RequestSize maxRequestB
options.DiagnosticLogger ??= new TraceDiagnosticLogger(options.DiagnosticLevel);
options.Release ??= SystemWebVersionLocator.Resolve(options, HttpContext.Current);
options.AddEventProcessor(eventProcessor);
options.AddDiagnosticListeners();
}
}
}
3 changes: 3 additions & 0 deletions src/Sentry.AspNetCore/Sentry.AspNetCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
<ProjectReference Include="..\Sentry.Extensions.Logging\Sentry.Extensions.Logging.csproj" />
</ItemGroup>


<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<ProjectReference Include="..\Sentry.Diagnostics.DiagnosticSource\Sentry.Diagnostics.DiagnosticSource.csproj" />

<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.Abstractions" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.0" />
Expand Down
4 changes: 4 additions & 0 deletions src/Sentry.AspNetCore/SentryAspNetCoreOptionsSetup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ public override void Configure(SentryAspNetCoreOptions options)
category,
"Microsoft.AspNetCore.Server.Kestrel",
StringComparison.Ordinal));

#if NETSTANDARD2_0
options.AddDiagnosticListeners();
#endif
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using System.Diagnostics;
using Sentry.Internal;

namespace Sentry.Internals.DiagnosticSource
{
internal class SentryDiagnosticListenerIntegration : IInternalSdkIntegration
{
private SentryDiagnosticSubscriber? _subscriber;
private IDisposable? _diagnosticListener { get; set; }

public void Register(IHub hub, SentryOptions options)
lucas-zimerman marked this conversation as resolved.
Show resolved Hide resolved
{
_subscriber = new SentryDiagnosticSubscriber(hub, options);
lucas-zimerman marked this conversation as resolved.
Show resolved Hide resolved
_diagnosticListener = DiagnosticListener.AllListeners.Subscribe(_subscriber);
}

public void Unregister(IHub hub)
{
_diagnosticListener?.Dispose();
_subscriber?.Dispose();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Sentry.Internals.DiagnosticSource
{
/// <summary>
/// Class that subscribes to specific listeners from DiagnosticListener.
/// </summary>
internal class SentryDiagnosticSubscriber : IObserver<DiagnosticListener>, IDisposable
{
private SentryEFCoreListener? _efInterceptor { get; set; }
private SentrySqlListener? _sqlListener { get; set; }
private List<IDisposable> _disposableListeners = new();
private IHub _hub { get; }
private SentryOptions _options { get; }

public SentryDiagnosticSubscriber(IHub hub, SentryOptions options)
{
_hub = hub;
_options = options;
}

public void OnCompleted() { }

public void OnError(Exception error) { }

public void OnNext(DiagnosticListener listener)
{
if (listener.Name == "Microsoft.EntityFrameworkCore")
{
_efInterceptor = new(_hub, _options);
_disposableListeners.Add(listener.Subscribe(_efInterceptor));
_options.DiagnosticLogger?.Log(SentryLevel.Debug, "Registered integration with EF Core.");
lucas-zimerman marked this conversation as resolved.
Show resolved Hide resolved
}
else if (listener.Name == "SqlClientDiagnosticListener")
{
_sqlListener = new(_hub, _options);
_disposableListeners.Add(listener.Subscribe(_sqlListener));
_options.DiagnosticLogger?.Log(SentryLevel.Debug, "Registered integration with SQL Client.");
lucas-zimerman marked this conversation as resolved.
Show resolved Hide resolved

// Duplicated data.
_efInterceptor?.DisableConnectionSpan();
_efInterceptor?.DisableQuerySpan();
}
}

/// <summary>
/// Dispose all registered integrations.
/// </summary>
public void Dispose()
{
foreach (var item in _disposableListeners)
{
item.Dispose();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using System.Threading;
using Sentry.Extensibility;

namespace Sentry.Internals.DiagnosticSource
{
/// <summary>
/// Class that consumes Entity Framework Core events.
/// </summary>
internal class SentryEFCoreListener : IObserver<KeyValuePair<string, object?>>
{
private enum SentryEFSpanType
{
Connection,
QueryExecution,
QueryCompiler
}

internal const string EFConnectionOpening = "Microsoft.EntityFrameworkCore.Database.Connection.ConnectionOpening";
internal const string EFConnectionClosed = "Microsoft.EntityFrameworkCore.Database.Connection.ConnectionClosed";
internal const string EFCommandExecuting = "Microsoft.EntityFrameworkCore.Database.Command.CommandExecuting";
internal const string EFCommandExecuted = "Microsoft.EntityFrameworkCore.Database.Command.CommandExecuted";
internal const string EFCommandFailed = "Microsoft.EntityFrameworkCore.Database.Command.CommandError";

/// <summary>
/// Used for EF Core 2.X and 3.X.
/// <seealso href="https://docs.microsoft.com/dotnet/api/microsoft.entityframeworkcore.diagnostics.coreeventid.querymodelcompiling?view=efcore-3.1"></seealso>
/// </summary>
internal const string EFQueryStartCompiling = "Microsoft.EntityFrameworkCore.Query.QueryCompilationStarting";
/// <summary>
/// Used for EF Core 2.X and 3.X.
/// <seealso href="https://docs.microsoft.com/dotnet/api/microsoft.entityframeworkcore.diagnostics.coreeventid.querymodelcompiling?view=efcore-3.1"></seealso>
/// </summary>
internal const string EFQueryCompiling = "Microsoft.EntityFrameworkCore.Query.QueryModelCompiling";
internal const string EFQueryCompiled = "Microsoft.EntityFrameworkCore.Query.QueryExecutionPlanned";

private IHub _hub { get; }
private SentryOptions _options { get; }

private AsyncLocal<WeakReference<ISpan>> _spansCompilerLocal = new();
private AsyncLocal<WeakReference<ISpan>> _spansQueryLocal = new();
private AsyncLocal<WeakReference<ISpan>> _spansConnectionLocal = new();

private bool _logConnectionEnabled = true;
private bool _logQueryEnabled = true;

public SentryEFCoreListener(IHub hub, SentryOptions options)
{
_hub = hub;
_options = options;
}

internal void DisableConnectionSpan() => _logConnectionEnabled = false;

internal bool DisableQuerySpan() => _logQueryEnabled = false;

private ISpan? AddSpan(SentryEFSpanType type, string operation, string? description)
{
if (_hub.GetSpan()?.StartChild(operation, description) is { } span &&
GetSpanBucket(type) is { } asyncLocalSpan)
{
asyncLocalSpan.Value = new WeakReference<ISpan>(span);
return span;
}
return null;
}

private ISpan? TakeSpan(SentryEFSpanType type)
{
if (GetSpanBucket(type)?.Value is { } reference && reference.TryGetTarget(out var span))
{
return span;
}
_options.DiagnosticLogger?.LogWarning("Trying to close a span that was already garbage collected. {0}", type);
return null;
}

private AsyncLocal<WeakReference<ISpan>>? GetSpanBucket(SentryEFSpanType type)
=> type switch
{
SentryEFSpanType.QueryCompiler => _spansCompilerLocal,
SentryEFSpanType.QueryExecution => _spansQueryLocal,
SentryEFSpanType.Connection => _spansConnectionLocal,
_ => null
};

public void OnCompleted() { }

public void OnError(Exception error) { }

public void OnNext(KeyValuePair<string, object?> value)
{
try
{
//Query compiler Span
if (value.Key == EFQueryStartCompiling || value.Key == EFQueryCompiling)
{
AddSpan(SentryEFSpanType.QueryCompiler, "db.query_compiler", FilterNewLineValue(value.Value));
}
else if (value.Key == EFQueryCompiled)
{
TakeSpan(SentryEFSpanType.QueryCompiler)?.Finish(SpanStatus.Ok);
}

//Connection Span
//A transaction may or may not show a connection with it.
if (_logConnectionEnabled && value.Key == EFConnectionOpening)
{
AddSpan(SentryEFSpanType.Connection, "db.connection", null);
}
else if (_logConnectionEnabled && value.Key == EFConnectionClosed)
{
TakeSpan(SentryEFSpanType.Connection)?.Finish(SpanStatus.Ok);
}

//Query Execution Span
else if (_logQueryEnabled)
{
if (value.Key == EFCommandExecuting)
{
AddSpan(SentryEFSpanType.QueryExecution, "db.query", FilterNewLineValue(value.Value));
}
else if (value.Key == EFCommandFailed)
{
TakeSpan(SentryEFSpanType.QueryExecution)?.Finish(SpanStatus.InternalError);
}
else if (value.Key == EFCommandExecuted)
{
TakeSpan(SentryEFSpanType.QueryExecution)?.Finish(SpanStatus.Ok);
}
}
}
catch (Exception ex)
{
_options.DiagnosticLogger?.LogError("Failed to intercept EF Core event.", ex);
}
}

/// <summary>
/// Get the Query with error message and remove the uneeded values.
/// </summary>
/// <example>
/// Compiling query model:
/// EF intialize...\r\nEF Query...
/// becomes:
/// EF Query...
/// </example>
/// <param name="value">the query to be parsed value</param>
/// <returns>the filtered query</returns>
internal static string? FilterNewLineValue(object? value)
{
var str = value?.ToString();
return str?.Substring(str.IndexOf('\n') + 1);
}
}
}
Loading